Files
claude-regelwerk/.claude/workflows/welle-mit-pruefung.js
T
Pascal MichelandClaude Opus 5 67d674cd0b Automatischer Push: 32 Datei(en) geändert
- .claude/agents/executer.md
- .claude/agents/planer.md
- .claude/agents/pruefer.md
- .claude/agents/recherche.md
- .claude/skills/plan-executer-pruefung/SKILL.md
- .claude/workflows/executer-welle.js
- .claude/workflows/plankritik.js
- .claude/workflows/pruefung.js
- .claude/workflows/welle-mit-pruefung.js
- .gitignore
- .vault-tools/regelwerk-check/ENTWURF.md
- .vault-tools/regelwerk-check/regelwerk_check.py
- … und 20 weitere

Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-24 04:00:02 +02:00

147 lines
8.7 KiB
JavaScript

export const meta = {
name: 'welle-mit-pruefung',
description: 'Schritt 3 und 4 in einem Aufruf: Kopie mit angewendetem Plan vor der Welle, executer-welle, Byte-Vergleich und Suite, danach pruefung mit den Rohbelegen',
whenToUse: 'Nach der Plankritik, sobald eine Welle mit disjunkten Zieldateien steht (AI/Regelwerk/codeworkflow.md, Schritte 3 und 4). Args: {projekt, planordner, tasks: [{datei, modell, effort?}], kopie? oder ohne: [relpfade, nicht leer], prosa?: [relpfade], suite?, kritisch?, abschluss?, effort?, version?, schwerpunkt?, rot_nachweis_erbracht?, zusatz?, werkzeuge?}. Ohne kopie wird sie vor der Welle gebaut; ohne nennt dann .git und Geheimnisdateien. Ersetzt die eigene Gegenprüfung des Koordinators nicht.',
phases: [
{ title: 'Kopie', detail: 'Hilfsagent: Stand vor der Welle kopieren und den Plan der Welle anwenden' },
{ title: 'Welle', detail: 'executer-welle' },
{ title: 'Vergleich', detail: 'Hilfsagent: welle_vergleich.py und Suite im echten Baum' },
{ title: 'Prüfung', detail: 'pruefung mit den Rohbelegen' },
],
}
// Ruft die gespeicherten Workflows executer-welle und pruefung verschachtelt auf, damit ihre
// Aufträge und Rollenparameter an genau einer Stelle stehen. Der Hilfsagent hat keinen
// agentType und bekommt seinen Effort deshalb mit (low). Opus läuft nur in den Kindern,
// und die laufen nacheinander; die Obergrenze von zwei hält executer-welle selbst.
const MODELLE = ['haiku', 'sonnet', 'opus']
const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max']
// Einfache Anführungszeichen: In Bash bleiben darin $, Backtick und Backslash wörtlich.
const q = s => "'" + String(s).replace(/'/g, "'\\''") + "'"
const PY = 'PYTHONIOENCODING=utf-8 python -X utf8'
const LAUF = {
type: 'object',
properties: {
laeufe: { type: 'array', items: { type: 'object', properties: { befehl: { type: 'string' }, exit: { type: 'integer' }, ausgabe: { type: 'string' } }, required: ['befehl', 'exit', 'ausgabe'] } },
},
required: ['laeufe'],
}
// Dieselben Regeln wie in executer-welle, aber VOR dem ersten Agenten: Ein ungültiger Task
// soll keine Projektkopie im Temp hinterlassen.
function pruefeArgs(a) {
if (!a || typeof a !== 'object') throw new Error('welle-mit-pruefung: args fehlen')
const fehlt = ['projekt', 'planordner', 'tasks'].filter(k => !a[k])
if (fehlt.length) throw new Error('welle-mit-pruefung: args fehlen: ' + fehlt.join(', '))
if (!Array.isArray(a.tasks) || a.tasks.length === 0) throw new Error('welle-mit-pruefung: tasks muss eine nicht leere Liste von {datei, modell, effort?} sein')
a.tasks.forEach((t, i) => {
if (!t || typeof t !== 'object' || !t.datei || typeof t.datei !== 'string') throw new Error(`welle-mit-pruefung: tasks[${i}] ohne datei`)
if (!MODELLE.includes(t.modell)) throw new Error(`welle-mit-pruefung: ${t.datei}: modell "${t.modell}" nicht in ${MODELLE.join('/')}`)
if (t.effort !== undefined && !EFFORTS.includes(t.effort)) throw new Error(`welle-mit-pruefung: ${t.datei}: effort "${t.effort}" nicht in ${EFFORTS.join('/')}`)
})
if (a.effort !== undefined && !EFFORTS.includes(a.effort)) throw new Error('welle-mit-pruefung: effort "' + a.effort + '" ungültig')
if (a.prosa !== undefined && !Array.isArray(a.prosa)) throw new Error('welle-mit-pruefung: prosa muss eine Liste sein')
if (!a.kopie) {
const ok = Array.isArray(a.ohne) && a.ohne.length > 0 && a.ohne.every(x => typeof x === 'string' && x)
if (!ok) throw new Error('welle-mit-pruefung: kopie oder ohne angeben. ohne ist eine nicht leere Liste von Ausschlüssen für kopie_bauen.py, als Pfade vom Projektwurzelordner, mindestens .git (falls vorhanden) und Geheimnisdateien')
}
}
function werkzeugAgent(label, phase, befehl, zweck) {
return agent(`Du führst einen Werkzeugbefehl aus und gibst seine Ausgabe roh zurück. Zweck: ${zweck}
Führe genau diesen Befehl einmal im Bash-Werkzeug (Git-Bash) aus, unverändert:
${befehl}
Ändere keine Datei außer denen, die der Befehl selbst anlegt. Deute nichts und fasse nichts zusammen. Gib Befehl, Exit-Code und die vollständige Ausgabe zurück; bei mehr als 200 Zeilen die ersten 60 und die letzten 120, mit einer Zeile "[... N Zeilen gekürzt ...]" dazwischen.`,
{ label, phase, schema: LAUF, model: 'sonnet', effort: 'low' })
}
// Anfang und Ende behalten: Das Ergebnis der Suite steht am Schluss.
function kopfUndEnde(text, kopf, ende) {
if (!text || text.length <= kopf + ende) return text || ''
return text.slice(0, kopf) + '\n[... gekürzt ...]\n' + text.slice(-ende)
}
pruefeArgs(args)
const W = args.werkzeuge || 'D:/Claude/.vault-tools/plan-kopie'
const taskdateien = args.tasks.map(t => t.datei)
let kopie = args.kopie || null
let temp = null
const belege = {}
const fehler = []
phase('Kopie')
if (!kopie) {
const befehl = `T=$(mktemp -d) && echo "KOPIE=$(cygpath -m "$T/mat")" && ${PY} ${q(W + '/kopie_bauen.py')} ${q(args.projekt)} "$T/mat" --ohne ${args.ohne.map(q).join(' ')} && ${PY} ${q(W + '/plan_materialisiere.py')} ${q(args.planordner)} "$T/mat" ${taskdateien.map(q).join(' ')}`
const r = await werkzeugAgent('werkzeug:kopie', 'Kopie', befehl, 'Stand vor der Welle kopieren und die Taskdateien der Welle darauf anwenden')
belege.kopie = r
const lauf = r && r.laeufe && r.laeufe[0]
const m = lauf && /^KOPIE=(.+?)\s*$/m.exec(lauf.ausgabe)
if (m) temp = m[1].replace(/\/mat$/, '')
const angewendet = lauf && /'(neu|ersetzt|angehaengt)': [1-9]/.test(lauf.ausgabe)
if (lauf && lauf.exit === 0 && m && angewendet) kopie = m[1]
else log('Kopie nicht verwendbar (' + (!lauf ? 'Hilfsagent ohne Ergebnis' : lauf.exit !== 0 ? 'Exit ' + lauf.exit : !m ? 'kein KOPIE-Pfad' : 'kein Block angewendet') + '); der Byte-Vergleich entfällt, die Welle läuft trotzdem')
}
phase('Welle')
let welle = null
try {
welle = await workflow('executer-welle', {
projekt: args.projekt, planordner: args.planordner, tasks: args.tasks,
rot_nachweis_erbracht: args.rot_nachweis_erbracht, zusatz: args.zusatz,
})
} catch (e) {
fehler.push('executer-welle: ' + (e && e.message))
log('executer-welle gescheitert: ' + (e && e.message))
}
phase('Vergleich')
if (welle) {
const teile = []
if (kopie) teile.push(`${PY} ${q(W + '/welle_vergleich.py')} --projekt ${q(args.projekt)} --mat ${q(kopie)}${args.prosa && args.prosa.length ? ' --prosa ' + args.prosa.map(q).join(' ') : ''} -- ${taskdateien.map(d => q(args.planordner + '/' + d)).join(' ')}; echo "EXIT welle_vergleich: $?"`)
if (args.suite) teile.push(`if cd ${q(args.projekt)}; then export COLUMNS=300; ${args.suite}; echo "EXIT suite: $?"; else echo "EXIT suite: cd ins Projekt gescheitert"; fi`)
if (teile.length) {
belege.vergleich = await werkzeugAgent('werkzeug:vergleich', 'Vergleich', teile.join('; '), 'Nach der Welle: geänderte Dateien und Bytes gegen die Kopie, dann die Suite im echten Baum')
if (!belege.vergleich) log('Vergleich: Hilfsagent ohne Ergebnis')
} else log('Vergleich entfällt: weder Kopie noch suite')
} else log('Vergleich entfällt: keine Welle')
phase('Prüfung')
let pruefung = null
let pruefungGrund = null
if (!welle) pruefungGrund = 'keine Welle'
else if (welle.ohne_bericht && welle.ohne_bericht.length) pruefungGrund = 'Welle unvollständig, ohne Bericht: ' + welle.ohne_bericht.join(', ')
if (pruefungGrund) log('Prüfung übersprungen: ' + pruefungGrund)
else {
const vl = belege.vergleich && belege.vergleich.laeufe && belege.vergleich.laeufe[0]
const vergleichstext = vl ? kopfUndEnde(vl.ausgabe, 1500, 2500) : ''
const schwerpunkt = [
args.schwerpunkt || '',
welle.mit_abweichung.length ? `Diese Tasks melden Abweichungen vom Plan: ${welle.mit_abweichung.join(', ')}.` : '',
vergleichstext ? `Rohausgabe von Byte-Vergleich und Suite nach der Welle (vom Hilfsagenten, nicht von dir gefahren):\n${vergleichstext}` : '',
].filter(Boolean).join('\n')
try {
pruefung = await workflow('pruefung', {
projekt: args.projekt, plan: args.planordner, tasks: taskdateien, suite: args.suite,
kritisch: args.kritisch, abschluss: args.abschluss, version: args.version, effort: args.effort, schwerpunkt,
})
} catch (e) {
fehler.push('pruefung: ' + (e && e.message))
pruefungGrund = 'pruefung gescheitert: ' + (e && e.message)
log(pruefungGrund)
}
}
return {
kopie,
temp_ordner: temp ? `${temp} bleibt liegen (Projektkopie ohne die Ausschlüsse); löschen, wenn der Diff nicht mehr gebraucht wird` : null,
welle,
vergleich: belege.vergleich || null,
kopie_lauf: belege.kopie || null,
pruefung: pruefung || { uebersprungen: pruefungGrund },
fehler,
naechster_schritt: 'Eigene Gegenprüfung nach Schritt 4: Testlauf selbst wiederholen, zentrale Artefakte selbst lesen, eigener Lauf mit echten Daten; bei Abweichung im Byte-Vergleich diff auf die ganze Datei. Befunde am Plan korrigieren, nicht am Code.',
}