Automatischer Push: 11 Datei(en) geändert
- .claude/agents/planer.md - .claude/workflows/plankritik.js - .claude/workflows/quorum.js - .claude/workflows/welle-mit-pruefung.js - .vault-tools/plan-kopie/LIESMICH.md - .vault-tools/plan-kopie/plan_bloecke.py - .vault-tools/plan-kopie/plan_materialisiere.py - .vault-tools/plan-kopie/plan_zaun_treiber.py - .vault-tools/workflow-pruefung/LIESMICH.md - .vault-tools/workflow-pruefung/faelle_workflows.mjs - .vault-tools/workflow-pruefung/mutanten_workflows.py Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
7f484f9be5
commit
22761eb186
11 files changed
+1520
-65
No files matched your search
@@ -33,11 +33,23 @@ function antworten(k = {}) {
|
||||
if (x === OHNE) return undefined
|
||||
return ok(x === undefined ? ablesung(10, 10) : x)
|
||||
}
|
||||
if (label === 'werkzeug:planwerkzeuge') return ok({ laeufe: [{ befehl: 'planwerkzeuge', exit: 0, ausgabe: 'keine Befunde' }] })
|
||||
if (label === 'werkzeug:kopie') return ok({ laeufe: [{ befehl: 'kopie', exit: 0, ausgabe: "KOPIE=C:/tmp/k/mat\n{'neu': 2}" }] })
|
||||
if (label === 'werkzeug:vergleich') return ok({ laeufe: [{ befehl: 'vergleich', exit: 0, ausgabe: 'VERGLEICH-OK\nEXIT suite: 0' }] })
|
||||
if (label === 'werkzeug:planwerkzeuge') {
|
||||
if (k.planwerkzeuge === OHNE) return undefined
|
||||
if (Array.isArray(k.planwerkzeuge)) return ok({ laeufe: k.planwerkzeuge })
|
||||
return ok({ laeufe: [{ befehl: 'planwerkzeuge', exit: 0, ausgabe: k.planwerkzeuge !== undefined ? k.planwerkzeuge : 'keine Befunde' }] })
|
||||
}
|
||||
if (label === 'werkzeug:kopie') {
|
||||
if (k.kopie === OHNE) return undefined
|
||||
if (Array.isArray(k.kopie)) return ok({ laeufe: k.kopie })
|
||||
return ok({ laeufe: [k.kopie || { befehl: 'kopie', exit: 0, ausgabe: "KOPIE=C:/tmp/k/mat\n{'neu': 2}" }] })
|
||||
}
|
||||
if (label === 'werkzeug:vergleich') {
|
||||
if (Array.isArray(k.vergleich)) return ok({ laeufe: k.vergleich })
|
||||
return ok({ laeufe: [{ befehl: 'vergleich', exit: 0, ausgabe: 'VERGLEICH-OK\nEXIT suite: 0' }] })
|
||||
}
|
||||
if (label === 'werkzeug:mutanten') {
|
||||
if (k.mutanten === OHNE) return undefined
|
||||
if (Array.isArray(k.mutanten)) return ok({ laeufe: k.mutanten })
|
||||
const ausgabe = k.mutantenAusgabe !== undefined ? k.mutantenAusgabe : 'wurzel: /tmp/x\nM1 OK a=GRUEN(x)\nergebnis: 1 Mutanten, 1 OK, 0 LUECKE, 0 UNKLAR, 0 ZEIT, 0 MUSTER\nEXIT mutanten_lauf: 0'
|
||||
return ok({ laeufe: [{ befehl: 'mutanten', exit: 0, ausgabe }] })
|
||||
}
|
||||
@@ -766,6 +778,267 @@ fall('plankritik', 'mutanten_status und mutanten_modus in allen Zuständen', asy
|
||||
return fehler
|
||||
})
|
||||
|
||||
// Planwerkzeuge mit Befund (2026-11-02, Auftrag der Startsitzung, Pascal: "Ja, nachziehen"): Ein
|
||||
// Planwerkzeug mit Exit 2 und mindestens einer Zeile "BEFUND <datei>:<zeile>:" ist gelaufen. Seine
|
||||
// Rohausgabe geht an die Linse bezeichner, die Zeilen nach werkzeug_befunde. Exit 2 ohne Befundzeile ist
|
||||
// ein gescheitertes Werkzeug; "Hilfsagent ohne Ergebnis" heißt nur noch: kein Lauf.
|
||||
const PK_TEXT = { projekt: 'D:/x', planordner: 'D:/x/plan' }
|
||||
const Z_OK = ['0 Befund(e).', 'EXIT plan_zeichen: 0']
|
||||
const B_OK = ['task-01.md: 2 Aus-/wird-Bloecke, im Modus vor geprueft: 1', '0 Befund(e).', 'EXIT plan_bloecke: 0']
|
||||
const B_ZAUN = 'BEFUND task-01.md:9: Zaun klebt an der Codezeile; Bloecke dieser Taskdatei nicht geprueft'
|
||||
const Z_BEFUND = 'BEFUND task-02.md:4:12: U+200B ZERO WIDTH SPACE (Cf) in: x'
|
||||
const ERRNO = "python: can't open file 'D:/x/werkzeuge/plan_bloecke.py': [Errno 2] No such file or directory"
|
||||
const werkzeuge = (...zeilen) => zeilen.flat().join('\n')
|
||||
const bezeichner = p => (mitLabel(p, 'plankritik:bezeichner')[0] || {}).prompt || ''
|
||||
const mitAusgabe = (p, text) => { const a = bezeichner(p); const i = a.indexOf('<werkzeugausgabe>'); return i >= 0 && a.indexOf(text, i) > i }
|
||||
const hilfsagentLog = p => p.logs.filter(l => /Hilfsagent ohne Ergebnis/.test(l))
|
||||
const kopieLauf = (exit, ...zeilen) => ({ befehl: 'kopie', exit, ausgabe: ['KOPIE=C:/tmp/k/mat', '12 Dateien kopiert, 1 Pfade ausgeschlossen, 0 Verknuepfungen uebersprungen, 0 Pakete in der Kopie.', '0 Befund(e).', ...zeilen].join('\n') })
|
||||
|
||||
fall('plankritik', 'Planwerkzeuge mit Exit 0: Rohausgabe geht an die Linse bezeichner, werkzeug_befunde leer', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: werkzeuge(Z_OK, B_OK) })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(mitAusgabe(p, 'EXIT plan_bloecke: 0'), 'Rohausgabe fehlt im Auftrag der Linse bezeichner')
|
||||
soll(e && Array.isArray(e.werkzeug_befunde) && e.werkzeug_befunde.length === 0, 'werkzeug_befunde ' + JSON.stringify(e && e.werkzeug_befunde))
|
||||
soll(!p.logs.some(l => l.startsWith('Planwerkzeuge:')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'plan_bloecke Exit 2 mit Zaunbefund: gelaufen, Rohausgabe an die Linse, Zeile unter werkzeug_befunde', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: werkzeuge(Z_OK, B_ZAUN, '1 Befund(e).', 'EXIT plan_bloecke: 2') })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(mitAusgabe(p, B_ZAUN), 'Befundzeile fehlt im Auftrag der Linse bezeichner')
|
||||
soll(e && JSON.stringify(e.werkzeug_befunde) === JSON.stringify([{ werkzeug: 'plan_bloecke', befund: B_ZAUN }]), 'werkzeug_befunde ' + JSON.stringify(e && e.werkzeug_befunde))
|
||||
soll(!p.logs.some(l => l.startsWith('Planwerkzeuge:')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'plan_zeichen Exit 2 mit Befund (erstes Werkzeug): gelaufen und plan_zeichen zugeordnet', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: werkzeuge(Z_BEFUND, '1 Befund(e).', 'EXIT plan_zeichen: 2', B_OK) })
|
||||
soll(mitAusgabe(p, Z_BEFUND), 'Befundzeile fehlt im Auftrag der Linse bezeichner')
|
||||
soll(e && JSON.stringify(e.werkzeug_befunde) === JSON.stringify([{ werkzeug: 'plan_zeichen', befund: Z_BEFUND }]), 'werkzeug_befunde ' + JSON.stringify(e && e.werkzeug_befunde))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'Befunde beider Werkzeuge mit CRLF: je Werkzeug zugeordnet, ohne CR', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const aus = [Z_BEFUND, '1 Befund(e).', 'EXIT plan_zeichen: 2', B_ZAUN, '1 Befund(e).', 'EXIT plan_bloecke: 2'].join('\r\n')
|
||||
const { ergebnis: e, protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: aus })
|
||||
soll(e && JSON.stringify(e.werkzeug_befunde) === JSON.stringify([{ werkzeug: 'plan_zeichen', befund: Z_BEFUND }, { werkzeug: 'plan_bloecke', befund: B_ZAUN }]), 'werkzeug_befunde ' + JSON.stringify(e && e.werkzeug_befunde))
|
||||
soll(mitAusgabe(p, B_ZAUN), 'Rohausgabe fehlt im Auftrag der Linse bezeichner')
|
||||
return fehler
|
||||
})
|
||||
|
||||
for (const [name, aus, werkzeug] of [
|
||||
['plan_bloecke Exit 2 ohne Befundzeile (Errno 2)', werkzeuge(Z_OK, ERRNO, 'EXIT plan_bloecke: 2'), 'plan_bloecke Exit 2 ohne Befundzeile'],
|
||||
['plan_zeichen Exit 2 ohne Befundzeile (erstes Werkzeug)', werkzeuge(ERRNO.replace('plan_bloecke', 'plan_zeichen'), 'EXIT plan_zeichen: 2', B_OK), 'plan_zeichen Exit 2 ohne Befundzeile'],
|
||||
['plan_bloecke Exit 3 ohne "Nichts geprueft"', werkzeuge(Z_OK, 'EXIT plan_bloecke: 3'), 'plan_bloecke Exit 3'],
|
||||
['plan_bloecke ohne EXIT-Zeile', werkzeuge(Z_OK, B_ZAUN), 'plan_bloecke ohne EXIT-Zeile'],
|
||||
]) {
|
||||
fall('plankritik', `${name}: gescheitert, Linse fährt selbst, Log nennt das Werkzeug statt des Hilfsagenten`, async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { fehler: f, protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: aus })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(!/<werkzeugausgabe>/.test(bezeichner(p)) && /Fahre dazu plan_bloecke\.py vor/.test(bezeichner(p)), 'Linse bekommt die Ausgabe eines gescheiterten Werkzeugs')
|
||||
soll(p.logs.some(l => l.startsWith('Planwerkzeuge: ') && l.includes(werkzeug)), 'Log nennt nicht ' + werkzeug + ': ' + p.logs.join(' | '))
|
||||
soll(hilfsagentLog(p).length === 0, 'Hilfsagent-Meldung trotz Lauf: ' + hilfsagentLog(p).join(' | '))
|
||||
return fehler
|
||||
})
|
||||
}
|
||||
|
||||
fall('plankritik', 'plan_bloecke Exit 3 mit "Nichts geprueft": gelaufen, Rohausgabe an die Linse', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: werkzeuge(Z_OK, 'task-01.md: 0 Aus-/wird-Bloecke, im Modus vor geprueft: 0', 'Nichts geprueft: kein Aus-/wird-Block gefunden.', 'EXIT plan_bloecke: 3') })
|
||||
soll(mitAusgabe(p, 'Nichts geprueft'), 'Rohausgabe fehlt im Auftrag der Linse bezeichner')
|
||||
soll(!p.logs.some(l => l.startsWith('Planwerkzeuge:')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'Hilfsagent ohne Lauf: nur dann "Hilfsagent ohne Ergebnis"', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: OHNE })
|
||||
soll(hilfsagentLog(p).some(l => l.startsWith('Planwerkzeuge: Hilfsagent ohne Ergebnis')), 'Log: ' + p.logs.join(' | '))
|
||||
soll(!/<werkzeugausgabe>/.test(bezeichner(p)), 'Linse bekommt eine Ausgabe ohne Lauf')
|
||||
return fehler
|
||||
})
|
||||
|
||||
const M_ARGS = { projekt: 'D:/x', planordner: 'D:/x/plan', materialisieren: { ohne: ['config/secrets.toml'] } }
|
||||
const MAT_BEFUND = 'BEFUND task-01.md:7: Zaun klebt an der Codezeile; nichts geschrieben'
|
||||
|
||||
fall('plankritik', 'materialisieren mit Planbefund: keine Kopie, Zeile unter werkzeug_befunde, temp_ordner genannt', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await PK(M_ARGS, { kopie: kopieLauf(2, MAT_BEFUND, '1 Befund(e).') })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.kopie === null && e.mutanten_status === 'ohne_kopie', JSON.stringify(e && [e.kopie, e.mutanten_status]))
|
||||
soll(e && JSON.stringify(e.werkzeug_befunde) === JSON.stringify([{ werkzeug: 'plan_materialisiere', befund: MAT_BEFUND }]), 'werkzeug_befunde ' + JSON.stringify(e && e.werkzeug_befunde))
|
||||
soll(e && /C:\/tmp\/k bleibt liegen/.test(e.temp_ordner || ''), 'temp_ordner ' + JSON.stringify(e && e.temp_ordner))
|
||||
soll(p.logs.some(l => /plan_materialisiere\.py meldet 1 Befund/.test(l)) && !p.logs.some(l => /Kopie nicht verwendbar/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'materialisieren mit BEFUND von kopie_bauen.py: kein Planbefund, Kopie nicht verwendbar', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await PK(M_ARGS, { kopie: { befehl: 'kopie', exit: 2, ausgabe: 'KOPIE=C:/tmp/k/mat\nBEFUND Projekt D:/x ist kein Ordner' } })
|
||||
soll(e && e.kopie === null && Array.isArray(e.werkzeug_befunde) && e.werkzeug_befunde.length === 0, JSON.stringify(e && [e.kopie, e.werkzeug_befunde]))
|
||||
soll(p.logs.some(l => /Kopie nicht verwendbar \(Exit 2/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'gebremst vor den Linsen: werkzeug_befunde steht auch dann in der Rückgabe', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e } = await PK(PK_TEXT, { planwerkzeuge: werkzeuge(Z_OK, B_ZAUN, '1 Befund(e).', 'EXIT plan_bloecke: 2'), ablesungen: [ablesung(90, 10)] })
|
||||
soll(e && e.status === 'gebremst', 'status ' + (e && e.status))
|
||||
soll(e && JSON.stringify(e.werkzeug_befunde) === JSON.stringify([{ werkzeug: 'plan_bloecke', befund: B_ZAUN }]), 'werkzeug_befunde ' + JSON.stringify(e && e.werkzeug_befunde))
|
||||
return fehler
|
||||
})
|
||||
|
||||
// Ein Hilfsagent darf seine Ausgabe auf mehrere laeufe verteilen. Im Echtlauf wf_9ab7fd8e-ad1 (2026-11-02)
|
||||
// gab werkzeug:planwerkzeuge je Werkzeug einen Lauf zurück; die zwei laeufe unten sind seine Rückgabe wörtlich.
|
||||
const ECHT_ZAUN = 'BEFUND task-01-probe.md:22: Zaun klebt an der Codezeile, Block ab Zeile 20; der Schlusszaun gehoert auf eine eigene Zeile, ein Text mit Zaunfolge in einen laengeren Zaun; Bloecke dieser Taskdatei nicht geprueft'
|
||||
const ECHT_LAEUFE = [
|
||||
{ befehl: 'plan_zeichen.py auf probe/plan', exit: 0, ausgabe: '1 Dateien, 2 Codebloecke gelesen.\nSichtbare Nicht-ASCII-Zeichen in Codebloecken: \n0 Befund(e).\nEXIT plan_zeichen: 0' },
|
||||
{ befehl: 'plan_bloecke.py vor probe/plan/task-*.md --projekt probe/projekt', exit: 2, ausgabe: ECHT_ZAUN + '\n1 Befund(e).\nEXIT plan_bloecke: 2' },
|
||||
]
|
||||
|
||||
fall('plankritik', 'Planwerkzeuge auf zwei laeufe verteilt (Echtlauf wf_9ab7fd8e-ad1): beide gelesen, Befund an die Linse', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await PK(PK_TEXT, { planwerkzeuge: ECHT_LAEUFE })
|
||||
soll(mitAusgabe(p, ECHT_ZAUN), 'Befundzeile aus dem zweiten Lauf fehlt im Auftrag der Linse bezeichner')
|
||||
soll(e && JSON.stringify(e.werkzeug_befunde) === JSON.stringify([{ werkzeug: 'plan_bloecke', befund: ECHT_ZAUN }]), 'werkzeug_befunde ' + JSON.stringify(e && e.werkzeug_befunde))
|
||||
soll(!p.logs.some(l => l.startsWith('Planwerkzeuge:')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'materialisieren auf zwei laeufe verteilt, Befundzeile im zweiten: Zeile unter werkzeug_befunde', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const kopie = [
|
||||
{ befehl: 'kopie_bauen.py', exit: 0, ausgabe: 'KOPIE=C:/tmp/k/mat\n12 Dateien kopiert, 1 Pfade ausgeschlossen, 0 Verknuepfungen uebersprungen, 0 Pakete in der Kopie.\n0 Befund(e).' },
|
||||
{ befehl: 'plan_materialisiere.py', exit: 2, ausgabe: MAT_BEFUND + '\n1 Befund(e).' },
|
||||
]
|
||||
const { ergebnis: e, protokoll: p } = await PK(M_ARGS, { kopie })
|
||||
soll(e && e.kopie === null && JSON.stringify(e.werkzeug_befunde) === JSON.stringify([{ werkzeug: 'plan_materialisiere', befund: MAT_BEFUND }]), JSON.stringify(e && [e.kopie, e.werkzeug_befunde]))
|
||||
soll(p.logs.some(l => /plan_materialisiere\.py meldet 1 Befund\(e\) \(Exit 0\/2\)/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
// Alle laeufe auch im Erfolgspfad der Kopie und im Mutantenlauf (Folgeauftrag 2026-11-02). KOPIE_GLIEDER ist die
|
||||
// Ausgabe einer Ausführung, je Glied der Kette mktemp, echo, kopie_bauen.py, plan_materialisiere.py auf einen Lauf
|
||||
// verteilt, wie im Echtlauf je Werkzeug: KOPIE-Zeile im zweiten, angewendeter Block im letzten. Verwendbar ist die
|
||||
// Kopie nur, wenn jeder Lauf Exit 0 hat; die Kette endet nur mit 0, wenn jedes Glied mit 0 endet. KOPIE_HALB: Der
|
||||
// erste Lauf ist vollständig und endet mit 0, danach stürzt plan_materialisiere.py an der zweiten Taskdatei ab;
|
||||
// laeufe[0] allein sah wie eine Kopie aus.
|
||||
const MAT_ZEILE = "task-01.md: {'neu': 0, 'ersetzt': 1, 'angehaengt': 0, 'uebergangen': 0, 'geloescht': 0}"
|
||||
const KOPIE_GLIEDER = (exits = [0, 0, 0, 0]) => [
|
||||
{ befehl: 'T=$(mktemp -d)', exit: exits[0], ausgabe: '' },
|
||||
{ befehl: 'echo "KOPIE=$(cygpath -m "$T/mat")"', exit: exits[1], ausgabe: 'KOPIE=C:/tmp/k/mat' },
|
||||
{ befehl: 'kopie_bauen.py', exit: exits[2], ausgabe: '12 Dateien kopiert, 1 Pfade ausgeschlossen, 0 Verknuepfungen uebersprungen, 0 Pakete in der Kopie.\n0 Befund(e).' },
|
||||
{ befehl: 'plan_materialisiere.py', exit: exits[3], ausgabe: MAT_ZEILE + '\n0 Befund(e).' },
|
||||
]
|
||||
const KOPIE_HALB = [
|
||||
{ befehl: 'kopie_bauen.py && plan_materialisiere.py', exit: 0, ausgabe: ['KOPIE=C:/tmp/k/mat', '12 Dateien kopiert, 1 Pfade ausgeschlossen, 0 Verknuepfungen uebersprungen, 0 Pakete in der Kopie.', '0 Befund(e).', MAT_ZEILE].join('\n') },
|
||||
{ befehl: 'plan_materialisiere.py, task-02.md', exit: 1, ausgabe: "Traceback (most recent call last):\nUnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte" },
|
||||
]
|
||||
// Exit 1 an erster bzw. letzter Stelle der vier Glieder, sonst 0.
|
||||
const exitsMitFehler = stelle => [0, 0, 0, 0].map((x, i) => (i === stelle ? 1 : x))
|
||||
// Der zweite Lauf ohne Feld exit: Er zählt nicht als 0 und steht im Log als undefined; das Schema LAUF verlangt
|
||||
// das Feld, die Attrappe prüft nur die oberste Ebene.
|
||||
const ohneExit = laeufe => laeufe.map((l, i) => (i === 1 ? { befehl: l.befehl, ausgabe: l.ausgabe } : l))
|
||||
|
||||
fall('plankritik', 'materialisieren auf vier laeufe verteilt, alle Exit 0: Kopie verwendet, Mutantenlauf und Linse arithmetik darauf', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await PK(M_ARGS, { kopie: KOPIE_GLIEDER() })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.kopie === 'C:/tmp/k/mat', 'kopie ' + JSON.stringify(e && e.kopie))
|
||||
soll(mitLabel(p, 'werkzeug:mutanten').length === 1, 'Mutantenlauf ohne Kopie: ' + mitLabel(p, 'werkzeug:mutanten').length)
|
||||
const arith = mitLabel(p, 'plankritik:arithmetik')[0]
|
||||
soll(arith && arith.prompt.includes('Der Plan ist auf die Kopie C:/tmp/k/mat angewendet'), 'Linse arithmetik rechnet ohne Kopie')
|
||||
soll(!p.logs.some(l => l.startsWith('Plankritik: ')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'materialisieren: erster Lauf vollständig mit Exit 0, Absturz im zweiten: keine Kopie, Log nennt beide Exits', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await PK({ ...M_ARGS, materialisieren: { ...M_ARGS.materialisieren, tasks: ['task-01.md', 'task-02.md'] } }, { kopie: KOPIE_HALB })
|
||||
soll(e && e.kopie === null && e.mutanten_status === 'ohne_kopie', JSON.stringify(e && [e.kopie, e.mutanten_status]))
|
||||
soll(p.logs.some(l => l.includes('Kopie nicht verwendbar (Exit 0/1)')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
for (const stelle of [0, 3]) {
|
||||
fall('plankritik', `materialisieren auf vier laeufe verteilt, Exit 1 im ${stelle ? 'letzten' : 'ersten'}: keine Kopie, Log nennt alle Exits`, async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const exits = exitsMitFehler(stelle)
|
||||
const { ergebnis: e, protokoll: p } = await PK(M_ARGS, { kopie: KOPIE_GLIEDER(exits) })
|
||||
soll(e && e.kopie === null && e.mutanten_status === 'ohne_kopie', JSON.stringify(e && [e.kopie, e.mutanten_status]))
|
||||
soll(p.logs.some(l => l.includes(`Kopie nicht verwendbar (Exit ${exits.join('/')})`)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
}
|
||||
|
||||
fall('plankritik', 'materialisieren auf vier laeufe verteilt, ein Lauf ohne exit: keine Kopie, Log nennt undefined', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await PK(M_ARGS, { kopie: ohneExit(KOPIE_GLIEDER()) })
|
||||
soll(e && e.kopie === null && e.mutanten_status === 'ohne_kopie', JSON.stringify(e && [e.kopie, e.mutanten_status]))
|
||||
soll(p.logs.some(l => l.includes('Kopie nicht verwendbar (Exit 0/undefined/0/0)')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'materialisieren, Hilfsagent ohne Ergebnis: keine Kopie, Log nennt den Hilfsagenten', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await PK(M_ARGS, { kopie: OHNE })
|
||||
soll(e && e.kopie === null && e.mutanten_status === 'ohne_kopie', JSON.stringify(e && [e.kopie, e.mutanten_status]))
|
||||
soll(p.logs.includes('Plankritik: Hilfsagent ohne Ergebnis, keine Kopie'), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
// Mutantenlauf auf zwei laeufe verteilt, etwa Anfang und Ende einer gekürzten Ausgabe: Die EXIT-Zeile und
|
||||
// KEINE_MUTANTEN_TOML stehen im zweiten.
|
||||
fall('plankritik', 'Mutantenlauf auf zwei laeufe verteilt, EXIT-Zeile im zweiten: exit_0, beide Teile an die Linse arithmetik', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const mutanten = [
|
||||
{ befehl: 'mutanten_lauf_kopie.sh, Anfang', exit: 0, ausgabe: 'wurzel: /tmp/x\nmodus: voll\nM1 OK a=ROT(MUTANT-ANFANG-01)' },
|
||||
{ befehl: 'mutanten_lauf_kopie.sh, Ende', exit: 0, ausgabe: 'M2 OK b=ROT(MUTANT-ENDE-02)\nergebnis: 2 Mutanten, 2 OK, 0 LUECKE, 0 UNKLAR, 0 ZEIT, 0 MUSTER\nEXIT mutanten_lauf: 0' },
|
||||
]
|
||||
const { ergebnis: e, protokoll: p } = await PK(PK_ARGS, { mutanten })
|
||||
soll(e && e.mutanten_status === 'exit_0', 'mutanten_status ' + JSON.stringify(e && e.mutanten_status))
|
||||
const arith = (mitLabel(p, 'plankritik:arithmetik')[0] || {}).prompt || ''
|
||||
const block = arith.includes('<mutantenlauf>') ? arith.slice(arith.indexOf('<mutantenlauf>')) : ''
|
||||
soll(block.includes('MUTANT-ANFANG-01') && block.includes('MUTANT-ENDE-02'), 'Block <mutantenlauf> ohne beide Teile: ' + JSON.stringify(block.slice(0, 300)))
|
||||
soll(e && typeof e.mutanten_lauf === 'string' && e.mutanten_lauf.includes('MUTANT-ANFANG-01') && e.mutanten_lauf.includes('EXIT mutanten_lauf: 0'), 'mutanten_lauf ' + JSON.stringify(e && e.mutanten_lauf))
|
||||
soll(!p.logs.some(l => l.startsWith('Mutantenlauf:')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('plankritik', 'Mutantenlauf auf zwei laeufe verteilt, KEINE_MUTANTEN_TOML im zweiten: keine_toml, Hinweissatz an die Linse', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const mutanten = [
|
||||
{ befehl: 'wsl -d Debian', exit: 0, ausgabe: 'wsl: Hinweis vor dem Skript' },
|
||||
{ befehl: 'mutanten_lauf_kopie.sh', exit: 0, ausgabe: 'KEINE_MUTANTEN_TOML' },
|
||||
]
|
||||
const { ergebnis: e, protokoll: p } = await PK(PK_ARGS, { mutanten })
|
||||
soll(e && e.mutanten_status === 'keine_toml', 'mutanten_status ' + JSON.stringify(e && e.mutanten_status))
|
||||
const arith = (mitLabel(p, 'plankritik:arithmetik')[0] || {}).prompt || ''
|
||||
soll(/keine mutanten\.toml/.test(arith) && !/<mutantenlauf>/.test(arith), 'Hinweissatz fehlt oder Block trotz KEINE_MUTANTEN_TOML')
|
||||
return fehler
|
||||
})
|
||||
|
||||
// Ein zusätzlicher Lauf des Hilfsagenten zeigt den Skripttext von mutanten_lauf_kopie.sh um die Suche nach
|
||||
// mutanten.toml: KEINE_MUTANTEN_TOML steht dort nur in einem echo, nicht als eigene Zeile der Ausgabe.
|
||||
fall('plankritik', 'Mutantenlauf mit zusätzlichem Lauf, der das Skript zeigt: exit_0 bleibt, kein Satz zur fehlenden mutanten.toml', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const mutanten = [
|
||||
{ befehl: 'mutanten_lauf_kopie.sh', exit: 0, ausgabe: 'wurzel: /tmp/x\nmodus: voll\nM1 OK a=ROT(MUTANT-SKRIPT-03)\nergebnis: 1 Mutanten, 1 OK, 0 LUECKE, 0 UNKLAR, 0 ZEIT, 0 MUSTER\nEXIT mutanten_lauf: 0' },
|
||||
{ befehl: 'cat mutanten_lauf_kopie.sh', exit: 0, ausgabe: 'MUTANTEN_TOML="$PLANORDNER/mutanten.toml"\nif [ ! -f "$MUTANTEN_TOML" ]; then\n echo "KEINE_MUTANTEN_TOML"\n exit 0\nfi' },
|
||||
]
|
||||
const { ergebnis: e, protokoll: p } = await PK(PK_ARGS, { mutanten })
|
||||
soll(e && e.mutanten_status === 'exit_0', 'mutanten_status ' + JSON.stringify(e && e.mutanten_status))
|
||||
const arith = (mitLabel(p, 'plankritik:arithmetik')[0] || {}).prompt || ''
|
||||
soll(arith.includes('<mutantenlauf>') && arith.includes('MUTANT-SKRIPT-03') && !/keine mutanten\.toml/.test(arith), 'Linse arithmetik ohne Mutantenlauf oder mit dem Satz zur fehlenden mutanten.toml')
|
||||
return fehler
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- pruefung
|
||||
const PR_ARGS = { projekt: 'D:/x', plan: 'D:/x/plan' }
|
||||
const PR = (a, k, o = {}) => fuehreAus(WF('pruefung'), a, antworten(k), { ordner: ORDNER, ...o })
|
||||
@@ -906,6 +1179,155 @@ for (const bis of ['alles', '', 1]) {
|
||||
})
|
||||
}
|
||||
|
||||
// Planbefund (2026-11-02, Auftrag der Startsitzung, Pascal: "Ja, nachziehen"): Meldet plan_materialisiere.py
|
||||
// in der Phase Kopie eine Zeile "BEFUND <taskdatei>:<zeile>:", endet der Lauf vor executer-welle. Exit 2
|
||||
// ohne Befundzeile und die BEFUND-Zeilen von kopie_bauen.py nehmen den alten Weg (Welle ohne Byte-Vergleich).
|
||||
const { kopie: _wmKopie, ...WM_OHNE_KOPIE } = WM_ARGS
|
||||
const WM_BAU = { ...WM_OHNE_KOPIE, ohne: ['config/secrets.toml'] }
|
||||
const ZAUN1 = 'BEFUND task-01.md:7: Zaun klebt an der Codezeile; nichts geschrieben'
|
||||
const ZAUN2 = 'BEFUND task-02.md:31: Block schliesst nie; nichts geschrieben'
|
||||
function ohneWelle(p, soll) {
|
||||
soll(!p.workflows.some(w => w.ref === 'executer-welle'), 'executer-welle aufgerufen')
|
||||
const danach = p.agents.filter(x => x.label !== 'werkzeug:kopie').map(x => x.label)
|
||||
soll(danach.length === 0, 'Agenten nach dem Halt: ' + JSON.stringify(danach))
|
||||
}
|
||||
|
||||
fall('welle-mit-pruefung', 'Planbefund: hält vor executer-welle, Befundzeile in plan_befunde', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await WM(WM_BAU, { kopie: kopieLauf(2, ZAUN1, '1 Befund(e).') })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.status === 'plan_befund', 'status ' + (e && e.status))
|
||||
soll(e && JSON.stringify(e.plan_befunde) === JSON.stringify([ZAUN1]), 'plan_befunde ' + JSON.stringify(e && e.plan_befunde))
|
||||
soll(e && e.welle === null && e.kopie === null && e.pruefung_args === null && /C:\/tmp\/k bleibt liegen/.test(e.temp_ordner || ''), JSON.stringify(e && [e.welle, e.kopie, e.pruefung_args, e.temp_ordner]))
|
||||
soll(e && e.kopie_lauf && e.kopie_lauf.laeufe[0].exit === 2, 'kopie_lauf ' + JSON.stringify(e && e.kopie_lauf))
|
||||
soll(p.logs.some(l => /meldet 1 Befund/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
ohneWelle(p, soll)
|
||||
ohneVerstoss(p, soll)
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('welle-mit-pruefung', 'Planbefund mit bis vergleich: hält ebenso, keine pruefung_args', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await WM({ ...WM_BAU, bis: 'vergleich' }, { kopie: kopieLauf(2, ZAUN1, '1 Befund(e).') })
|
||||
soll(e && e.status === 'plan_befund' && e.pruefung_args === null && /Befund/.test(e.pruefung_grund || ''), JSON.stringify(e && [e.status, e.pruefung_args, e.pruefung_grund]))
|
||||
ohneWelle(p, soll)
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('welle-mit-pruefung', 'Planbefund an erster und letzter Stelle, CRLF: beide Zeilen ohne CR, in Reihenfolge', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const tasks = [{ datei: 'task-01.md', modell: 'haiku' }, { datei: 'task-02.md', modell: 'haiku' }]
|
||||
const ausgabe = ['KOPIE=C:/tmp/k/mat', ZAUN1, 'zwischen', ZAUN2, '2 Befund(e).'].join('\r\n')
|
||||
const { ergebnis: e, protokoll: p } = await WM({ ...WM_BAU, tasks }, { kopie: { befehl: 'kopie', exit: 2, ausgabe } })
|
||||
soll(e && e.status === 'plan_befund' && JSON.stringify(e.plan_befunde) === JSON.stringify([ZAUN1, ZAUN2]), JSON.stringify(e && [e.status, e.plan_befunde]))
|
||||
soll(e && /C:\/tmp\/k bleibt liegen/.test(e.temp_ordner || ''), 'temp_ordner ' + JSON.stringify(e && e.temp_ordner))
|
||||
ohneWelle(p, soll)
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('welle-mit-pruefung', 'Absturz nach einer Befundzeile (Exit 1): hält ebenso', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await WM(WM_BAU, { kopie: kopieLauf(1, ZAUN1, 'Traceback (most recent call last):', 'OSError: x') })
|
||||
soll(e && e.status === 'plan_befund' && JSON.stringify(e.plan_befunde) === JSON.stringify([ZAUN1]), JSON.stringify(e && [e.status, e.plan_befunde]))
|
||||
ohneWelle(p, soll)
|
||||
return fehler
|
||||
})
|
||||
|
||||
for (const [name, lauf] of [
|
||||
['Exit 2 ohne Befundzeile (Errno 2)', { befehl: 'kopie', exit: 2, ausgabe: "KOPIE=C:/tmp/k/mat\n12 Dateien kopiert.\n0 Befund(e).\npython: can't open file 'D:/x/plan_materialisiere.py': [Errno 2] No such file or directory" }],
|
||||
['BEFUND von kopie_bauen.py ohne Zeilennummer', { befehl: 'kopie', exit: 2, ausgabe: 'KOPIE=C:/tmp/k/mat\nBEFUND Projekt D:/x ist kein Ordner' }],
|
||||
['BEFUND von kopie_bauen.py zu einem Ausschluss', { befehl: 'kopie', exit: 1, ausgabe: "KOPIE=C:/tmp/k/mat\n12 Dateien kopiert.\nBEFUND Ausschluss 'config/secrets.toml' gibt es nicht\n1 Befund(e)." }],
|
||||
]) {
|
||||
fall('welle-mit-pruefung', `${name}: kein Planbefund, Welle läuft wie bisher ohne Byte-Vergleich`, async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await WM(WM_BAU, { kopie: lauf })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.status === 'fertig' && e.kopie === null && !('plan_befunde' in e), JSON.stringify(e && [e.status, e.kopie, e.plan_befunde]))
|
||||
soll(mitLabel(p, 'executer:').length === 1, 'executer ' + mitLabel(p, 'executer:').length)
|
||||
soll(p.logs.some(l => /Kopie nicht verwendbar \(Exit [12]\)/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
}
|
||||
|
||||
fall('welle-mit-pruefung', 'Hilfsagent der Kopie ohne Ergebnis: Welle läuft wie bisher', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await WM(WM_BAU, { kopie: OHNE })
|
||||
soll(e && e.status === 'fertig' && mitLabel(p, 'executer:').length === 1, JSON.stringify(e && e.status))
|
||||
soll(p.logs.some(l => /Hilfsagent ohne Ergebnis/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('welle-mit-pruefung', 'Kopie auf zwei laeufe verteilt, Befundzeile im zweiten: hält ebenso', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const kopie = [
|
||||
{ befehl: 'kopie_bauen.py', exit: 0, ausgabe: 'KOPIE=C:/tmp/k/mat\n12 Dateien kopiert, 1 Pfade ausgeschlossen, 0 Verknuepfungen uebersprungen, 0 Pakete in der Kopie.\n0 Befund(e).' },
|
||||
{ befehl: 'plan_materialisiere.py', exit: 2, ausgabe: ZAUN1 + '\n1 Befund(e).' },
|
||||
]
|
||||
const { ergebnis: e, protokoll: p } = await WM(WM_BAU, { kopie })
|
||||
soll(e && e.status === 'plan_befund' && JSON.stringify(e.plan_befunde) === JSON.stringify([ZAUN1]), JSON.stringify(e && [e.status, e.plan_befunde]))
|
||||
soll(p.logs.some(l => /meldet 1 Befund\(e\) \(Exit 0\/2\)/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
ohneWelle(p, soll)
|
||||
return fehler
|
||||
})
|
||||
|
||||
// Alle laeufe auch im Erfolgspfad der Kopie und im Vergleich (Folgeauftrag 2026-11-02); KOPIE_GLIEDER, KOPIE_HALB
|
||||
// und exitsMitFehler stehen bei plankritik.
|
||||
const vergleichAuftrag = p => (mitLabel(p, 'werkzeug:vergleich')[0] || {}).prompt || ''
|
||||
|
||||
fall('welle-mit-pruefung', 'Kopie auf vier laeufe verteilt, alle Exit 0: Kopie verwendet, Byte-Vergleich gegen sie', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await WM(WM_BAU, { kopie: KOPIE_GLIEDER() })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.status === 'fertig' && e.kopie === 'C:/tmp/k/mat', JSON.stringify(e && [e.status, e.kopie]))
|
||||
soll(vergleichAuftrag(p).includes("--mat 'C:/tmp/k/mat'"), 'Byte-Vergleich ohne die Kopie: ' + vergleichAuftrag(p).slice(0, 300))
|
||||
soll(!p.logs.some(l => /Kopie nicht verwendbar/.test(l)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('welle-mit-pruefung', 'Kopie: erster Lauf vollständig mit Exit 0, Absturz im zweiten: Kopie nicht verwendet, Log nennt beide Exits', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const tasks = [{ datei: 'task-01.md', modell: 'haiku' }, { datei: 'task-02.md', modell: 'haiku' }]
|
||||
const { ergebnis: e, protokoll: p } = await WM({ ...WM_BAU, tasks }, { kopie: KOPIE_HALB })
|
||||
soll(e && e.status === 'fertig' && e.kopie === null && !('plan_befunde' in e), JSON.stringify(e && [e.status, e.kopie]))
|
||||
soll(!vergleichAuftrag(p).includes('--mat'), 'Byte-Vergleich gegen eine halbe Kopie: ' + vergleichAuftrag(p).slice(0, 300))
|
||||
soll(p.logs.some(l => l.includes('Kopie nicht verwendbar (Exit 0/1)')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
for (const stelle of [0, 3]) {
|
||||
fall('welle-mit-pruefung', `Kopie auf vier laeufe verteilt, Exit 1 im ${stelle ? 'letzten' : 'ersten'}: Kopie nicht verwendet, Log nennt alle Exits`, async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const exits = exitsMitFehler(stelle)
|
||||
const { ergebnis: e, protokoll: p } = await WM(WM_BAU, { kopie: KOPIE_GLIEDER(exits) })
|
||||
soll(e && e.status === 'fertig' && e.kopie === null, JSON.stringify(e && [e.status, e.kopie]))
|
||||
soll(p.logs.some(l => l.includes(`Kopie nicht verwendbar (Exit ${exits.join('/')})`)), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
}
|
||||
|
||||
fall('welle-mit-pruefung', 'Kopie auf vier laeufe verteilt, ein Lauf ohne exit: Kopie nicht verwendet, Log nennt undefined', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await WM(WM_BAU, { kopie: ohneExit(KOPIE_GLIEDER()) })
|
||||
soll(e && e.status === 'fertig' && e.kopie === null, JSON.stringify(e && [e.status, e.kopie]))
|
||||
soll(p.logs.some(l => l.includes('Kopie nicht verwendbar (Exit 0/undefined/0/0)')), 'Log: ' + p.logs.join(' | '))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('welle-mit-pruefung', 'Vergleich auf zwei laeufe verteilt: Byte-Vergleich und Suite erreichen den pruefer, in ihrer Reihenfolge', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const vergleich = [
|
||||
{ befehl: 'welle_vergleich.py', exit: 0, ausgabe: 'VERGLEICH-MARKE-01: task-01.md, 1 Datei, Bytes gleich\nEXIT welle_vergleich: 0' },
|
||||
{ befehl: 'pytest -q', exit: 1, ausgabe: 'SUITE-MARKE-02: 1 failed, 3 passed\nEXIT suite: 1' },
|
||||
]
|
||||
const { ergebnis: e, protokoll: p } = await WM(WM_ARGS, { vergleich })
|
||||
soll(e && e.status === 'fertig', 'status ' + (e && e.status))
|
||||
const auftrag = (mitLabel(p, 'pruefung')[0] || {}).prompt || ''
|
||||
const ab = auftrag.indexOf('Schwerpunkt:')
|
||||
soll(ab >= 0 && ['VERGLEICH-MARKE-01', 'EXIT welle_vergleich: 0', 'SUITE-MARKE-02', 'EXIT suite: 1'].every(t => auftrag.indexOf(t, ab) > ab), 'Auftrag des pruefer ohne beide laeufe: ' + JSON.stringify(auftrag.slice(ab, ab + 400)))
|
||||
soll(auftrag.indexOf('VERGLEICH-MARKE-01', ab) < auftrag.indexOf('SUITE-MARKE-02', ab), 'Reihenfolge der laeufe vertauscht: ' + JSON.stringify(auftrag.slice(ab, ab + 400)))
|
||||
return fehler
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- planung
|
||||
const T = (nr, voraussetzungen, welle) => ({ nr, thema: 'Thema ' + nr, datei: `task-${String(nr).padStart(2, '0')}-t.md`, modell: 'haiku', voraussetzungen, welle })
|
||||
const DAG = [T(1, [], 1), T(2, [1], 2), T(3, [1], 2), T(4, [1], 2), T(5, [2, 3], 3), T(6, [4], 3), T(7, [], 1)]
|
||||
@@ -1194,7 +1616,8 @@ function antwortenQuorum(k = {}) {
|
||||
let n = 0
|
||||
const stimmen = k.stimmen || Q_STIMMEN
|
||||
const ohne = k.ohne || []
|
||||
const lauf = (label, text) => ok({ laeufe: [{ befehl: label, exit: 0, ausgabe: text }] })
|
||||
// Eine Liste statt eines Textes sind die laeufe selbst, etwa auf mehrere laeufe verteilt.
|
||||
const lauf = (label, text) => ok({ laeufe: Array.isArray(text) ? text : [{ befehl: label, exit: 0, ausgabe: text }] })
|
||||
return async aufruf => {
|
||||
const label = aufruf.label || ''
|
||||
if (ohne.includes(label)) return undefined
|
||||
@@ -1447,6 +1870,8 @@ fall('quorum', 'Reihenfolge: blockierender Einzelbefund an erster und letzter St
|
||||
for (const [name, k, grund] of [
|
||||
['Status vor dem Lauf nicht gelesen', { codexStart: 'Fehler beim Lesen\nEXIT status: 1' }, /Status vor dem Lauf/],
|
||||
['Auftrag ohne jobId', { codexStart: '{\n "running": []\n}\nEXIT status: 0\nFehler\nEXIT task: 1' }, /nicht gestartet/],
|
||||
['Task-Antwort mit jobId, aber EXIT task 1', { codexStart: Q_START.replace('EXIT task: 0', 'EXIT task: 1') }, /^Codex-Auftrag nicht gestartet$/],
|
||||
['Status gescheitert, task nicht gelaufen: Grund ohne Warnung', { codexStart: 'Fehler beim Lesen\nEXIT status: 1' }, /^Status vor dem Lauf nicht gelesen$/],
|
||||
['usage limit im Log', { codexEnde: { usage_limit_treffer: 1 } }, /usage limit/],
|
||||
['Lesebeleg mit fremder Lesemarke', { codexEnde: { lesebeleg: 'Lesemarke: ffffffffffffffff' } }, /Lesebeleg/],
|
||||
['Job endet failed', { codexEnde: { job_status: 'failed' } }, /failed/],
|
||||
@@ -1544,6 +1969,281 @@ fall('quorum', 'Codex-Befehle: Plugin 1.0.6, nur lesend, Auftrag aus dem Paket,
|
||||
return fehler
|
||||
})
|
||||
|
||||
const qStartLaeufeVertauscht = text => {
|
||||
const [status, task] = text.split('\nEXIT status: 0\n')
|
||||
return [
|
||||
{ befehl: 'Startbefehl, Ausgabe von task', exit: 0, ausgabe: task },
|
||||
{ befehl: 'Startbefehl, Ausgabe von status', exit: 0, ausgabe: status + '\nEXIT status: 0' },
|
||||
]
|
||||
}
|
||||
const qStartZeilenLaeufe = zeilen => zeilen.map((ausgabe, i) => ({
|
||||
befehl: 'Startbefehl, Ausgabe Teil ' + (i + 1), exit: 0, ausgabe,
|
||||
}))
|
||||
const qStartNeueIdZeile = ' "jobId": "task-q1",'
|
||||
const qStartZeilen = Q_START_ALT.split('\n')
|
||||
const qStartOhneNeueId = Q_START_ALT.replace(qStartNeueIdZeile + '\n', '')
|
||||
const qStartStatusRoh = Q_START_ALT.split('\nEXIT status: 0\n')[0]
|
||||
const qStartTaskBlock = Q_START.split('\nEXIT status: 0\n')[1].replace('\nEXIT task: 0', '')
|
||||
const qStartTaskRoh = qStartTaskBlock + '\nEXIT task: 0'
|
||||
const qStartTaskKompakt = '{"jobId":"task-q1","status":"queued","title":"Codex Task","summary":".","logFile":"C:/tmp/q/codex.log"}'
|
||||
const qStartReihenfolgeHinweis = 'Codex-Start: Ausgabe nicht in Befehlsreihenfolge; jobId aus der task-Ausgabe (task-q1)'
|
||||
const qStartUnsicherGrund = 'jobId nicht sicher bestimmbar; Codex-Auftrag läuft womöglich ungesehen weiter'
|
||||
const qStartTaskGeteilt = [
|
||||
{ befehl: 'Startbefehl, Status und Task-Anfang', exit: 0, ausgabe: qStartStatusRoh + '\nEXIT status: 0\n{' },
|
||||
{ befehl: 'Startbefehl, Task-Fortsetzung', exit: 0, ausgabe: qStartTaskRoh.slice(2) },
|
||||
]
|
||||
const qStartAlterRest = ausgabe => [
|
||||
...qStartLaeufeVertauscht(qStartOhneNeueId.replace(' "jobId": "task-alt"\n', '')),
|
||||
{ befehl: 'Startbefehl, verschobener Statusrest', exit: 0, ausgabe },
|
||||
]
|
||||
const qStartAlterJob = (id, status) => ({
|
||||
createdAt: '2026-10-01T00:00:00.000Z',
|
||||
updatedAt: '2026-10-01T00:01:00.000Z',
|
||||
id,
|
||||
status,
|
||||
request: {
|
||||
cwd: 'D:/x',
|
||||
model: 'codex',
|
||||
effort: 'low',
|
||||
prompt: 'früherer Auftrag',
|
||||
write: false,
|
||||
resumeLast: false,
|
||||
jobId: id
|
||||
},
|
||||
})
|
||||
const qStartStatusKompakt = JSON.stringify({
|
||||
workspaceRoot: 'D:/x',
|
||||
config: { stopReviewGate: false },
|
||||
sessionRuntime: {},
|
||||
running: [qStartAlterJob('task-alt-queued', 'queued')],
|
||||
latestFinished: {
|
||||
createdAt: '2026-10-01T00:00:00.000Z',
|
||||
updatedAt: '2026-10-01T00:01:00.000Z',
|
||||
id: 'task-alt-ohne-request',
|
||||
status: 'completed',
|
||||
},
|
||||
recent: [],
|
||||
needsReview: false,
|
||||
})
|
||||
const qStartStatusUmformatiert = qStartStatusKompakt.replace(
|
||||
'"jobId":"task-alt-queued"',
|
||||
'\n "jobId": "task-alt-queued"\n'
|
||||
)
|
||||
const qStartK4Status = 'EXIT status: 0\n' + qStartStatusUmformatiert
|
||||
const qStartK7StatusZeilen = qStartStatusKompakt
|
||||
.replace(/^\{/, '{\n')
|
||||
.replace('"id":"task-alt-queued","status":"queued",', '"id":"task-alt-queued"\n,\n"status": "queued",\n')
|
||||
.replace('"jobId":"task-alt-queued"', '\n "jobId": "task-alt-queued"\n')
|
||||
.split('\n')
|
||||
const qStartK7Laeufe = qStartZeilenLaeufe([
|
||||
qStartK7StatusZeilen[0],
|
||||
qStartK7StatusZeilen[5],
|
||||
qStartK7StatusZeilen[2],
|
||||
qStartK7StatusZeilen[3],
|
||||
qStartK7StatusZeilen[1],
|
||||
qStartK7StatusZeilen[4],
|
||||
qStartK7StatusZeilen[6],
|
||||
'EXIT status: 0',
|
||||
'EXIT task: 0',
|
||||
])
|
||||
const qStartStatusLang = JSON.stringify({
|
||||
workspaceRoot: 'D:/x',
|
||||
config: { stopReviewGate: false },
|
||||
sessionRuntime: {},
|
||||
running: Array.from({ length: 24 }, (_, i) => qStartAlterJob('task-alt-' + (i + 1), 'queued')),
|
||||
latestFinished: qStartAlterJob('task-alt-fertig', 'completed'),
|
||||
recent: [],
|
||||
needsReview: false,
|
||||
}, null, 2)
|
||||
const qStartLangeZeilen = (qStartStatusLang + '\nEXIT status: 0\n' + qStartTaskRoh).split('\n')
|
||||
const qStartGekuerzteZeilen = [
|
||||
...qStartLangeZeilen.slice(0, 60),
|
||||
'[... ' + (qStartLangeZeilen.length - 180) + ' Zeilen gekürzt ...]',
|
||||
...qStartLangeZeilen.slice(-120),
|
||||
]
|
||||
const qStartGekuerzt = qStartGekuerzteZeilen.join('\n')
|
||||
const qStartB = 'STATUS (EXIT status: 0): workspaceRoot D:/x; config.stopReviewGate=false; running=[]; latestFinished: id task-alt\nTASK (EXIT task: 0): ' + qStartTaskKompakt + '\nEXIT task: 0'
|
||||
const qStartC = 'EXIT status: 0\nTASK-START: ' + qStartTaskKompakt + '\nEXIT task: 0'
|
||||
const qStartKompaktMitStatus = qStartStatusKompakt + '\nEXIT status: 0\nTASK-START: ' + qStartTaskKompakt + '\nEXIT task: 0'
|
||||
|
||||
for (const [name, codexStart, hinweisErwartet] of [
|
||||
['task vor status, ohne früheren Job', qStartLaeufeVertauscht(Q_START), true],
|
||||
['task vor status, mit früherem Job', qStartLaeufeVertauscht(Q_START_ALT), true],
|
||||
['Task-Block vor Status-JSON, EXIT-Zeilen in Befehlsreihenfolge', [
|
||||
{ befehl: 'Startbefehl, Task-Block', exit: 0, ausgabe: qStartTaskBlock },
|
||||
{ befehl: 'Startbefehl, Status mit EXIT', exit: 0, ausgabe: qStartStatusRoh + '\nEXIT status: 0' },
|
||||
{ befehl: 'Startbefehl, Task-Exit', exit: 0, ausgabe: 'EXIT task: 0' },
|
||||
], true],
|
||||
['Task-Block hinter EXIT task', [
|
||||
{ befehl: 'Startbefehl, Status mit EXIT', exit: 0, ausgabe: qStartStatusRoh + '\nEXIT status: 0' },
|
||||
{ befehl: 'Startbefehl, Task-Exit', exit: 0, ausgabe: 'EXIT task: 0' },
|
||||
{ befehl: 'Startbefehl, Task-Block', exit: 0, ausgabe: qStartTaskBlock },
|
||||
], true],
|
||||
['Mittelkürzung nach 60/120-Regel, danach task vor status', qStartLaeufeVertauscht(qStartGekuerzt), true],
|
||||
['Echtlauf B, kompakt mit TASK-Vorsatz und Status als Prosa', qStartB, false],
|
||||
['Echtlauf C, kompakt mit TASK-START und ohne Status-JSON', qStartC, false],
|
||||
['Echtlauf A, EXIT status vor Status-JSON', 'EXIT status: 0\n' + Q_START_ALT.replace('\nEXIT status: 0', ''), false],
|
||||
['dieselbe ID kompakt vor status und formatiert danach', [
|
||||
{ befehl: 'Startbefehl, kompakter Task-Treffer', exit: 0, ausgabe: qStartTaskKompakt },
|
||||
{ befehl: 'Startbefehl, Status und formatierter Task-Treffer', exit: 0, ausgabe: Q_START_ALT },
|
||||
], true],
|
||||
['K4: neuformatierter Status mit alter Request-Zeile und Task-Antwort', qStartK4Status + '\n' + qStartTaskRoh, false],
|
||||
['synthetisch: K7-Statusreste mit intakter Task-Antwort in eigenem Lauf', [
|
||||
...qStartK7Laeufe,
|
||||
{ befehl: 'Startbefehl, intakte Task-Antwort', exit: 0, ausgabe: qStartTaskBlock },
|
||||
], true],
|
||||
['geordnet: Status mit EXIT in einem Lauf, Task mit EXIT im nächsten', [
|
||||
{ befehl: 'Startbefehl, Status mit EXIT', exit: 0, ausgabe: qStartStatusRoh + '\nEXIT status: 0' },
|
||||
{ befehl: 'Startbefehl, Task mit EXIT', exit: 0, ausgabe: qStartTaskRoh },
|
||||
], false],
|
||||
['synthetisch: 15 leere Läufe vor Status und Task', [
|
||||
...Array.from({ length: 15 }, (_, i) => ({ befehl: 'Startbefehl, leerer Lauf ' + (i + 1), exit: 0, ausgabe: '' })),
|
||||
{ befehl: 'Startbefehl, Status mit EXIT', exit: 0, ausgabe: qStartStatusRoh + '\nEXIT status: 0' },
|
||||
{ befehl: 'Startbefehl, Task mit EXIT', exit: 0, ausgabe: qStartTaskRoh },
|
||||
], false],
|
||||
['synthetisch: Vorsatz mit EXIT status vor dem Task-Block, Status-JSON danach', [
|
||||
{ befehl: 'Startbefehl, Vorsatz und Task-Block', exit: 0, ausgabe: 'Vorab (EXIT status: 0): ' + qStartTaskBlock },
|
||||
{ befehl: 'Startbefehl, Status mit EXIT', exit: 0, ausgabe: qStartStatusRoh + '\nEXIT status: 0' },
|
||||
{ befehl: 'Startbefehl, Task-Exit', exit: 0, ausgabe: 'EXIT task: 0' },
|
||||
], true],
|
||||
]) {
|
||||
fall('quorum', 'Codex-Start (' + name + '): neue Stimme und passende Hinweise', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
if (name === 'Mittelkürzung nach 60/120-Regel, danach task vor status') {
|
||||
soll(qStartStatusLang.split('\n').length > 200, 'Statusvorlage hat nicht mehr als 200 Zeilen')
|
||||
soll(qStartGekuerzteZeilen.length === 181, 'Kürzung enthält nicht 60 + 1 + 120 Zeilen')
|
||||
soll(qStartGekuerzteZeilen[60] === '[... ' + (qStartLangeZeilen.length - 180) + ' Zeilen gekürzt ...]', 'Kürzungsmarker oder Zeilenzahl falsch')
|
||||
soll(qStartGekuerzteZeilen.includes(qStartNeueIdZeile), 'neue Task-ID ging bei der Kürzung verloren')
|
||||
}
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await QR(Q_ARGS, { codexStart })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.stimmen.codex.abgegeben === true && e.stimmen.codex.job === 'task-q1' && e.stimmen.codex.grund === '', 'Codex-Stimme ' + JSON.stringify(e && e.stimmen.codex))
|
||||
const warten = mitLabel(p, 'codex:warten:')
|
||||
const ende = mitLabel(p, 'codex:ende')
|
||||
soll(warten.length === 1 && warten[0].prompt.includes("status 'task-q1' --wait"), 'Warten nicht genau einmal auf task-q1')
|
||||
soll(ende.length === 1 && ende[0].prompt.includes("result 'task-q1' --json"), 'Auswertung nicht genau einmal für task-q1')
|
||||
soll(mitLabel(p, 'stimme:haiku').length === 0 && mitLabel(p, 'codex:abbruch').length === 0, 'Haiku oder Abbruch trotz fertigem neuen Auftrag')
|
||||
soll(p.agents.every(x => !String(x.prompt).includes('task-alt')), 'ein Auftrag nennt einen früheren Job')
|
||||
soll(p.logs.filter(text => text === qStartReihenfolgeHinweis).length === Number(hinweisErwartet), 'Reihenfolgehinweis im Protokoll falsch')
|
||||
soll(e && e.hinweise.includes(qStartReihenfolgeHinweis) === hinweisErwartet && e.bericht_md.includes(qStartReihenfolgeHinweis) === hinweisErwartet, 'Reihenfolgehinweis in Hinweisen oder Bericht falsch')
|
||||
soll(e && !e.hinweise.some(text => text.includes(qStartUnsicherGrund)) && !e.bericht_md.includes(qStartUnsicherGrund), 'Warnung trotz eindeutiger neuer ID')
|
||||
return fehler
|
||||
})
|
||||
}
|
||||
|
||||
for (const [name, codexStart, hinweisErwartet] of [
|
||||
['vertauscht', qStartLaeufeVertauscht(Q_START_ALT), true],
|
||||
['kompakt mit früherem queued-Job', qStartKompaktMitStatus, false],
|
||||
]) {
|
||||
fall('quorum', 'Codex-Start (' + name + '): neuer Auftrag wird nach zwei Warterunden abgebrochen', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await QR({ ...Q_ARGS, codex_runden: 2 }, {
|
||||
codexStart,
|
||||
codexWarten: () => Q_WARTEN_OFFEN,
|
||||
})
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
const warten = mitLabel(p, 'codex:warten:')
|
||||
const abbruch = mitLabel(p, 'codex:abbruch')
|
||||
soll(warten.length === 2 && warten.every(x => x.prompt.includes("status 'task-q1' --wait")), 'nicht zweimal auf den neuen Auftrag gewartet')
|
||||
soll(abbruch.length === 1 && abbruch[0].prompt.includes("cancel 'task-q1' --json"), 'neuer Auftrag nicht genau einmal abgebrochen')
|
||||
soll(e && e.stimmen.codex.job === 'task-q1' && e.stimmen.codex.abgegeben === false && /nach 2 Warterunden/.test(e.stimmen.codex.grund), 'Codex-Ergebnis ' + JSON.stringify(e && e.stimmen.codex))
|
||||
soll(mitLabel(p, 'stimme:haiku').length === 1 && mitLabel(p, 'codex:ende').length === 0, 'Ersatz oder Auswertung nach Zeitüberschreitung falsch')
|
||||
soll(p.agents.every(x => !String(x.prompt).includes('task-alt')), 'Warten oder Abbruch nennt einen früheren Job')
|
||||
soll(p.logs.filter(text => text === qStartReihenfolgeHinweis).length === Number(hinweisErwartet), 'Reihenfolgehinweis vor dem Warten falsch')
|
||||
soll(e && e.hinweise.includes(qStartReihenfolgeHinweis) === hinweisErwartet && e.bericht_md.includes(qStartReihenfolgeHinweis) === hinweisErwartet, 'Reihenfolgehinweis im Ergebnis falsch')
|
||||
return fehler
|
||||
})
|
||||
}
|
||||
|
||||
for (const [name, codexStart, grund] of [
|
||||
[
|
||||
'Task-Nutzlast in umgekehrte Einzelzeilen zerlegt',
|
||||
qStartZeilenLaeufe(qStartZeilen.slice().reverse()),
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'keine task-ID',
|
||||
qStartLaeufeVertauscht(Q_START.replace(qStartNeueIdZeile + '\n', '')),
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'nur alte Status-ID',
|
||||
qStartLaeufeVertauscht(qStartOhneNeueId),
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'alte Status-ID hinter EXIT status',
|
||||
qStartAlterRest(' "jobId": "task-alt"'),
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'unvollständige Rückgabe ohne Status und task-ID',
|
||||
[
|
||||
{ befehl: 'Startbefehl, Ausgaberest', exit: 0, ausgabe: '[... 250 Zeilen gekürzt ...]\nEXIT task: 0' },
|
||||
],
|
||||
'Status vor dem Lauf nicht gelesen; Codex-Auftrag läuft womöglich ungesehen weiter',
|
||||
],
|
||||
[
|
||||
'kompakter Status mit früherem queued-Job, ohne Task-Antwort',
|
||||
qStartStatusKompakt + '\nEXIT status: 0\nEXIT task: 0',
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'synthetisch: alte Request-ID mit Komma vor queued, ohne Objektöffnung',
|
||||
qStartAlterRest(' "jobId": "task-alt",\n "status": "queued"'),
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'synthetisch: alte Request-ID ohne Komma vor queued, mit Objektöffnung',
|
||||
qStartAlterRest('{\n "jobId": "task-alt"\n "status": "queued"\n}'),
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'zwei verschiedene IDs aus formatiertem und kompaktem Task-Objekt',
|
||||
qStartLaeufeVertauscht(Q_START_ALT + '\n' + qStartTaskKompakt.replace('task-q1', 'task-q2')),
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'K4: neuformatierter Status mit alter Request-Zeile, ohne Task-Antwort',
|
||||
qStartK4Status + '\nEXIT task: 0',
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'synthetisch: Task-Objektanfang mit completed statt queued',
|
||||
'{\n "running": []\n}\nEXIT status: 0\n{"jobId":"task-alt","status":"completed","title":"synthetisch"}\nEXIT task: 0',
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'synthetisch: K7-Merkmal aus vier einzeiligen Läufen, nur Statusreste',
|
||||
qStartK7Laeufe,
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
[
|
||||
'echte Task-Antwort am Objektanfang über zwei Läufe zerrissen',
|
||||
qStartTaskGeteilt,
|
||||
qStartUnsicherGrund,
|
||||
],
|
||||
]) {
|
||||
fall('quorum', 'Codex-Start unsicher (' + name + '): Haiku und Warnung vor ungesehenem Auftrag', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
if (name === 'synthetisch: K7-Merkmal aus vier einzeiligen Läufen, nur Statusreste') {
|
||||
soll(qStartK7StatusZeilen.length === 7 && JSON.stringify(JSON.parse(qStartK7StatusZeilen.join('\n'))) === qStartStatusKompakt, 'K7-Statusvorlage ändert JSON-Bestandteile oder ihre Reihenfolge')
|
||||
soll(qStartK7Laeufe.every(l => !l.ausgabe.includes('\n')), 'K7-Statusfragmente sind nicht einzeilige Läufe')
|
||||
}
|
||||
if (name === 'echte Task-Antwort am Objektanfang über zwei Läufe zerrissen') {
|
||||
soll(qStartTaskGeteilt.map(l => l.ausgabe).join('\n') === Q_START_ALT, 'geteilte echte Task-Antwort weicht von Q_START_ALT ab')
|
||||
}
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await QR(Q_ARGS, { codexStart })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.stimmen.codex.abgegeben === false && e.stimmen.codex.job === null && e.stimmen.codex.grund === grund, 'Codex-Ergebnis ' + JSON.stringify(e && e.stimmen.codex))
|
||||
soll(mitLabel(p, 'codex:warten:').length === 0 && mitLabel(p, 'codex:abbruch').length === 0 && mitLabel(p, 'codex:ende').length === 0, 'Auftrag ohne sichere jobId verfolgt')
|
||||
soll(mitLabel(p, 'stimme:haiku').length === 1, 'Haiku nicht genau einmal eingesprungen')
|
||||
soll(p.agents.every(x => !String(x.prompt).includes('task-alt')), 'ein Auftrag nennt einen früheren Job')
|
||||
soll(e && e.hinweise.some(text => text.includes(grund)) && e.bericht_md.includes(grund), 'Warnung vor ungesehenem Auftrag fehlt in Hinweisen oder Bericht')
|
||||
return fehler
|
||||
})
|
||||
}
|
||||
|
||||
fall('quorum', 'Codex-Start: jobId nur aus der Ausgabe von task, nicht aus einem früheren Job im Status', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, protokoll: p } = await QR(Q_ARGS, { codexStart: Q_START_ALT })
|
||||
@@ -1555,6 +2255,50 @@ fall('quorum', 'Codex-Start: jobId nur aus der Ausgabe von task, nicht aus einem
|
||||
return fehler
|
||||
})
|
||||
|
||||
// Alle laeufe in ausgabe() (Folgeauftrag 2026-11-02): Paket, Codex-Start und Warten, je eine Ausführung, deren
|
||||
// Ausgabe der Hilfsagent auf zwei laeufe verteilt; die EXIT-Zeile steht jeweils im zweiten. Mit laeufe[0] allein
|
||||
// galt das Paket als nicht gebaut und ein gestarteter Codex-Auftrag als nicht gestartet: Haiku sprang ein, der
|
||||
// Auftrag lief ungesehen weiter.
|
||||
fall('quorum', 'Paket auf zwei laeufe verteilt, EXIT paket im zweiten: Paket gebaut, Stimmen laufen', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const paketAusgabe = [
|
||||
{ befehl: 'Paketbefehl, Ausgabe Teil 1', exit: 0, ausgabe: 'Paket in C:/tmp/q\nQUORUM_PAKET ' + JSON.stringify(qPaket()) },
|
||||
{ befehl: 'Paketbefehl, Ausgabe Teil 2', exit: 0, ausgabe: 'EXIT paket: 0' },
|
||||
]
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await QR(Q_ARGS, { paketAusgabe })
|
||||
soll(f === null, 'Fehler: ' + f)
|
||||
soll(e && e.status === 'fertig' && e.paket === 'C:/tmp/q', JSON.stringify(e && [e.status, e.paket]))
|
||||
soll(mitLabel(p, 'stimme:opus').length === 1 && mitLabel(p, 'stimme:sonnet').length === 1, 'Stimmen: ' + JSON.stringify(p.agents.map(x => x.label)))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('quorum', 'Codex-Start auf zwei laeufe verteilt, Status mit früherem Job: jobId aus task, Codex-Stimme gültig', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const codexStart = [
|
||||
{ befehl: 'Startbefehl, Ausgabe von status', exit: 0, ausgabe: '{\n "running": [],\n "latestFinished": {\n "id": "task-alt",\n "status": "completed",\n "request": {\n "jobId": "task-alt"\n }\n }\n}\nEXIT status: 0' },
|
||||
{ befehl: 'Startbefehl, Ausgabe von task', exit: 0, ausgabe: '{\n "jobId": "task-q1",\n "status": "queued"\n}\nEXIT task: 0' },
|
||||
]
|
||||
soll(codexStart.map(l => l.ausgabe).join('\n') === Q_START_ALT, 'Vorlage: die zwei laeufe sind nicht Q_START_ALT')
|
||||
const { ergebnis: e, protokoll: p } = await QR(Q_ARGS, { codexStart })
|
||||
soll(e && e.stimmen.codex.abgegeben === true && e.stimmen.codex.job === 'task-q1', 'Codex-Stimme ' + JSON.stringify(e && [e.stimmen.codex.job, e.stimmen.codex.grund]))
|
||||
soll(mitLabel(p, 'stimme:haiku').length === 0 && mitLabel(p, 'codex:abbruch').length === 0, 'Agenten: ' + JSON.stringify(p.agents.map(x => x.label)))
|
||||
const warten = mitLabel(p, 'codex:warten:1')[0]
|
||||
soll(warten && warten.prompt.includes("status 'task-q1' --wait"), 'Warten nicht auf task-q1')
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('quorum', 'Warten auf zwei laeufe verteilt, EXIT warten im zweiten: fertig, kein Abbruch, Codex-Stimme gültig', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const codexWarten = () => [
|
||||
{ befehl: 'Wartebefehl, Ausgabe Teil 1', exit: 0, ausgabe: '{\n "waitTimedOut": false\n}' },
|
||||
{ befehl: 'Wartebefehl, Ausgabe Teil 2', exit: 0, ausgabe: 'EXIT warten: 0' },
|
||||
]
|
||||
const { ergebnis: e, protokoll: p } = await QR(Q_ARGS, { codexWarten })
|
||||
soll(mitLabel(p, 'codex:warten:').length === 1 && mitLabel(p, 'codex:abbruch').length === 0 && mitLabel(p, 'codex:ende').length === 1, 'Agenten: ' + JSON.stringify(p.agents.map(x => x.label)))
|
||||
soll(e && e.stimmen.codex.abgegeben === true && mitLabel(p, 'stimme:haiku').length === 0, 'Codex-Stimme ' + JSON.stringify(e && [e.stimmen.codex.abgegeben, e.stimmen.codex.grund]))
|
||||
return fehler
|
||||
})
|
||||
|
||||
fall('quorum', 'gebremst vor den Stimmen: keine Stimme, kein Urteil, kein Bericht', async () => {
|
||||
const { fehler, soll } = pruefe()
|
||||
const { ergebnis: e, fehler: f, protokoll: p } = await QR(Q_ARGS, { ablesungen: [ablesung(90, 10)] })
|
||||
|
||||
Reference in new issue
Block a user