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:
Pascal MichelandClaude Opus 5 committed 2026-11-03 03:00:01 +01:00
1 parent 7f484f9be5
commit 22761eb186
11 files changed
+1520 -65

No files matched your search

+76 -18
View File
@@ -26,6 +26,9 @@ export const meta = {
// und der Woche.
// - Rückgabe mit status fertig oder gebremst; ein gebremster Lauf wird neu gestartet, nicht
// fortgesetzt.
// - werkzeug_befunde (seit 2026-11-02) listet die Befundzeilen der Planwerkzeuge und die von
// plan_materialisiere.py aus materialisieren, je mit dem Werkzeug. Die Plankritik hält dabei
// nicht an; die Linsen laufen wie sonst.
//
// Rollenparameter: model wird immer gesetzt (Stamm). Der Effort der pruefer kommt aus dem
// Frontmatter von .claude/agents/pruefer.md; args.effort überschreibt ihn nur bewusst.
@@ -45,6 +48,36 @@ const VERBRAUCH = 'D:/Claude/.claude/workflows/verbrauch.js'
const RANG = { blockierend: 0, wichtig: 1, klein: 2 }
// Einfache Anführungszeichen: In Bash bleiben darin $, Backtick und Backslash wörtlich.
const q = s => "'" + String(s).replace(/'/g, "'\\''") + "'"
// Befundzeile der Planwerkzeuge: "BEFUND <datei>:<zeile>:", bei plan_zeichen "<datei>:<zeile>:<spalte>:".
// kopie_bauen.py schreibt "BEFUND Projekt ..." ohne Zeilennummer; die Form trennt beide.
const BEFUNDZEILE = /^BEFUND [^\s:]+:\d+:/
const befundZeilen = text => String(text || '').split('\n').map(z => z.replace(/\r$/, '')).filter(z => BEFUNDZEILE.test(z))
// 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, und laeufe[0] allein
// enthielt plan_bloecke nicht. Jede Ausgabe eines Hilfsagenten wird deshalb über alle laeufe gelesen,
// in ihrer Reihenfolge und mit Zeilenumbruch verbunden: Befund- und EXIT-Zeilen, KOPIE-Zeile und
// angewendete Blöcke, Mutantenlauf.
const ausgabeAller = r => (r && Array.isArray(r.laeufe) ? r.laeufe : []).map(l => String((l && l.ausgabe) || '')).join('\n')
// Exit über alle laeufe, für die Kopie aus materialisieren: Ihr Befehl ist eine Kette mit &&, sie
// endet nur mit 0, wenn jedes Glied mit 0 endet. Verwendbar ist die Kopie deshalb nur, wenn jeder Lauf
// Exit 0 hat; ein Lauf ohne Exit zählt nicht als 0 und steht im Log als undefined. Planwerkzeuge und
// Mutantenlauf lesen stattdessen die EXIT-Zeilen, die ihr Befehl selbst schreibt.
const exitsAller = r => (r && Array.isArray(r.laeufe) ? r.laeufe : []).map(l => l && l.exit)
const alleExitNull = r => exitsAller(r).length > 0 && exitsAller(r).every(x => x === 0)
// Stand eines Planwerkzeugs aus seiner EXIT-Zeile und dem Teil der Ausgabe, der zu ihm gehört.
// Gelaufen ist es mit Exit 0, mit Exit 2 und mindestens einer Befundzeile, plan_bloecke auch mit
// Exit 3 und "Nichts geprueft:" (kein Aus-/wird-Block). Exit 2 ohne Befundzeile ist ein
// gescheitertes Werkzeug: python endet auch bei "can't open file" mit 2 (2026-09-24 ging so ein
// Errno 2 als Rohausgabe an die Linse).
function werkzeugStand(aus, name, abschnitt) {
const m = new RegExp('EXIT ' + name + ': (\\d+)').exec(aus)
const befunde = befundZeilen(abschnitt)
if (!m) return { name, gelaufen: false, grund: 'ohne EXIT-Zeile', befunde }
const exit = Number(m[1])
const gelaufen = exit === 0 || (exit === 2 && befunde.length > 0) || (name === 'plan_bloecke' && exit === 3 && /^Nichts geprueft:/m.test(abschnitt))
return { name, gelaufen, grund: 'Exit ' + exit + (exit === 2 && !befunde.length ? ' ohne Befundzeile' : ''), befunde }
}
// Text für die Linse arithmetik, wenn der Mutantenlauf (werkzeug:mutanten) etwas geliefert hat.
// Seit der Nachbesserung 2026-09-28 (N5, N7): modus: muster in der Rohausgabe heißt, dass kein
@@ -231,19 +264,29 @@ Gilt der Befund nur unter einer Bedingung, nenn sie. Gib deine eigene Einschätz
pruefeArgs(args)
const W = args.werkzeuge || 'D:/Claude/.vault-tools/plan-kopie'
const PY = 'PYTHONIOENCODING=utf-8 python -X utf8'
const ctx = { kopie: args.kopie || null, werkzeuge: null, temp: null, mutantenLauf: null, mutantenStatus: null }
const ctx = { kopie: args.kopie || null, werkzeuge: null, temp: null, mutantenLauf: null, mutantenStatus: null, befunde: [] }
const MUTANTEN_MODUS = args.mutanten || 'voll'
const vorbereitung = []
// Eine Kopie gilt nur als verwendbar, wenn der Lauf sauber endet, den Pfad bis zum
// Zeilenende nennt (Pfade mit Leerzeichen) und das Planwerkzeug mindestens einen Block
// angewendet hat. Sonst rechnet Linse 1 am Text, und das steht im Log.
// Eine Kopie gilt nur als verwendbar, wenn jeder Lauf sauber endet (alleExitNull), die Ausgabe
// den Pfad bis zum Zeilenende nennt (Pfade mit Leerzeichen) und das Planwerkzeug mindestens einen
// Block angewendet hat. Sonst rechnet Linse 1 am Text, und das steht im Log. Meldet
// plan_materialisiere.py einen Befund, ist das kein gescheitertes Werkzeug: Die Zeilen gehen
// nach werkzeug_befunde, und der Temp-Ordner wird genannt.
function kopieAusLauf(r, wozu) {
const lauf = r && r.laeufe && r.laeufe[0]
if (!lauf) { log(`${wozu}: Hilfsagent ohne Ergebnis, keine Kopie`); return null }
const m = /^KOPIE=(.+?)\s*$/m.exec(lauf.ausgabe)
const angewendet = /'(neu|ersetzt|angehaengt)': [1-9]/.test(lauf.ausgabe)
if (lauf.exit !== 0 || !m) { log(`${wozu}: Kopie nicht verwendbar (Exit ${lauf.exit}${m ? '' : ', kein KOPIE-Pfad'})`); return null }
const exits = exitsAller(r)
if (!exits.length) { log(`${wozu}: Hilfsagent ohne Ergebnis, keine Kopie`); return null }
const aus = ausgabeAller(r)
const m = /^KOPIE=(.+?)\s*$/m.exec(aus)
const angewendet = /'(neu|ersetzt|angehaengt)': [1-9]/.test(aus)
const befunde = befundZeilen(aus)
if (befunde.length) {
befunde.forEach(befund => ctx.befunde.push({ werkzeug: 'plan_materialisiere', befund }))
if (m) ctx.temp = m[1].replace(/\/mat$/, '')
log(`${wozu}: plan_materialisiere.py meldet ${befunde.length} Befund(e) (Exit ${exits.map(String).join('/')}), keine Kopie; die Zeilen stehen unter werkzeug_befunde`)
return null
}
if (!alleExitNull(r) || !m) { log(`${wozu}: Kopie nicht verwendbar (Exit ${exits.map(String).join('/')}${m ? '' : ', kein KOPIE-Pfad'})`); return null }
ctx.temp = m[1].replace(/\/mat$/, '')
if (!angewendet) { log(`${wozu}: das Planwerkzeug hat keinen Block angewendet; die Kopie ist der unveränderte Stand und wird nicht verwendet`); return null }
return m[1]
@@ -268,13 +311,22 @@ if (!ctx.kopie && args.materialisieren) {
{
const befehl = `${PY} ${q(W + '/plan_zeichen.py')} ${q(args.planordner)}; echo "EXIT plan_zeichen: $?"; ls ${q(args.planordner)}/task-*.md >/dev/null 2>&1 && { ${PY} ${q(W + '/plan_bloecke.py')} vor ${q(args.planordner)}/task-*.md --projekt ${q(args.projekt)}; echo "EXIT plan_bloecke: $?"; } || echo "plan_bloecke: keine task-*.md im Planordner, nicht gefahren"`
const r = await werkzeugAgent('werkzeug:planwerkzeuge', 'Vorbereitung', befehl, 'Codeblöcke auf unsichtbare Zeichen und Aus-Blöcke gegen den Baum prüfen')
// Nur wenn beide Werkzeuge mit 0 endeten, gilt die Ausgabe als gelaufen (2026-09-24: ein Protokollsatz statt
// Pfad in args.werkzeuge endete mit Errno 2 und ging trotzdem als Rohausgabe an die Linse). Der Exit des
// ganzen Befehls hilft dabei nicht, er ist der des letzten echo; gelesen werden die EXIT-Zeilen.
const aus = r && r.laeufe && r.laeufe[0] ? String(r.laeufe[0].ausgabe || '') : ''
const gelaufen = /EXIT plan_zeichen: 0(?!\d)/.test(aus) && (/EXIT plan_bloecke: 0(?!\d)/.test(aus) || /plan_bloecke: keine task-/.test(aus))
if (gelaufen) ctx.werkzeuge = aus
else log('Planwerkzeuge: Hilfsagent ohne Ergebnis; Linse bezeichner fährt sie selbst')
// Die Ausgabe geht an die Linse, wenn beide Werkzeuge gelaufen sind, auch mit Befund (werkzeugStand oben).
// Der Exit des ganzen Befehls hilft dabei nicht, er ist der des letzten echo; gelesen werden die
// EXIT-Zeilen. Was vor "EXIT plan_zeichen:" steht, gehört zu plan_zeichen, der Rest zu plan_bloecke.
const lauf = r && r.laeufe && r.laeufe[0]
if (!lauf) log('Planwerkzeuge: Hilfsagent ohne Ergebnis; Linse bezeichner fährt sie selbst')
else {
const aus = ausgabeAller(r)
const grenze = aus.indexOf('EXIT plan_zeichen:')
const zeichen = werkzeugStand(aus, 'plan_zeichen', grenze >= 0 ? aus.slice(0, grenze) : aus)
const bloecke = /plan_bloecke: keine task-/.test(aus)
? { name: 'plan_bloecke', gelaufen: true, befunde: [] }
: werkzeugStand(aus, 'plan_bloecke', grenze >= 0 ? aus.slice(grenze) : '')
for (const w of [zeichen, bloecke]) w.befunde.forEach(befund => ctx.befunde.push({ werkzeug: w.name, befund }))
if (zeichen.gelaufen && bloecke.gelaufen) ctx.werkzeuge = aus
else log('Planwerkzeuge: ' + [zeichen, bloecke].filter(w => !w.gelaufen).map(w => w.name + ' ' + w.grund).join(', ') + '; Linse bezeichner fährt sie selbst')
}
vorbereitung.push(r)
}
// Mutantenlauf (seit 2026-09-27). mutanten_lauf.py ändert Dateien in der Wurzel, die es bekommt,
@@ -298,9 +350,13 @@ else if (!ctx.kopie) ctx.mutantenStatus = 'ohne_kopie'
if (ctx.kopie && MUTANTEN_MODUS !== 'aus') {
const befehl = `MSYS_NO_PATHCONV=1 wsl -d Debian -- bash /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf_kopie.sh "$(cygpath -m -l ${q(ctx.kopie)})" "$(cygpath -m -l ${q(args.planordner)})" ${MUTANTEN_MODUS === 'muster' ? 'muster' : 'voll'}`
const r = await werkzeugAgent('werkzeug:mutanten', 'Vorbereitung', befehl, 'Mutanten aus mutanten.toml auf einer Kopie der Kopie fahren', 'Setze im Bash-Werkzeug den Parameter timeout auf 600000; der Befehl darf bis zu zehn Minuten laufen.')
const aus = r && r.laeufe && r.laeufe[0] ? String(r.laeufe[0].ausgabe || '') : ''
// Alle laeufe, etwa Anfang und Ende einer gekürzten Ausgabe: Die EXIT-Zeile steht am Schluss
// (mutanten_lauf_kopie.sh schreibt höchstens eine); bei mehreren gilt wie bisher die erste.
// KEINE_MUTANTEN_TOML zählt nur als ganze Zeile, wie das Skript sie schreibt: Ein zusätzlicher Lauf,
// der etwa den Skripttext zeigt, machte sonst aus einem gefahrenen Lauf keine_toml.
const aus = ausgabeAller(r)
const exitMatch = /EXIT mutanten_lauf: (\d+)/.exec(aus)
if (/KEINE_MUTANTEN_TOML/.test(aus)) { ctx.mutantenLauf = { keineToml: true, rohausgabe: aus }; ctx.mutantenStatus = 'keine_toml' }
if (/^KEINE_MUTANTEN_TOML\s*$/m.test(aus)) { ctx.mutantenLauf = { keineToml: true, rohausgabe: aus }; ctx.mutantenStatus = 'keine_toml' }
else if (exitMatch) { ctx.mutantenLauf = { keineToml: false, rohausgabe: aus }; ctx.mutantenStatus = 'exit_' + exitMatch[1] }
else { log('Mutantenlauf: Hilfsagent ohne verwertbares Ergebnis (kein KEINE_MUTANTEN_TOML, kein EXIT mutanten_lauf); Linsen laufen ohne Mutantenlauf weiter'); ctx.mutantenStatus = 'ohne_ergebnis' }
vorbereitung.push(r)
@@ -324,6 +380,7 @@ if (vorLinsen.gebremst) {
selbst_ausgefuehrt: [],
nicht_pruefbar: [],
vorbereitung,
werkzeug_befunde: ctx.befunde,
ohne_ergebnis: gewaehlt,
verbrauch: ablesungen,
plaetze: { linsen: 0, widerlegung: 0 },
@@ -388,6 +445,7 @@ return {
selbst_ausgefuehrt: ergebnisse.map(e => ({ linse: e.linse, befehle: e.ergebnis.selbst_ausgefuehrt })),
nicht_pruefbar: ergebnisse.flatMap(e => e.ergebnis.nicht_pruefbar.map(x => e.linse + ': ' + x)),
vorbereitung,
werkzeug_befunde: ctx.befunde,
ohne_ergebnis: ohneErgebnis,
verbrauch: ablesungen,
plaetze: { linsen: vorLinsen.plaetze, widerlegung: plaetzeWiderlegung },