--- type: plan-task title: "Task 09 — Nachbesserung nach der Prüfung: Fälle für die Reset-Toleranz und kleine Lücken" status: draft summary: "Delta nach Welle 1 und 2 aus der Prüfung wf_e0294c82-76c: Regressionsfälle und Mutanten für die Reset-Toleranz, doppelte Linsen abweisen, Status ohne_pruefung, Dateiname mit Pfadgrenze, Kindphase mit Nummer, Fälle für den Nachlesetakt, den Lesehinweis, fehlendes verbrauch.js und das verankerte 5-Stunden-Label." tags: [plan-task, vault-tools, codeworkflow] created: 2026-09-24 updated: 2026-09-24 modell: sonnet effort: low modell_begruendung: "Abschrift: alle Aus-wird-Blöcke stehen wörtlich im Plan, Zieldateien und Verbote sind genannt. Sonnet low statt Haiku, weil Blöcke die Zeichen · und ▸ tragen und Haiku in der Messung vom 2026-09-23 typografische Zeichen still ersetzt hat." --- # Task 09 — Nachbesserung nach der Prüfung Die globalen Vorgaben stehen in `00-uebersicht.md` (Abschnitt „Globale Vorgaben“). Arbeitsverzeichnis ist der Vault-Wurzelordner `D:/Claude`; alle Pfade gelten von dort. Die Tasks 02 bis 07 sind ausgeführt und bleiben byte-gleich; diese Nachbesserung ändert die Dateien nur an den Stellen unten. Jeder Aus-Block steht genau einmal in seiner Datei. Was danach anders ist: - **Die Reset-Toleranz hat Fälle für ihren Wert:** Verschiebung um 4:59 (dasselbe Fenster), um 5:01 nach vorn und nach hinten (Reset), um fünf Stunden bei steigendem Wert (Reset). Die Mutanten 6 Stunden, 1 Sekunde und „nur nach vorn“ werden gefangen. - **`plankritik.js`** weist doppelte Linsen ab. **`welle-mit-pruefung.js`** meldet `status: ohne_pruefung`, wenn die Prüfung übersprungen wurde oder scheiterte. **`planung.js`** vergleicht den Dateinamen des Planer-Ergebnisses mit Pfadgrenze. - **Die Attrappe** nummeriert wiederholte Kinder wie das Ziel (`▸ name #2`). - **Neue Fälle** für den Nachlesetakt, den Lesehinweis im Planerauftrag, ein fehlendes `verbrauch.js` in `welle-mit-pruefung` und das verankerte 5-Stunden-Label, dazu die Mutanten dafür. ## Zieldateien - `.claude/workflows/plankritik.js` (geändert) - `.claude/workflows/planung.js` (geändert) - `.claude/workflows/welle-mit-pruefung.js` (geändert) - `.vault-tools/workflow-pruefung/attrappe.mjs` (geändert) - `.vault-tools/workflow-pruefung/faelle_workflows.mjs` (geändert) - `.vault-tools/workflow-pruefung/mutanten_workflows.py` (geändert) - `.vault-tools/workflow-pruefung/faelle_verbrauch_reihe.py` (geändert) - `.vault-tools/workflow-pruefung/mutanten_reihe.py` (geändert) ## Verbote - Keine andere Datei. - Kein Commit, kein Push, kein Lauf eines echten Workflows. - Kein Umformulieren; ein Widerspruch zwischen Plan und Baum wird gemeldet. ## Umsetzung ### `.claude/workflows/plankritik.js`, Stelle 1 Aus: ```javascript if (unbekannt.length) throw new Error('plankritik: unbekannte Linse: ' + unbekannt.join(', ') + '; bekannt: ' + Object.keys(LINSEN).join(', ')) if (a.effort !== undefined && !EFFORTS.includes(a.effort)) throw new Error('plankritik: effort "' + a.effort + '" ungültig') ``` wird: ```javascript if (unbekannt.length) throw new Error('plankritik: unbekannte Linse: ' + unbekannt.join(', ') + '; bekannt: ' + Object.keys(LINSEN).join(', ')) if (a.linsen && new Set(a.linsen).size !== a.linsen.length) throw new Error('plankritik: eine Linse steht doppelt in linsen: ' + a.linsen.join(', ')) if (a.effort !== undefined && !EFFORTS.includes(a.effort)) throw new Error('plankritik: effort "' + a.effort + '" ungültig') ``` ### `.claude/workflows/planung.js`, Stelle 1 Aus: ```javascript laufend.delete(nr) const passt = roh && typeof roh.datei === 'string' && roh.datei.replace(/\\/g, '/').endsWith(jeNr.get(nr).datei) const r = passt ? roh : null ``` wird: ```javascript laufend.delete(nr) const passt = roh && typeof roh.datei === 'string' && ('/' + roh.datei.replace(/\\/g, '/')).endsWith('/' + jeNr.get(nr).datei) const r = passt ? roh : null ``` ### `.claude/workflows/welle-mit-pruefung.js`, Stelle 1 Aus: ```javascript return { status: 'fertig', kopie, ``` wird: ```javascript return { status: pruefung ? 'fertig' : 'ohne_pruefung', kopie, ``` ### `.vault-tools/workflow-pruefung/attrappe.mjs`, Stelle 1 Aus: ```javascript // "nesting is limited to one level"; die Attrappe setzt einen Doppelpunkt statt des Strichs. // - Im Kind ist phase() wirkungslos, und jeder agent() bekommt die Phase "▸ ". // - workflow() mit unbekanntem Namen oder unlesbarem scriptPath wirft. ``` wird: ```javascript // "nesting is limited to one level"; die Attrappe setzt einen Doppelpunkt statt des Strichs. // - Im Kind ist phase() wirkungslos, und jeder agent() bekommt die Phase "▸ ", ab dem // zweiten Aufruf desselben Kindes "▸ #" (Bundle 2.1.280 und meta.json eines // echten Laufs mit drei Ablesungen: "▸ verbrauch", "▸ verbrauch #2", "▸ verbrauch #3"). // - workflow() mit unbekanntem Namen oder unlesbarem scriptPath wirft. ``` ### `.vault-tools/workflow-pruefung/attrappe.mjs`, Stelle 2 Aus: ```javascript const laufendJePhase = {} let uhr = 0 ``` wird: ```javascript const laufendJePhase = {} const kindAufrufe = {} let uhr = 0 ``` ### `.vault-tools/workflow-pruefung/attrappe.mjs`, Stelle 3 Aus: ```javascript const { name, fn } = lade(datei) const kindPhase = tiefe >= 1 ? '▸ ' + name : null let aktuellePhase = null ``` wird: ```javascript const { name, fn } = lade(datei) let kindPhase = null if (tiefe >= 1) { kindAufrufe[name] = (kindAufrufe[name] || 0) + 1 kindPhase = '▸ ' + name + (kindAufrufe[name] > 1 ? ' #' + kindAufrufe[name] : '') } let aktuellePhase = null ``` ### `.vault-tools/workflow-pruefung/faelle_workflows.mjs`, Stelle 1 Aus: ```javascript // Exit 0, wenn alle Fälle bestehen, sonst 1. import { mkdtempSync, writeFileSync, rmSync } from 'node:fs' import os from 'node:os' ``` wird: ```javascript // Exit 0, wenn alle Fälle bestehen, sonst 1. import { mkdtempSync, writeFileSync, rmSync, cpSync } from 'node:fs' import os from 'node:os' ``` ### `.vault-tools/workflow-pruefung/faelle_workflows.mjs`, Stelle 2 Aus: ```javascript soll(JSON.stringify(p.phasen) === '["Oben"]', 'phase() im Kind hat gewirkt: ' + JSON.stringify(p.phasen)) return fehler ``` wird: ```javascript soll(JSON.stringify(p.phasen) === '["Oben"]', 'phase() im Kind hat gewirkt: ' + JSON.stringify(p.phasen)) return fehler }) fall('attrappe', 'dasselbe Kind zweimal: ▸ name und ▸ name #2', async () => { const { fehler, soll } = pruefe() const kind = skript('kindzwei', "return await agent('x', { label: 'werkzeug:planwerkzeuge', model: 'sonnet' })") const { protokoll: p } = await A(skript('elternzwei', `await workflow({ scriptPath: ${JSON.stringify(kind)} }, {}); return await workflow({ scriptPath: ${JSON.stringify(kind)} }, {})`)) soll(JSON.stringify(p.agents.map(a => a.phase)) === '["▸ kindzwei","▸ kindzwei #2"]', 'Phasen: ' + JSON.stringify(p.agents.map(a => a.phase))) return fehler ``` ### `.vault-tools/workflow-pruefung/faelle_workflows.mjs`, Stelle 3 Aus: ```javascript fall('verbrauch', 'Woche aller Modelle 90 % zählt auch neben einer niedrigen Opus-Woche', async () => { ``` wird: ```javascript fall('verbrauch', 'ein Label mit "hour" in der Mitte ist kein 5-Stunden-Fenster', async () => { const { fehler, soll } = pruefe() const { ergebnis: e } = await V({ anlass: 't', wunsch: 4, probe: ablesung(10, 10, [{ label: 'Weekly · Opus hours', percentUsed: 90, resetsAt: 'z' }]) }) soll(e && e.plaetze === 1 && e.fuenf_stunden.prozent === 10, JSON.stringify(e)) return fehler }) fall('verbrauch', 'Woche aller Modelle 90 % zählt auch neben einer niedrigen Opus-Woche', async () => { ``` ### `.vault-tools/workflow-pruefung/faelle_workflows.mjs`, Stelle 4 Aus: ```javascript fall('plankritik', 'Linse ohne Ergebnis steht in ohne_ergebnis', async () => { ``` wird: ```javascript fall('plankritik', 'doppelte Linse wirft vor dem ersten Agenten', async () => { const { fehler, soll } = pruefe() const { fehler: f, protokoll: p } = await PK({ ...PK_ARGS, linsen: ['arithmetik', 'bezeichner', 'arithmetik'] }, {}) soll(f && /doppelt in linsen/.test(f), 'Fehler: ' + f) soll(p.agents.length === 0, 'Agent gestartet') return fehler }) fall('plankritik', 'Linse ohne Ergebnis steht in ohne_ergebnis', async () => { ``` ### `.vault-tools/workflow-pruefung/faelle_workflows.mjs`, Stelle 5 Aus: ```javascript const { ergebnis: e, protokoll: p } = await WM(WM_ARGS, { executer: OHNE }) soll(e.status === 'fertig' && /unvollständig/.test(e.pruefung.uebersprungen), JSON.stringify(e.pruefung)) soll(mitLabel(p, 'verbrauch:').length === 0 && mitLabel(p, 'pruefung').length === 0, 'Ablesung oder Prüfung trotz Lücke') return fehler ``` wird: ```javascript const { ergebnis: e, protokoll: p } = await WM(WM_ARGS, { executer: OHNE }) soll(e.status === 'ohne_pruefung' && /unvollständig/.test(e.pruefung.uebersprungen), JSON.stringify([e.status, e.pruefung])) soll(mitLabel(p, 'verbrauch:').length === 0 && mitLabel(p, 'pruefung').length === 0, 'Ablesung oder Prüfung trotz Lücke') return fehler }) fall('welle-mit-pruefung', 'verbrauch.js fehlt: Ablesungen nicht_gelesen, Welle und Prüfung bleiben', async () => { const { fehler, soll } = pruefe() const ohneVerbrauch = mkdtempSync(path.join(os.tmpdir(), 'ohne-verbrauch-')) cpSync(ORDNER, ohneVerbrauch, { recursive: true }) rmSync(path.join(ohneVerbrauch, 'verbrauch.js')) const { ergebnis: e, fehler: f } = await fuehreAus(path.join(ohneVerbrauch, 'welle-mit-pruefung.js'), WM_ARGS, antworten({}), { ordner: ohneVerbrauch }) rmSync(ohneVerbrauch, { recursive: true, force: true }) soll(f === null && e.status === 'fertig' && e.welle && e.pruefung.zaehlung, 'Fehler/Ergebnis: ' + f + ' ' + JSON.stringify(e && e.status)) soll(e && e.verbrauch.length === 2 && e.verbrauch.every(v => v.status === 'nicht_gelesen'), JSON.stringify(e && e.verbrauch)) return fehler ``` ### `.vault-tools/workflow-pruefung/faelle_workflows.mjs`, Stelle 6 Aus: ```javascript fall('planung', '5 Stunden bei 60 %: höchstens zwei gleichzeitig', async () => { ``` wird: ```javascript fall('planung', 'Ergebnis mit ähnlichem Dateinamen gilt als gescheitert', async () => { const { fehler, soll } = pruefe() const { ergebnis: e } = await PL(PL_ARGS, { planerDatei: (nr, richtig) => (nr === 7 ? 'D:/x/plan/xtask-07-t.md' : richtig) }) soll(JSON.stringify(e.gescheitert) === '[7]', JSON.stringify(e.gescheitert)) return fehler }) fall('planung', 'Nachlesen im Takt von nachlesen_alle, solange Tasks offen sind', async () => { const { fehler, soll } = pruefe() const { protokoll: p } = await PL({ ...PL_ARGS, tasks: FREI, plaetze: 2, nachlesen_alle: 2 }, { planerDauer: () => 100 }) const ab = mitLabel(p, 'verbrauch:').map(x => x.label.slice('verbrauch:planung:'.length)) soll(JSON.stringify(ab) === '["Anfang","nach 2 Tasks","nach 4 Tasks","nach 6 Tasks","Ende"]', 'Ablesungen: ' + JSON.stringify(ab)) return fehler }) fall('planung', '5 Stunden bei 60 %: höchstens zwei gleichzeitig', async () => { ``` ### `.vault-tools/workflow-pruefung/faelle_workflows.mjs`, Stelle 7 Aus: ```javascript soll(!/\{(NR|THEMA|DATEI|MODELL|WELLE|VORAUSSETZUNGEN)\}/.test(t5), 'Platzhalter übrig') soll(/vor: \(keine\)/.test(planerVon(p, 1).prompt), 'Task 1 ohne "(keine)"') ``` wird: ```javascript soll(!/\{(NR|THEMA|DATEI|MODELL|WELLE|VORAUSSETZUNGEN)\}/.test(t5), 'Platzhalter übrig') soll(/Lies sie, bevor du einen Namen, eine Signatur oder einen Import daraus übernimmst/.test(t5), 'Lesehinweis fehlt') soll(!/Lies sie/.test(planerVon(p, 1).prompt), 'Lesehinweis ohne Voraussetzungen') soll(/vor: \(keine\)/.test(planerVon(p, 1).prompt), 'Task 1 ohne "(keine)"') ``` ### `.vault-tools/workflow-pruefung/mutanten_workflows.py`, Stelle 1 Aus: ```python ("planung", "Rueckfall ohne Ablesung 3", "const OHNE_ABLESUNG = 2", "const OHNE_ABLESUNG = 3"), ] ``` wird: ```python ("planung", "Rueckfall ohne Ablesung 3", "const OHNE_ABLESUNG = 2", "const OHNE_ABLESUNG = 3"), ("planung", "Datei ohne Pfadgrenze", "('/' + roh.datei.replace(/\\\\/g, '/')).endsWith('/' + jeNr.get(nr).datei)", "roh.datei.replace(/\\\\/g, '/').endsWith(jeNr.get(nr).datei)"), ("planung", "Nachlesezaehler nicht zurueckgesetzt", " fertigSeitAblesung = 0\n const v = await ablesen(", " const v = await ablesen("), ("planung", "nachlesen_alle ignoriert", "const nachlesenAlle = args.nachlesen_alle || NACHLESEN_ALLE", "const nachlesenAlle = NACHLESEN_ALLE"), ("planung", "Lesehinweis fehlt", " Lies sie, bevor du einen Namen, eine Signatur oder einen Import daraus übernimmst.", ""), ("plankritik", "doppelte Linse erlaubt", " if (a.linsen && new Set(a.linsen).size !== a.linsen.length) throw", " if (false) throw"), ("welle-mit-pruefung", "status fertig ohne Pruefung", "status: pruefung ? 'fertig' : 'ohne_pruefung',", "status: 'fertig',"), ("welle-mit-pruefung", "Ablesung ohne try", " try {\n r = await workflow({ scriptPath: VERBRAUCH }, { anlass: 'welle-mit-pruefung:' + anlass, wunsch: 0 })\n } catch (e) {\n r = { status: 'nicht_gelesen', grund: 'verbrauch.js nicht ausführbar: ' + (e && e.message) }\n }", " r = await workflow({ scriptPath: VERBRAUCH }, { anlass: 'welle-mit-pruefung:' + anlass, wunsch: 0 })"), ("verbrauch", "5-Stunden-Regex unverankert", "const FUENF_STUNDEN = /^\\s*5[- ]?hour/i", "const FUENF_STUNDEN = /hour/i"), ] ``` ### `.vault-tools/workflow-pruefung/faelle_verbrauch_reihe.py`, Stelle 1 Aus: ```python def f_fallender_wert(tmp): ``` wird: ```python def f_verschiebung(tmp, reset5, erwartet): # Nur resetsAt verschiebt sich, der Wert steigt; entscheidend ist allein die Toleranz. baue(tmp, {"wf_v": [ablesung(1, "a", "2026-09-24T10:00:00Z", 10, 5), ablesung(2, "b", "2026-09-24T10:20:00Z", 12, 5, reset5=reset5)]}) code, aus, _ = fahre(tmp) return [] if code == 0 and erwartet in aus else [f"Exit {code}, Ausgabe {aus[-300:]!r}"] def f_fallender_wert(tmp): ``` ### `.vault-tools/workflow-pruefung/faelle_verbrauch_reihe.py`, Stelle 2 Aus: ```python ("Absturz in einem Lauf ist ein Hinweis", f_absturz_ist_befund), ("fallender Wert im selben Fenster ist ein Reset", f_fallender_wert), ``` wird: ```python ("Absturz in einem Lauf ist ein Hinweis", f_absturz_ist_befund), ("resetsAt 5 Stunden weiter, Wert steigt: Reset", lambda t: f_verschiebung(t, "2026-09-24T23:30:00Z", "| Reset | 0.0 |")), ("resetsAt 4:59 verschoben: dasselbe Fenster", lambda t: f_verschiebung(t, "2026-09-24T18:34:59Z", "| 2.0 | 0.0 |")), ("resetsAt 5:01 verschoben: Reset", lambda t: f_verschiebung(t, "2026-09-24T18:35:01Z", "| Reset | 0.0 |")), ("resetsAt 5:01 frueher: Reset", lambda t: f_verschiebung(t, "2026-09-24T18:24:59Z", "| Reset | 0.0 |")), ("fallender Wert im selben Fenster ist ein Reset", f_fallender_wert), ``` ### `.vault-tools/workflow-pruefung/mutanten_reihe.py`, Stelle 1 Aus: ```python ("Reset nur bei gleichem Text", "if abs(rb - ra) > RESET_TOLERANZ or b[0] < a[0]:", "if a[1] != b[1] or b[0] < a[0]:"), ("fallender Wert kein Reset", "if abs(rb - ra) > RESET_TOLERANZ or b[0] < a[0]:", "if abs(rb - ra) > RESET_TOLERANZ:"), ``` wird: ```python ("Reset nur bei gleichem Text", "if abs(rb - ra) > RESET_TOLERANZ or b[0] < a[0]:", "if a[1] != b[1] or b[0] < a[0]:"), ("Toleranz 6 Stunden", "RESET_TOLERANZ = timedelta(minutes=5)", "RESET_TOLERANZ = timedelta(hours=6)"), ("Toleranz 1 Sekunde", "RESET_TOLERANZ = timedelta(minutes=5)", "RESET_TOLERANZ = timedelta(seconds=1)"), ("Toleranz nur nach vorn", "if abs(rb - ra) > RESET_TOLERANZ or b[0] < a[0]:", "if rb - ra > RESET_TOLERANZ or b[0] < a[0]:"), ("fallender Wert kein Reset", "if abs(rb - ra) > RESET_TOLERANZ or b[0] < a[0]:", "if abs(rb - ra) > RESET_TOLERANZ:"), ``` ## Verifikation Im Vault-Wurzelordner: ```bash node .vault-tools/workflow-pruefung/faelle_workflows.mjs ``` Erwartet: Alle übrigen Zeilen beginnen mit `OK`, die letzte enthält `0 gescheitert`, Exit 0. ```bash python .vault-tools/workflow-pruefung/faelle_verbrauch_reihe.py ``` Erwartet: Die letzte Zeile enthält `0 gescheitert`, Exit 0. ```bash python .vault-tools/workflow-pruefung/mutanten_reihe.py ``` Erwartet: Die letzte Zeile enthält `0 ueberlebt, 0 veraltet`, Exit 0. ```bash python .vault-tools/workflow-pruefung/mutanten_workflows.py ``` Erwartet: Die letzte Zeile enthält `0 ueberlebt, 0 veraltet`, Exit 0. Der Lauf dauert einige Minuten. Danach `git status --porcelain -- .claude/workflows/plankritik.js .claude/workflows/planung.js .claude/workflows/welle-mit-pruefung.js .vault-tools/workflow-pruefung/attrappe.mjs .vault-tools/workflow-pruefung/faelle_workflows.mjs .vault-tools/workflow-pruefung/mutanten_workflows.py .vault-tools/workflow-pruefung/faelle_verbrauch_reihe.py .vault-tools/workflow-pruefung/mutanten_reihe.py` zeigen.