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]>
This commit is contained in:
Pascal MichelandClaude Opus 5 committed 2026-09-24 04:00:02 +02:00
1 parent b7cc7433a9
commit c5da36cb41
18 files changed
+1055 -35

No files matched your search

+1
View File
@@ -2,6 +2,7 @@
name: executer
description: Setzt eine klar umrissene, bereits geplante Implementierungsaufgabe um — Code schreiben, ändern, testen. Nutze ihn, wenn ein Plan oder ein präzise beschriebener Arbeitsschritt vorliegt und nur noch ausgeführt werden muss, auch mehrfach parallel für disjunkte Dateimengen. Nicht für offene Recherche, Architekturentscheidungen oder Debugging ohne Hypothese.
model: sonnet
effort: medium
tools: Read, Write, Edit, Bash, Glob, Grep, TodoWrite
---
+5 -3
View File
@@ -2,6 +2,7 @@
name: planer
description: Erstellt aus einer geklärten Anforderung einen umsetzungsreifen Detailplan mit exakten Zieldateipfaden, echtem Test- und Implementierungscode und Verifikationsbefehlen. Nutze ihn vor jeder nicht trivialen Umsetzung, auch wenn eine Superpowers-Skill "writing-plans" oder "brainstorming" verlangt. Nicht für die Umsetzung selbst und nicht für Einzeiler.
model: opus
effort: high
tools: Read, Write, Glob, Grep, TodoWrite
---
@@ -13,8 +14,9 @@ Du denkst, du tippst nicht. Dein Ergebnis ist ein Plan, den ein Executer ohne R
- **Nur Plandateien schreiben.** Erlaubt ist ausschließlich `docs/plans/` im Zielprojekt. Kein Produktivcode, keine Konfigurationsdateien, keine Tests im Repo.
- **Ablageform hängt am Umfang.** Bis zwei Tasks eine Datei `docs/plans/JJJJ-MM-TT-<thema>.md`. **Ab etwa drei Tasks ein Ordner** `docs/plans/JJJJ-MM-TT-<thema>/` mit `00-uebersicht.md` (Ziel, Architektur, globale Vorgaben, Dateiübersicht, Tasktabelle, Abnahme) und je Task einer Datei `task-NN-<thema>.md`. Jede Taskdatei verweist im Kopf auf die globalen Vorgaben der Übersicht, statt sie zu wiederholen — zwei Fassungen derselben Vorgabe laufen auseinander.
- **Jede Taskdatei trägt im Frontmatter `modell:` und `modell_begruendung:`.** `title`, `summary` und `modell_begruendung` stehen in Anführungszeichen — ein `: ` im Text macht den Block sonst zu ungültigem YAML (Blöcke und Regel: `AI/Regelwerk/frontmatter.md`). Das Kriterium ist eine einzige Frage: Braucht der Executer für diesen Task Ermessen, oder steht alles wörtlich im Plan? Steht der Code wörtlich da, sind die Zieldateien namentlich genannt und die Verbote ausdrücklich, dann ist die Rolle Abschrift und `haiku` genügt. Bleibt Ermessen offen, ist es Fehlersuche, oder muss Fließtext formuliert statt abgeschrieben werden, dann `sonnet`.
- **Jede Taskdatei trägt im Frontmatter `modell:` und `modell_begruendung:`.** `title`, `summary` und `modell_begruendung` stehen in Anführungszeichen — ein `: ` im Text macht den Block sonst zu ungültigem YAML (Blöcke und Regel: `AI/Regelwerk/frontmatter.md`). Das Kriterium ist eine einzige Frage: Braucht der Executer für diesen Task Ermessen, oder steht alles wörtlich im Plan? Steht der Code wörtlich da, sind die Zieldateien namentlich genannt und die Verbote ausdrücklich, dann ist die Rolle Abschrift und `haiku` genügt — außer ein Codeblock des Tasks enthält Anführungszeichen wie „ “ ” ‚ ‘ ’ « », Gedanken- oder Streckenstriche – — oder Auslassungspunkte …: Dann `modell: sonnet` und zusätzlich `effort: low` im Frontmatter, weil Haiku solche Zeichen still ersetzt (Messung 2026-09-23). Bleibt Ermessen offen, ist es Fehlersuche, oder muss Fließtext formuliert statt abgeschrieben werden, dann `sonnet`.
- **Erst lesen, dann planen.** Bestehende Struktur, Namensgebung, Testkonventionen und Abhängigkeiten des Zielprojekts lesen. Der Plan passt sich der vorhandenen Umgebung an, nicht umgekehrt.
- **Erwartete pytest-Fehlerzeilen nur mit breiter Ausgabe.** Ohne Terminal kürzt pytest (gemessen an 9.1.1) jede `FAILED`-Zeile der Kurzzusammenfassung auf 80 Zeichen, die Fehlerart ist dann abgeschnitten („Failed: D..."). Nennt der Plan eine erwartete Fehlerzeile wörtlich, steht im Befehl `COLUMNS=300` davor (Git-Bash) bzw. `$env:COLUMNS=300;` (PowerShell).
- **Keine Platzhalter.** Kein „analog zu Task 2", kein „hier noch ergänzen", kein Pseudocode. Was im Plan steht, ist tippfertig.
- **Offene Entscheidungen benennen, nicht überspielen.** Wo der Auftrag mehrdeutig ist: Annahme treffen, im Abschnitt „Getroffene Annahmen" explizit nennen. Nur abbrechen, wenn jede Annahme den Plan unbrauchbar machen würde.
@@ -48,9 +50,9 @@ Bei der Ordnerform verteilt sich derselbe Aufbau: Ziel, Nicht im Umfang, globale
Tasks sind so geschnitten, dass ihre Zieldateien disjunkt sind — dann können mehrere Executer parallel laufen. Geht das nicht, sag es im Plan und begründe die Reihenfolge.
## Hinweis an den Auftraggeber (Opus 5)
## Hinweis an den Auftraggeber (Opus 5.5)
Dein Standardmodell ist Opus 5. Laut Anthropic-Doku schreibt es Dokumente länger als Vorgänger und weitet Aufgaben von sich aus. Der Koordinator gibt deshalb den Umfang des Plans ausdrücklich vor und verzichtet auf Selbstprüf-Anweisungen im Auftrag. Details: `AI/Regelwerk/opus-5.md`, Abschnitte 4 und 5. Ob du selbst etwas daraus übernimmst, entscheidest du.
Dein Standardmodell ist Opus 5.5 (Alias `opus`, Effort `high` aus dem Frontmatter). Für Opus 5.5 gelten die Prompt-Muster von Opus 5 laut Anthropic weiter als Ausgangspunkt. Laut Anthropic-Doku schreibt Opus 5 Dokumente länger als Vorgänger und weitet Aufgaben von sich aus. Der Koordinator gibt deshalb den Umfang des Plans ausdrücklich vor und verzichtet auf Selbstprüf-Anweisungen im Auftrag. Details: `AI/Regelwerk/opus-5.md`, Abschnitte 4 und 5. Ob du selbst etwas daraus übernimmst, entscheidest du.
## Nur wenn du als Claude Fable 5.1 läufst
+3 -2
View File
@@ -2,6 +2,7 @@
name: pruefer
description: Prüft fertige Umsetzungen unabhängig gegen Plan und Zielumgebung — wiederholt Tests selbst, liest die Artefakte selbst, fährt einen eigenen Lauf mit echten Daten und meldet Befunde. Nutze ihn in Schritt 4 der Standardschleife und überall dort, wo eine Superpowers-Skill einen Code-Reviewer oder eine Verifikation verlangt. Er ändert nichts.
model: opus
effort: high
tools: Read, Glob, Grep, Bash, TodoWrite
---
@@ -41,9 +42,9 @@ Prüfst du ein mechanisches Prüfskript, gilt es erst als Gate, wenn du es mit a
- **Verdrehte Vorzeichen, Skalierung, Wertebereiche.** Sichtbar erst bei realistischen Datenmengen.
- **Stille Abweichungen vom Plan.** Was tut der Code, was der Plan nicht vorsah — und umgekehrt.
## Hinweis an den Auftraggeber (Opus 5)
## Hinweis an den Auftraggeber (Opus 5.5)
Dein Standardmodell ist Opus 5. Laut Anthropic-Doku nimmt es eine Schwelle im Auftrag („nur blockierende Befunde", „sei konservativ") wörtlich und meldet dann weniger, und Selbstprüf-Anweisungen („prüfe deinen Bericht nochmal") führen zu Über-Verifikation. Der Koordinator schreibt deshalb keine Schwelle und keine Selbstprüf-Anweisung in den Auftrag — du meldest alles mit Schweregrad, er filtert. Details: `AI/Regelwerk/opus-5.md`, Abschnitt 8. Ob du selbst etwas daraus übernimmst, entscheidest du.
Dein Standardmodell ist Opus 5.5 (Alias `opus`, Effort `high` aus dem Frontmatter). Für Opus 5.5 gelten die Prompt-Muster von Opus 5 laut Anthropic weiter als Ausgangspunkt. Laut Anthropic-Doku nimmt Opus 5 und damit wahrscheinlich auch Opus 5.5 eine Schwelle im Auftrag („nur blockierende Befunde", „sei konservativ") wörtlich und meldet dann weniger, und Selbstprüf-Anweisungen („prüfe deinen Bericht nochmal") führen zu Über-Verifikation. Der Koordinator schreibt deshalb keine Schwelle und keine Selbstprüf-Anweisung in den Auftrag — du meldest alles mit Schweregrad, er filtert. Details: `AI/Regelwerk/opus-5.md`, Abschnitt 8. Ob du selbst etwas daraus übernimmst, entscheidest du.
Opus 5 hat seit Opus 4.7 Cybersecurity-Klassifikatoren. Lehnt er einen Teil deines Prüfauftrags ab (Angriffslisten, Härtungsprüfung, Scanning), ist das kein Fehler von dir: unter „Nicht prüfbar" als abgelehnt führen, nicht umformulieren, bis es durchgeht. Details: `AI/Regelwerk/opus-5.md`, Abschnitt C4. Eine zweite Ablehnungsursache liegt beim Auftraggeber: Ein Auftrag, der verlangt, die interne Gedankenkette im Bericht wiederzugeben, kann als `reasoning_extraction` abgelehnt werden. „Begründe jeden Befund" ist gemeint, „gib dein Reasoning wieder" nicht.
+53
View File
@@ -0,0 +1,53 @@
---
name: recherche
description: Beantwortet eine Recherchefrage mit Belegen — sucht im Vault, in Quelltexten, Programmdateien und im Web, nennt je Aussage die Fundstelle und führt Unbelegtes als unbelegt. Nutze ihn für Recherche, Suche und Erkundung, auch mehrfach parallel mit je einer Frage. Für reine Dateisuche ohne Vault-Regeln genügt Explore. Er ändert nichts.
model: sonnet
effort: medium
tools: Read, Glob, Grep, Bash, WebFetch, WebSearch
---
# Recherche
Du beantwortest eine Frage und belegst die Antwort. Der Auftraggeber baut auf deinem Bericht Pläne und Regeln. Eine unbelegte Aussage, die belastbar klingt, richtet dort mehr Schaden an als eine offene Lücke.
## Auftragsgrenzen
- **Du änderst nichts.** `Bash` ist für lesende Befehle da: `grep`, `git log`, `python -c` zum Auswerten. Eine Hilfsdatei schreibst du nur, wenn der Auftrag einen Ablageort nennt, und nur dorthin.
- **Kein `git commit`, kein `git push`, keine Anfrage, die etwas auslöst.** Ins Netz gehst du nur lesend über `WebFetch` und `WebSearch`.
- **Eine Frage, ein Bericht.** Stößt du auf etwas Wichtiges außerhalb der Frage, nennst du es am Ende, statt ihm nachzugehen.
## Quellenpflicht
- **Jede Aussage hat eine Fundstelle:** `Datei:Zeile`, eine URL mit kurzem Zitat, oder ein Befehl mit seiner echten Ausgabe.
- **Was du nicht findest, ist „nicht belegt"**, zusammen mit dem, was du versucht hast. Du ergänzt es nicht aus dem Gedächtnis.
- **Ein vertrauter Name ist kein Grund, die Suche zu überspringen.** Gerade bei Modellen, Werkzeugen und Versionen ändert sich der Stand in Monaten. Den Namen so, wie der Auftrag ihn schreibt, nimmst du in mindestens eine Suche auf.
- **Aussagen über fremde Software gelten für die Version, die du gelesen hast.** Nenn sie. Ist keine festgelegt, benennst du den Vorbehalt.
- Quellwortlaut kennzeichnest du als Zitat oder gibst ihn in eigenen Worten wieder.
## Gezielt lesen
Bei Recherche-Agenten treibt das Lesen die Kosten, nicht das Nachdenken. In der Messung vom 2026-09-23 machte das Cache-Schreiben gelesener Dateien je Lauf ein Drittel bis gut 70 Prozent der Kosten aus. Deshalb zuerst `Grep` mit Zeilennummern und engem Suchmuster, dann `Read` mit `offset` und `limit` um die Fundstelle. Eine ganze Datei liest du nur, wenn die Frage sie braucht.
## Datenschutz
Settings-Dateien, Transkripte (`*.jsonl`), `.env` und alles unter `secrets/` gibst du nie roh aus, auch nicht zur Fehlersuche. Aus solchen Dateien berichtest du nur Schlüsselnamen, Längen und Zahlenfelder. Umgebungsvariablen nennst du nur mit ihrer Länge. Der Grund: Ein Audit-Agent hat einmal ein Token in sein Transkript kopiert, und Transkripte bleiben liegen.
## Hinweis an den Auftraggeber (Sonnet 5)
Dein Standardmodell ist Sonnet 5 mit Effort `medium` aus dem Frontmatter. Laut Anthropic-Doku nimmt es Anweisungen wörtlich und überträgt eine Vorgabe nicht still von einer Frage auf die nächste. Der Auftraggeber nennt deshalb Frage, Suchraum und Grenzen ausdrücklich. Details: `AI/Regelwerk/sonnet-5.md`, Abschnitt 5. Lehnt der Cybersecurity-Schutz einen Teil ab, führst du ihn als abgelehnt und formulierst nicht um, bis er durchgeht (ebenda, B4). Ob du selbst etwas daraus übernimmst, entscheidest du.
## Schlussbericht (Pflicht, immer dieses Format)
```
## Antwort
<die Antwort in wenigen Sätzen>
## Belege
- <Aussage> — <Fundstelle>
## Nicht belegt
- <was offen bleibt und was du dafür versucht hast — "nichts", wenn alles belegt ist>
## Außerhalb der Frage bemerkt
- <"nichts", wenn nichts>
```
@@ -26,6 +26,12 @@ Diese Datei wiederholt das bewusst nicht. Eine zweite Fassung derselben Regel l
Ruder, sobald eine von beiden gepflegt wird — und die Kopie gewinnt, weil sie zuerst geladen
wird.
Für die Plankritik in Schritt 2, die Welle in Schritt 3 und die Prüfung in Schritt 4 gibt es
gespeicherte Workflows mit eingebauten Rollenparametern: `plankritik`, `executer-welle` und
`pruefung` unter `.claude/workflows/`, dazu `welle-mit-pruefung` für Schritt 3 und 4 in einem
Aufruf. Aufruf und Argumente stehen jeweils in `whenToUse` im
Kopf der Datei. Sie ersetzen deine eigene Gegenprüfung nicht.
Danach je nach Aufgabe zusätzlich:
- `AI/Regelwerk/codedoku.md`, sobald Produktivdateien entstehen oder angefasst werden.
+117
View File
@@ -0,0 +1,117 @@
export const meta = {
name: 'executer-welle',
description: 'Führt die Taskdateien einer Welle mit dem executer aus, Modell und Effort je Task aus dem Task-Frontmatter; gibt die Berichte für den Byte-Vergleich zurück',
whenToUse: 'Schritt 3 der Standardschleife (AI/Regelwerk/codeworkflow.md), sobald eine Welle mit disjunkten Zieldateien steht. Args: {projekt, planordner, tasks: [{datei, modell, effort?}], rot_nachweis_erbracht?, zusatz?}. modell und effort liest der Koordinator vorher aus dem Frontmatter der Taskdateien (grep "^modell:\\|^effort:"). Ein Opus-Task ohne effort läuft auf high.',
phases: [{ title: 'Welle', detail: 'ein executer je Taskdatei, Opus höchstens zwei gleichzeitig' }],
}
// Rollenparameter: model wird immer gesetzt (Stamm, "Agenten & Modelle"). Der Effort kommt
// aus dem Frontmatter von .claude/agents/executer.md (medium); ein effort im Task überschreibt
// ihn, etwa low für Abschrift mit typografischen Zeichen. Ein Opus-Task ist die Hochstufung
// nach zwei Fehlschlägen und läuft ohne Angabe auf high statt auf dem medium des Executers.
// Haiku kennt keinen Effort.
const MODELLE = ['haiku', 'sonnet', 'opus']
const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max']
const OPUS_PARALLEL = 2
function pruefeArgs(a) {
if (!a || typeof a !== 'object') throw new Error('executer-welle: args fehlen')
const fehlt = ['projekt', 'planordner', 'tasks'].filter(k => !a[k])
if (fehlt.length) throw new Error('executer-welle: args fehlen: ' + fehlt.join(', '))
if (!Array.isArray(a.tasks) || a.tasks.length === 0) throw new Error('executer-welle: tasks muss eine nicht leere Liste von {datei, modell, effort?} sein')
a.tasks.forEach((t, i) => {
if (!t || typeof t !== 'object') throw new Error(`executer-welle: tasks[${i}] ist kein Objekt`)
if (!t.datei || typeof t.datei !== 'string') throw new Error(`executer-welle: tasks[${i}] ohne datei`)
if (!MODELLE.includes(t.modell)) throw new Error(`executer-welle: ${t.datei}: modell "${t.modell}" nicht in ${MODELLE.join('/')}`)
if (t.effort !== undefined && !EFFORTS.includes(t.effort)) throw new Error(`executer-welle: ${t.datei}: effort "${t.effort}" nicht in ${EFFORTS.join('/')}`)
})
}
// Höchstens n Aufgaben gleichzeitig, ohne Leerlauf zwischen Paaren.
async function hoechstens(n, items, fn) {
const ergebnisse = new Array(items.length).fill(null)
let naechster = 0
async function arbeiter() {
while (naechster < items.length) {
const i = naechster++
try { ergebnisse[i] = await fn(items[i], i) } catch (e) { log(`Aufgabe ${i + 1} abgebrochen: ${e && e.message}`) }
}
}
await Promise.all(Array.from({ length: Math.min(n, items.length) }, () => arbeiter()))
return ergebnisse
}
const BERICHT = {
type: 'object',
properties: {
erledigt: { type: 'array', items: { type: 'string' } },
geaenderte_dateien: { type: 'array', items: { type: 'string' } },
verifikation: { type: 'array', items: { type: 'object', properties: { befehl: { type: 'string' }, ausgabe: { type: 'string' } }, required: ['befehl', 'ausgabe'] } },
abweichungen: { type: 'array', items: { type: 'string' } },
annahmen: { type: 'array', items: { type: 'string' } },
beobachtet: { type: 'array', items: { type: 'string' } },
},
required: ['erledigt', 'geaenderte_dateien', 'verifikation', 'abweichungen', 'annahmen', 'beobachtet'],
}
function auftrag(a, t) {
const rot = a.rot_nachweis_erbracht
? 'Der Rot-Nachweis dieses Tasks ist mit rot_nachweis.py schon erbracht. Du musst keine Produktivdatei beiseitelegen.'
: 'Verlangt der Task erst den roten Test und dann die Umsetzung: Leg die fertige Produktivdatei kurz beiseite (den alten Stand etwa aus git show HEAD:<pfad>), beobachte und zitiere den roten Lauf und leg danach die Fassung aus dem Plan byte-gleich zurück. Schreib sie nie neu.'
return `Setze die Taskdatei ${a.planordner}/${t.datei} um. Arbeitsverzeichnis ist das Projekt ${a.projekt}.
Verweist die Taskdatei auf globale Vorgaben, stehen sie in ${a.planordner}/00-uebersicht.md.
Erlaubt sind genau die Dateien unter "## Zieldateien" der Taskdatei. Jede andere Datei ist verboten, ebenso git commit und git push.
Code, der im Plan wörtlich steht, übernimmst du byte-genau, auch Kommentare, Docstrings und Sonderzeichen.
Ein Widerspruch im Plan wird im Bericht unter abweichungen gemeldet und nicht still aufgelöst. Das gilt auch dann, wenn ein Prüfbefehl nur grün würde, weil du einen Kommentar umformulierst. Gibt es keine Abweichung, steht dort genau ein Eintrag "keine".
${rot}
Endet eine FAILED-Zeile von pytest auf "...", ist sie gekürzt: pytest kürzt ohne Terminal auf 80 Zeichen. Wiederhole den Befehl dann mit COLUMNS=300 davor, bevor du eine Fehlerart mit dem Plan vergleichst.
Ist das Projekt ein Git-Repo, zeig am Ende die Ausgabe von git status --porcelain -- <deine Zieldateien>.
${a.zusatz ? a.zusatz + '\n' : ''}Übertrage deinen Schlussbericht in die Felder des StructuredOutput-Werkzeugs.`
}
function effortFuer(t) {
if (t.modell === 'haiku') {
if (t.effort) log(`${t.datei}: effort ${t.effort} bei haiku wirkungslos, nicht gesetzt`)
return undefined
}
if (t.effort) return t.effort
return t.modell === 'opus' ? 'high' : undefined
}
function starte(a, t) {
const opts = { label: 'executer:' + t.datei.replace(/\.md$/, ''), phase: 'Welle', schema: BERICHT, agentType: 'executer', model: t.modell }
const effort = effortFuer(t)
if (effort) opts.effort = effort
return agent(auftrag(a, t), opts).then(r => {
if (!r) log(`${t.datei}: kein Bericht (Agent abgebrochen oder übersprungen)`)
return { datei: t.datei, modell: t.modell, effort: effort || '(Frontmatter)', bericht: r }
})
}
// "keine", "Keine.", "keine — alles wie beauftragt" zählen als keine Abweichung;
// "Keine Abweichung im Code, aber ..." und alles andere zählt als Abweichung.
function istKeine(x) {
return /^\s*keine(\s+abweichung(en)?)?\s*($|[.:;!—–-])/i.test(x) && !/\baber\b/i.test(x)
}
pruefeArgs(args)
phase('Welle')
const opus = args.tasks.filter(t => t.modell === 'opus')
const rest = args.tasks.filter(t => t.modell !== 'opus')
const [restErg, opusErg] = await Promise.all([
parallel(rest.map(t => () => starte(args, t))),
hoechstens(OPUS_PARALLEL, opus, t => starte(args, t)),
])
const berichte = [...restErg, ...opusErg].filter(Boolean)
const ohneBericht = args.tasks.map(t => t.datei).filter(d => !berichte.some(b => b.datei === d && b.bericht))
const mitAbweichung = berichte.filter(b => b.bericht && b.bericht.abweichungen.some(x => !istKeine(x))).map(b => b.datei)
if (ohneBericht.length) log('Ohne Bericht: ' + ohneBericht.join(', '))
return {
berichte,
ohne_bericht: ohneBericht,
mit_abweichung: mitAbweichung,
naechster_schritt: 'Ein Bericht ist eine Behauptung. Jetzt Byte-Vergleich gegen die Kopie mit angewendetem Plan (.vault-tools/plan-kopie/welle_vergleich.py) und Gegenprüfung nach Schritt 4.',
}
+239
View File
@@ -0,0 +1,239 @@
export const meta = {
name: 'plankritik',
description: 'Liest einen Planordner mit getrennten pruefer-Linsen, fasst doppelte Befunde zusammen und lässt jeden wichtigen oder blockierenden Befund von einem zweiten pruefer zu widerlegen versuchen; höchstens zwei Opus gleichzeitig',
whenToUse: 'Schritt 2 der Standardschleife vor dem ersten Executer, Pflicht ab etwa drei Tasks (AI/Regelwerk/codeworkflow.md, "Plankritik vor dem ersten Executer"). Args: {projekt, planordner, spec?, kopie?, materialisieren?: {ohne: [relpfade], tasks?: [taskdateien]}, suite?, linsen?, verifizieren?: true, verifizieren_ab?: "wichtig"|"blockierend", effort?, werkzeuge?, zusatz?}. Ohne kopie baut materialisieren sie selbst (kopie_bauen.py plus plan_materialisiere.py); ohne() muss Geheimnisdateien und große Datenordner nennen.',
phases: [
{ title: 'Vorbereitung', detail: 'Hilfsagent: Kopie bauen, Plan anwenden, Planwerkzeuge fahren' },
{ title: 'Plankritik', detail: 'je Linse ein pruefer, paarweise' },
{ title: 'Widerlegung', detail: 'je wichtiger Befund ein zweiter pruefer, höchstens zwei gleichzeitig' },
],
}
// 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.
// Der Hilfsagent hat keinen agentType und bekommt deshalb seinen Effort mit (low: gemessen
// gleich treu wie high bei mechanischer Arbeit, codeworkflow.md "Effort je Rolle").
const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max']
const OPUS_PARALLEL = 2
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, "'\\''") + "'"
const LINSEN = {
arithmetik: (a, ctx) => `Linse: Fixture-Arithmetik und Rot-Nachweise. Rechne jede Mindestzahl, jede Grenze und jeden Schwellenwert im Produktivcode des Plans gegen jede Fixture der Tests nach. Prüfe jede im Plan erwartete Fehlermeldung und jede vorhergesagte Testausgabe wörtlich: Würde der Test vor der Umsetzung genau so rot, und danach grün? pytest kürzt FAILED-Zeilen ohne Terminal auf 80 Zeichen; vergleiche Fehlerzeilen deshalb mit COLUMNS=300 vor dem Befehl.${ctx.kopie ? ` Der Plan ist auf die Kopie ${ctx.kopie} angewendet. Fahre dort die Suite (${a.suite || 'Testbefehl aus dem Plan'}) und vergleiche jede Vorhersage des Plans mit der echten Ausgabe. In der Kopie änderst du nichts außer dem, was die Testläufe selbst anlegen.` : ' Eine Kopie mit angewendetem Plan gibt es nicht; du rechnest am Text.'}`,
bezeichner: (a, ctx) => `Linse: Bezeichner, Signaturen und Byte-Treue. Prüfe jeden Namen, jede Signatur und jeden Import über Taskgrenzen hinweg. Jeder Aus-Block muss genau einmal im echten Baum stehen, jeder Codeblock muss sich parsen lassen, und Codeblöcke dürfen keine Zeilen- und Absatztrenner, Format- oder Steuerzeichen enthalten. Prüfbefehle im Plan dürfen nur Code treffen, nie ein Wort, das auch in einem Kommentar steht.${ctx.werkzeuge ? `\n\nDie Planwerkzeuge sind schon gelaufen, hier ihre Rohausgabe. Bewerte sie, statt sie erneut zu fahren; nur bei Zweifeln selbst nachfahren.\n<werkzeugausgabe>\n${ctx.werkzeuge}\n</werkzeugausgabe>` : ` Fahre dazu plan_bloecke.py vor und plan_zeichen.py aus ${a.werkzeuge || 'D:/Claude/.vault-tools/plan-kopie'}.`}`,
abdeckung: (a) => `Linse: Spec-Abdeckung in beide Richtungen. Jede Anforderung${a.spec ? ` aus ${a.spec}` : ' der Übersicht'} braucht einen Task und einen Test, und jeder Task braucht eine Anforderung. Die "Fertig heißt"-Liste der Übersicht muss je Check einen Beleg außerhalb der eigenen Meinung haben. Das Frontmatter jeder Taskdatei braucht modell und modell_begruendung; Abschrift-Tasks mit Anführungszeichen, Gedanken- und Streckenstrichen oder Auslassungspunkten im Code tragen modell sonnet und effort low statt haiku.`,
}
const BEFUNDE = {
type: 'object',
properties: {
befunde: {
type: 'array',
items: {
type: 'object',
properties: {
schweregrad: { type: 'string', enum: ['blockierend', 'wichtig', 'klein'] },
datei: { type: 'string' },
zeile: { type: 'integer' },
beschreibung: { type: 'string' },
reproduktion: { type: 'string' },
},
required: ['schweregrad', 'datei', 'zeile', 'beschreibung', 'reproduktion'],
},
},
selbst_ausgefuehrt: { type: 'array', items: { type: 'string' } },
nicht_pruefbar: { type: 'array', items: { type: 'string' } },
},
required: ['befunde', 'selbst_ausgefuehrt', 'nicht_pruefbar'],
}
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'],
}
const URTEIL = {
type: 'object',
properties: {
urteil: { type: 'string', enum: ['bestaetigt', 'widerlegt', 'offen'] },
schweregrad: { type: 'string', enum: ['blockierend', 'wichtig', 'klein'], description: 'dein eigenes Urteil über die Schwere' },
bedingung: { type: 'string', description: 'unter welcher Bedingung der Befund gilt; leer, wenn immer' },
begruendung: { type: 'string' },
beleg: { type: 'string', description: 'eigener Befehl mit Ausgabe oder wörtliches Zitat mit Fundstelle' },
},
required: ['urteil', 'schweregrad', 'bedingung', 'begruendung', 'beleg'],
}
function pruefeArgs(a) {
if (!a || typeof a !== 'object') throw new Error('plankritik: args fehlen')
const fehlt = ['projekt', 'planordner'].filter(k => !a[k])
if (fehlt.length) throw new Error('plankritik: args fehlen: ' + fehlt.join(', '))
if (a.linsen !== undefined && !Array.isArray(a.linsen)) throw new Error('plankritik: linsen muss eine Liste sein, bekannt: ' + Object.keys(LINSEN).join(', '))
if (a.materialisieren && a.materialisieren.tasks !== undefined && !Array.isArray(a.materialisieren.tasks)) throw new Error('plankritik: materialisieren.tasks muss eine Liste von Taskdateien sein')
const unbekannt = (a.linsen || []).filter(l => !LINSEN[l])
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')
if (a.verifizieren_ab !== undefined && !['wichtig', 'blockierend'].includes(a.verifizieren_ab)) throw new Error('plankritik: verifizieren_ab muss wichtig oder blockierend sein')
if (a.materialisieren && !(Array.isArray(a.materialisieren.ohne) && a.materialisieren.ohne.length > 0 && a.materialisieren.ohne.every(x => typeof x === 'string' && x))) throw new Error('plankritik: materialisieren.ohne muss eine nicht leere Liste sein — Ausschlüsse als Pfade vom Projektwurzelordner, mindestens .git (falls vorhanden) und Geheimnisdateien')
}
// Höchstens n Aufgaben gleichzeitig, ohne Leerlauf zwischen Paaren.
async function hoechstens(n, items, fn) {
const ergebnisse = new Array(items.length).fill(null)
let naechster = 0
async function arbeiter() {
while (naechster < items.length) {
const i = naechster++
try { ergebnisse[i] = await fn(items[i], i) } catch (e) { log(`Aufgabe ${i + 1} abgebrochen: ${e && e.message}`) }
}
}
await Promise.all(Array.from({ length: Math.min(n, items.length) }, () => arbeiter()))
return ergebnisse
}
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' })
}
function auftrag(a, linse, ctx) {
return `Lies den Plan im Ordner ${a.planordner} für das Projekt ${a.projekt}, bevor ein Executer startet. Du änderst keine Datei im Projekt und keine im Plan.
${LINSEN[linse](a, ctx)}
Melde jeden Befund mit Schweregrad, auch unsichere; ein zweiter Prüfer versucht danach jeden wichtigen Befund zu widerlegen. Nenn je Befund Datei, Zeile und wie er sich reproduzieren lässt.
${a.zusatz ? a.zusatz + '\n' : ''}Übertrage deinen Schlussbericht in die Felder des StructuredOutput-Werkzeugs.`
}
function pfadnorm(p) { return String(p).replace(/\\/g, '/').replace(/\/+/g, '/').toLowerCase() }
function dateiname(p) { return pfadnorm(p).split('/').pop() }
// Doppelte Befunde verschiedener Linsen zusammenfassen: gleicher Pfad und gleiche Zeile.
// Zeile 0 oder kleiner heißt "ganze Datei" und wird nie zusammengefasst, weil darunter
// verschiedene Befunde derselben Datei liegen.
function zusammenfassen(befunde) {
const gruppen = new Map()
befunde.forEach((b, i) => {
const k = b.zeile > 0 ? pfadnorm(b.datei) + ':' + b.zeile : 'einzeln:' + i
if (!gruppen.has(k)) gruppen.set(k, { ...b, linsen: [b.linse], quellen: [b] })
else {
const g = gruppen.get(k)
g.quellen.push(b)
if (!g.linsen.includes(b.linse)) g.linsen.push(b.linse)
if (RANG[b.schweregrad] < RANG[g.schweregrad]) g.schweregrad = b.schweregrad
}
})
return [...gruppen.values()]
}
function widerlegungsauftrag(a, g, ctx) {
const texte = g.quellen.map((b, i) => `Fassung ${i + 1} (Linse ${b.linse}, ${b.schweregrad}, ${b.datei}:${b.zeile}): ${b.beschreibung}\nReproduktion laut Linse: ${b.reproduktion}`).join('\n\n')
return `Eine Plankritik hat zum Plan im Ordner ${a.planordner} (Projekt ${a.projekt}) diesen Befund gemeldet, Fundstelle ${g.datei}:${g.zeile}:
<befund>
${texte}
</befund>
Versuche ihn zu widerlegen. Lies die Stelle selbst und reproduziere den Befund mit einem eigenen Befehl${ctx.kopie ? `; der Plan ist auf die Kopie ${ctx.kopie} angewendet, dort darfst du Tests fahren` : ''}. Du änderst keine Datei im Projekt und keine im Plan.
- "bestaetigt" nur mit eigener Reproduktion oder einem wörtlichen Beleg aus der Quelle.
- "widerlegt" nur mit einem Gegenbeleg: ein Befehl mit Ausgabe oder ein Zitat, das die Aussage falsch macht.
- Sonst "offen".
Gilt der Befund nur unter einer Bedingung, nenn sie. Gib deine eigene Einschätzung der Schwere an.
Übertrage dein Urteil in die Felder des StructuredOutput-Werkzeugs.`
}
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 }
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.
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 }
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]
}
phase('Vorbereitung')
if (ctx.kopie && args.materialisieren) log('kopie ist gesetzt, materialisieren wird übergangen')
if (!ctx.kopie && args.materialisieren) {
const liste = (args.materialisieren.tasks || []).map(q).join(' ')
const auswahl = liste
? `TASKS=(${liste})`
: `shopt -s nullglob; pushd ${q(args.planordner)} >/dev/null && TASKS=(task-*.md) && popd >/dev/null; [ \${#TASKS[@]} -gt 0 ] || { echo "KEINE_TASKS: im Planordner liegt keine task-*.md; materialisieren.tasks angeben"; exit 3; }`
const befehl = `${auswahl}; T=$(mktemp -d) && echo "KOPIE=$(cygpath -m "$T/mat")" && ${PY} ${q(W + '/kopie_bauen.py')} ${q(args.projekt)} "$T/mat" --ohne ${args.materialisieren.ohne.map(q).join(' ')} && ${PY} ${q(W + '/plan_materialisiere.py')} ${q(args.planordner)} "$T/mat" "\${TASKS[@]}"`
const r = await werkzeugAgent('werkzeug:kopie', 'Vorbereitung', befehl, 'Kopie des Projekts bauen und den Plan darauf anwenden')
ctx.kopie = kopieAusLauf(r, 'Plankritik')
vorbereitung.push(r)
}
{
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')
if (r && r.laeufe && r.laeufe[0]) ctx.werkzeuge = r.laeufe[0].ausgabe
else log('Planwerkzeuge: Hilfsagent ohne Ergebnis; Linse bezeichner fährt sie selbst')
vorbereitung.push(r)
}
phase('Plankritik')
const gewaehlt = args.linsen && args.linsen.length ? args.linsen : Object.keys(LINSEN)
const kritiken = await hoechstens(OPUS_PARALLEL, gewaehlt, linse => {
const opts = { label: 'plankritik:' + linse, phase: 'Plankritik', schema: BEFUNDE, agentType: 'pruefer', model: 'opus' }
if (args.effort) opts.effort = args.effort
return agent(auftrag(args, linse, ctx), opts).then(r => ({ linse, ergebnis: r }))
})
const ergebnisse = kritiken.filter(k => k && k.ergebnis)
const ohneErgebnis = gewaehlt.filter(l => !ergebnisse.some(e => e.linse === l))
if (ohneErgebnis.length) log('Linsen ohne Ergebnis: ' + ohneErgebnis.join(', '))
const roh = ergebnisse.flatMap(e => e.ergebnis.befunde.map(b => ({ linse: e.linse, ...b })))
const gruppen = zusammenfassen(roh)
const ab = RANG[args.verifizieren_ab || 'wichtig']
const zuPruefen = args.verifizieren === false ? [] : gruppen.filter(g => RANG[g.schweregrad] <= ab)
const nichtGeprueft = gruppen.length - zuPruefen.length
log(`${roh.length} Befunde, zusammengefasst ${gruppen.length}; ${zuPruefen.length} gehen in die Widerlegung, ${nichtGeprueft} nicht (${args.verifizieren === false ? 'abgeschaltet' : 'unter der Schwelle ' + (args.verifizieren_ab || 'wichtig')})`)
phase('Widerlegung')
const urteile = await hoechstens(OPUS_PARALLEL, zuPruefen, (g, i) => {
const opts = { label: `widerlegung:${i + 1}:${dateiname(g.datei)}:${g.zeile}`, phase: 'Widerlegung', schema: URTEIL, agentType: 'pruefer', model: 'opus' }
if (args.effort) opts.effort = args.effort
return agent(widerlegungsauftrag(args, g, ctx), opts)
})
zuPruefen.forEach((g, i) => {
if (!urteile[i]) log(`Widerlegung ${i + 1} (${g.datei}:${g.zeile}) ohne Ergebnis; Befund bleibt offen`)
g.widerlegung = urteile[i] || { urteil: 'offen', schweregrad: g.schweregrad, bedingung: '', begruendung: 'Widerlegungsagent ohne Ergebnis', beleg: '' }
})
const URTEILRANG = { bestaetigt: 0, offen: 1, undefined: 2, widerlegt: 3 }
const befunde = gruppen
.map(g => ({ urteil: g.widerlegung ? g.widerlegung.urteil : 'nicht geprüft', ...g }))
.sort((x, y) => (URTEILRANG[x.widerlegung && x.widerlegung.urteil] - URTEILRANG[y.widerlegung && y.widerlegung.urteil]) || (RANG[x.schweregrad] - RANG[y.schweregrad]))
const zaehlung = { roh: roh.length, zusammengefasst: gruppen.length, bestaetigt: 0, offen: 0, widerlegt: 0, nicht_geprueft: nichtGeprueft }
befunde.forEach(b => { if (b.widerlegung) zaehlung[b.widerlegung.urteil]++ })
return {
zaehlung,
kopie: ctx.kopie,
temp_ordner: ctx.temp ? `${ctx.temp} bleibt liegen (Projektkopie ohne die Ausschlüsse); löschen, wenn nicht mehr gebraucht` : null,
befunde,
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,
ohne_ergebnis: ohneErgebnis,
}
+66
View File
@@ -0,0 +1,66 @@
export const meta = {
name: 'pruefung',
description: 'Lässt eine fertige Welle vom pruefer mit dem vollen Pflichtprogramm gegen Plan und Zielumgebung prüfen; kritische Änderungen mit xhigh',
whenToUse: 'Schritt 4 der Standardschleife nach einer Welle, zusätzlich zur eigenen Gegenprüfung des Koordinators (AI/Regelwerk/codeworkflow.md). Args: {projekt, plan, tasks?, suite?, version?, kritisch?, abschluss?, schwerpunkt?, effort?}. kritisch für sicherheits- oder datenrelevante Änderungen; abschluss fragt, ob eine weitere Runde noch Substanz hat.',
phases: [{ title: 'Prüfung', detail: 'ein pruefer' }],
}
// Rollenparameter: model wird immer gesetzt (Stamm). Der Effort kommt aus dem Frontmatter
// von .claude/agents/pruefer.md (high). kritisch hebt auf xhigh: In der Messung vom
// 2026-09-23 fuhr nur xhigh einen großen eigenen Datenlauf (codeworkflow.md, "Effort je Rolle").
const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max']
const BERICHT = {
type: 'object',
properties: {
selbst_ausgefuehrt: { type: 'array', items: { type: 'string' } },
eigener_lauf: { type: 'array', items: { type: 'string' } },
befunde: {
type: 'array',
items: {
type: 'object',
properties: {
schweregrad: { type: 'string', enum: ['blockierend', 'wichtig', 'klein'] },
datei: { type: 'string' },
zeile: { type: 'integer' },
beschreibung: { type: 'string' },
reproduktion: { type: 'string' },
},
required: ['schweregrad', 'datei', 'zeile', 'beschreibung', 'reproduktion'],
},
},
plan_vs_umsetzung: { type: 'array', items: { type: 'string' } },
geprueft_und_in_ordnung: { type: 'array', items: { type: 'string' } },
nicht_pruefbar: { type: 'array', items: { type: 'string' } },
weitere_runde: { type: 'string', description: 'nur bei Abschlussprüfung: hat eine weitere Runde noch Substanz, und warum' },
},
required: ['selbst_ausgefuehrt', 'eigener_lauf', 'befunde', 'plan_vs_umsetzung', 'geprueft_und_in_ordnung', 'nicht_pruefbar'],
}
function pruefeArgs(a) {
const fehlt = ['projekt', 'plan'].filter(k => !a || !a[k])
if (fehlt.length) throw new Error('pruefung: args fehlen: ' + fehlt.join(', '))
if (a.effort !== undefined && !EFFORTS.includes(a.effort)) throw new Error('pruefung: effort "' + a.effort + '" ungültig')
if (a.tasks !== undefined && !Array.isArray(a.tasks)) throw new Error('pruefung: tasks muss eine Liste sein')
}
function auftrag(a) {
const umfang = a.tasks && a.tasks.length ? `Geprüft wird die Umsetzung dieser Tasks: ${a.tasks.join(', ')}.` : 'Geprüft wird die gesamte Umsetzung des Plans.'
return `Prüfe die Umsetzung im Projekt ${a.projekt} gegen den Plan ${a.plan}. ${umfang}
Der Executer-Bericht ist eine Behauptung. Fahre das Pflichtprogramm deiner Agentendefinition: Tests selbst ausführen${a.suite ? ` (${a.suite})` : ''}, die zentralen Artefakte selbst lesen, einen eigenen Lauf mit realistischen Daten und eigenem Prüfcode. Eigenen Prüfcode führst du als python -c oder in einem Wegwerfordner unter dem System-Temp aus; im Projekt änderst du nichts.
${a.version ? `Aussagen über fremde Software belegst du am tatsächlich eingesetzten Versionsstand: ${a.version}.\n` : ''}${a.schwerpunkt ? `Schwerpunkt: ${a.schwerpunkt}\n` : ''}Melde jeden Befund mit Schweregrad, auch unsichere; der Koordinator filtert.
${a.abschluss ? 'Das ist die Abschlussprüfung dieser Runde. Sag im Feld weitere_runde, ob eine weitere Runde noch Substanz hat oder nur noch Text umschichtet, und warum.\n' : ''}Übertrage deinen Schlussbericht in die Felder des StructuredOutput-Werkzeugs.`
}
pruefeArgs(args)
phase('Prüfung')
const opts = { label: 'pruefung' + (args.kritisch ? ':kritisch' : ''), phase: 'Prüfung', schema: BERICHT, agentType: 'pruefer', model: 'opus' }
if (args.effort) opts.effort = args.effort
else if (args.kritisch) opts.effort = 'xhigh'
const bericht = await agent(auftrag(args), opts)
if (!bericht) throw new Error('pruefung: der pruefer hat keinen Bericht geliefert')
const RANG = { blockierend: 0, wichtig: 1, klein: 2 }
const zaehlung = { blockierend: 0, wichtig: 0, klein: 0 }
bericht.befunde.forEach(b => { zaehlung[b.schweregrad]++ })
return { effort: opts.effort || '(Frontmatter)', zaehlung, ...bericht, befunde: [...bericht.befunde].sort((x, y) => RANG[x.schweregrad] - RANG[y.schweregrad]) }
+146
View File
@@ -0,0 +1,146 @@
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.',
}
+1
View File
@@ -18,6 +18,7 @@
!/.claude/settings.json
!/.claude/agents/
!/.claude/skills/
!/.claude/workflows/
!/.vault-tools/
+32 -1
View File
@@ -27,6 +27,27 @@ Geschrieben am 2026-08-30 **vor** der ersten Zeile Code, wie es [[AI/Regelwerk/p
→ **Aufgelöst durch eine erzwungene Form.** Das Skript sucht das Literal `docs/plans/JJJJ-MM-TT` und klassifiziert das unmittelbar folgende Zeichen: `/` = Ordnerform, `.` = Dateiform. Regel: **Wer die Dateiform nennt, ohne die Ordnerform zu nennen, ist ein Befund** — denn `codeworkflow.md` schreibt beide vor und die Ordnerform ist die, die vergessen wird. Genau dieser Fall lag am 2026-08-30 in `planer.md` vor.
## Nachtrag 2026-09-23: Prüfung 5 prüft auch `effort:`
Geschrieben vor der Änderung am Skript. Anlass: Keine Agentendatei trug `effort:`, jeder Subagent erbte deshalb den Effort der Hauptsitzung, oft `xhigh` oder `max`. Seitdem steht der Effort je Rolle im Stamm und im Frontmatter (Messung und Begründung: [[AI/Regelwerk/codeworkflow|codeworkflow.md]], „Effort je Rolle"). Die Prüfung sorgt dafür, dass beide gleich bleiben.
| # | Frage | Herkunft | Auflösung |
|---|---|---|---|
| 5a | Welche Effort-Werte gibt es? | **Quelle** für die Stufen — Claude Code 2.1.280, Enum `["low","medium","high","xhigh","max"]` in der Programmdatei (nachgelesen 2026-09-23). Der Leser für Agentendateien nimmt darüber hinaus Großschreibung, `med` und ganze Zahlen an. **Projektvorgabe** ist die engere Form. | Erlaubt sind nur die fünf Wörter, klein geschrieben: eine Schreibweise, die jeder ohne Programmkenntnis liest. Die Meldung sagt deshalb „nicht in der Vorgabe", nicht „ungültig". Anführungszeichen um den Wert sind YAML und zählen nicht mit. |
| 5b | Welcher Effort gilt je Agent? | **Projektvorgabe** — Rollentabelle im Stamm, Spalte mit der Überschrift „Effort", erster Backtick-Ausdruck der Zelle | Das `effort:` im Frontmatter muss ihm gleichen. Die Spalte wird über ihre Überschrift gefunden, nicht über die Position. |
| 5c | Agentendatei ohne `effort:` oder mit leerem Wert | **Projektvorgabe** — Stamm: „Deshalb trägt jede Agentendatei `effort:`" | Befund: Der Agent erbt den Effort der Hauptsitzung. |
| 5d | Rollentabelle ohne Effort-Spalte | **Projektvorgabe** | Befund am Stamm. Die Effort-Hälfte von Prüfung 5 schweigt dann nicht, sie meldet, dass sie nicht laufen kann. |
| 5e | Tabellenzeile eines Agenten ohne Backtick-Wert in der Effort-Spalte | **Projektvorgabe** | Befund am Stamm, mit dem Namen des Agenten. |
Benannte Lücken, bewusst nicht geprüft:
- **`effort:` bei `model: haiku`.** Haiku 4.5 kennt keinen Effort; Claude Code sendet ihn dann nicht (Programmdatei). Die Rollentabelle führt keinen Agenten auf Haiku; die Abschrift-Stufe steht im Task-Frontmatter, nicht in einer Agentendatei.
- **Doppelter Schlüssel `effort:`.** Der flache Frontmatter-Leser nimmt den letzten Wert, wie bei `model:`.
- **Ob Claude Code den Wert anwendet.** Das zeigt nur das Feld `effort` im Transkript eines echten Laufs, und eine geänderte Agentendatei wirkt in der laufenden Sitzung nicht sofort. Belegt am 2026-09-23 in frischen Sitzungen über das Agent-Werkzeug und im Workflow.
- **`effort` in gespeicherten Workflows unter `.claude/workflows/`.** Sie setzen den Effort mit `agentType` absichtlich nicht; eine Abweichung dort sieht diese Prüfung nicht.
Dazu zwei Korrekturen am Regressionslauf, die mit diesem Nachtrag gekommen sind: Die Grundlinie setzt `status: pending-review` in ihrer Kopie auf `reference`. Vorher brach der Lauf nach jeder Selbstpflege an der Grundlinie ab, weil Prüfung 4 dann anschlägt — gemessen am 2026-09-23. Und Prüfung 4 bekommt dafür erstmals einen eigenen Angriffsfall.
## Positivliste, nicht Negativliste
Alle sechs Prüfungen schränken **geschlossene** Mengen ein: die Menge der Wikilinks in einer Datei, die Menge der Pflichtfelder, die Menge der Agentendateien, die Menge der Regelwerk-Dateien. Es gibt keine Prüfung, die eine offene Menge verbotener Muster abzählt — und damit auch keine handgepflegte Negativliste, die gegen sie verlieren könnte.
@@ -46,6 +67,16 @@ Je Fall eine Wegwerfkopie des Vaults, Rücksetzen dazwischen, Exit-Code und ausl
| leer | Regelwerk-Datei auf 0 Byte gekürzt |
| zusätzlich | neue Regelwerk-Datei, die niemand verlinkt |
| Wertebereich | `model: sonnet` in `pruefer.md` (Tabelle sagt `opus`) |
| Wertebereich | `effort: xhigh` in `pruefer.md` (Tabelle sagt `high`) |
| Wertebereich | `effort: hoch` in `planer.md` — kein gültiger Wert |
| fehlt, **Reihenfolge** | `effort:` fehlt in der ersten Agentendatei (`executer.md`) und, als eigener Fall, in der letzten (`recherche.md`) |
| null | `effort:` ohne Wert in `planer.md` |
| Umgebung | Effort-Spalte der Rollentabelle entfernt |
| Verweis ins Leere | Effort-Zelle eines Agenten ohne Backtick-Wert |
| Freigabe | eine Regelwerk-Datei auf `status: pending-review` |
| Gegenfall (muss stumm bleiben) | `effort: "high"` in Anführungszeichen |
| Gegenfall (muss stumm bleiben) | Zusatzspalte mit Backtick-Wert hinter der Effort-Spalte — fängt einen Rückfall auf die Spaltenposition |
| Gegenfall (muss stumm bleiben) | Effort-Zelle mit zweitem Backtick-Wert — fängt „letzter statt erster Backtick-Ausdruck" |
| Verweis ins Leere | Wikilink auf nicht existierende Datei |
| Konvention | Dateiform der Planablage ohne Ordnerform |
| **Reihenfolge** | toter Wikilink an **erster** und an **letzter** Position derselben Datei |
@@ -57,7 +88,7 @@ Die letzten drei sind die wichtigsten: Sie prüfen, dass eine fehlende Vorausset
## Abbruchbedingung, vor der ersten Runde benannt
Schluss ist, wenn eine Runde nur noch Umformulierungen findet oder wenn alle verbleibenden Befunde dieselbe Antwort haben. Die Prüfung ist fertig, wenn alle dreizehn Fälle der Angriffsliste anschlagen und die Grundlinie grün ist — nicht, wenn sie „durchläuft".
Schluss ist, wenn eine Runde nur noch Umformulierungen findet oder wenn alle verbleibenden Befunde dieselbe Antwort haben. Die Prüfung ist fertig, wenn alle Fälle der Angriffsliste anschlagen, alle Gegenfälle stumm bleiben und die Grundlinie grün ist — nicht, wenn sie „durchläuft". Die Zahl der Fälle zählt der Regressionslauf selbst; hier steht keine, weil sie bei jeder Erweiterung von Hand nachgezogen und dabei falsch würde.
## Was diese Prüfung ausdrücklich nicht ist
+136 -6
View File
@@ -20,7 +20,7 @@ Sechs Prüfungen, jede über eine GESCHLOSSENE Menge:
2 Frontmatter Trägt jede Regelwerk-Datei die sieben Pflichtfelder?
3 Erreichbarkeit Ist jede Regelwerk-Datei vom Stamm aus verlinkt?
4 Freigabe Steht eine Regelwerk-Datei auf status: pending-review?
5 Modell Weicht model: einer Agentendatei von der Rollentabelle ab?
5 Modell/Effort Weichen model: oder effort: einer Agentendatei von der Rollentabelle ab?
6 Planablage Nennt eine Datei die Dateiform ohne die Ordnerform?
Die Rollentabelle und die Liste der Regelwerk-Verweise werden AUS CLAUDE.md
@@ -36,8 +36,12 @@ WAS DIESE PRÜFUNG NICHT SIEHT — benannte Lücken
eine Heuristik im Positivlisten-Gewand. Bewusst weggelassen.
* Modellaussagen im Fließtext. "(`executer`, Sonnet — bei reiner Abschrift
Haiku)" ist für einen Menschen eindeutig und für ein Skript nicht. Geprüft
wird ausschließlich das model:-Feld im Frontmatter.
Haiku)" ist für einen Menschen eindeutig und für ein Skript nicht.
Geprüft werden ausschließlich die Felder model: und effort: im
Frontmatter.
* Ein YAML-Kommentar hinter dem Wert (effort: high # …) wird nicht
abgeschnitten und ergibt einen Befund; für model: galt das schon vorher.
* INHALTLICHE Widersprüche. Zwei Dateien können dieselbe Regel in
verschiedenen Worten gegensätzlich formulieren, und diese Prüfung meldet
@@ -46,6 +50,23 @@ WAS DIESE PRÜFUNG NICHT SIEHT — benannte Lücken
aufgefallen: die Planablage (Prüfung 6) ja, "fortsetzen statt frisch
starten" nein.
* effort: bei model: haiku. Haiku 4.5 kennt keinen Effort; Claude Code
sendet ihn dann nicht (Claude Code 2.1.280, Programmdatei, nachgelesen
2026-09-23). Die Rollentabelle führt keinen Agenten auf Haiku; die
Abschrift-Stufe steht im Task-Frontmatter, nicht in einer Agentendatei.
* Doppelter Schlüssel effort:. Der flache Frontmatter-Leser nimmt den
letzten Wert, wie bei model:.
* Ob Claude Code den Wert anwendet. Das zeigt nur das Feld effort im
Transkript eines echten Laufs, und eine geänderte Agentendatei wirkt
in der laufenden Sitzung nicht sofort. Belegt am 2026-09-23 in frischen
Sitzungen über das Agent-Werkzeug und im Workflow.
* effort in gespeicherten Workflows unter .claude/workflows/. Sie setzen
den Effort mit agentType absichtlich nicht; eine Abweichung dort sieht
diese Prüfung nicht.
===============================================================================
DIESES SKRIPT IST EIN FANGNETZ, KEIN NACHWEIS.
===============================================================================
@@ -103,6 +124,11 @@ def wikilinks(text: str) -> list[str]:
PLANPFAD = re.compile(r"docs/plans/JJJJ-MM-TT[^\s`]*")
BACKTICK = re.compile(r"`([^`]+)`")
# Die Stufen stammen aus Claude Code 2.1.280 (Programmdatei, nachgelesen
# 2026-09-23). Claude Code selbst nimmt zusätzlich Großschreibung, "med"
# und ganze Zahlen an; die Vorgabe hier ist bewusst enger.
EFFORT_WERTE = ("low", "medium", "high", "xhigh", "max")
class Befund:
def __init__(self, schwere: str, ort: str, text: str) -> None:
@@ -163,7 +189,7 @@ def rollentabelle(text: str) -> dict[str, str]:
"""Liest die Zuordnung Agent -> Modellalias aus der Tabelle im Stamm.
Eine Zeile sieht so aus:
| Planen, Spezifizieren | `planer` | Opus 5 (`opus`) |
| Planen, Spezifizieren | `planer` | Opus 5.5 (`opus`) | `high` (Frontmatter) |
Genommen wird der erste Backtick-Ausdruck der Agenten- und der
Modellspalte. Zeilen ohne Backticks in der Agentenspalte (etwa
'Hauptsitzung') werden übersprungen.
@@ -184,6 +210,52 @@ def rollentabelle(text: str) -> dict[str, str]:
return zuordnung
def entquotet(wert: str) -> str:
"""Nimmt passende einfache oder doppelte Anführungszeichen ab: 'high' -> high."""
if len(wert) >= 2 and wert[0] == wert[-1] and wert[0] in ("'", '"'):
return wert[1:-1]
return wert
def effort_spalte(text: str) -> int | None:
"""Findet den Index der Effort-Spalte in der Rollentabelle im Stamm.
Gesucht wird die Tabellenzeile, deren Zellen (getrimmt) sowohl 'Agent'
als auch 'Effort' enthalten — das ist die Kopfzeile. Der Index der Zelle
'Effort' ist die Effort-Spalte.
"""
for zeile in text.split("\n"):
if not zeile.startswith("|"):
continue
spalten = [s.strip() for s in zeile.strip("|").split("|")]
if "Agent" in spalten and "Effort" in spalten:
return spalten.index("Effort")
return None
def rollentabelle_effort(text: str, spalte: int) -> dict[str, str]:
"""Liest den Effort je Agent aus der Rollentabelle im Stamm.
Genommen wird der erste Backtick-Ausdruck der Effort-Spalte (`spalte`).
Fehlt ein Backtick-Ausdruck, ist der Wert für diesen Agenten leer.
"""
zuordnung: dict[str, str] = {}
for zeile in text.split("\n"):
if not zeile.startswith("|"):
continue
spalten = [s.strip() for s in zeile.strip("|").split("|")]
if len(spalten) <= spalte:
continue
agenten = BACKTICK.findall(spalten[1])
if not agenten:
continue
effort_treffer = BACKTICK.findall(spalten[spalte])
wert = effort_treffer[0].strip() if effort_treffer else ""
for name in agenten:
zuordnung[name.strip()] = wert
return zuordnung
def stamm_regelwerkverweise(text: str) -> set[str]:
"""Alle im Stamm verlinkten Regelwerk-Dateien, als Dateiname ohne Endung."""
namen: set[str] = set()
@@ -252,7 +324,13 @@ def pruefe_freigabe(vault: Path, befunde: list[Befund]) -> None:
)
def pruefe_modelle(vault: Path, tabelle: dict[str, str], befunde: list[Befund]) -> None:
def pruefe_modelle(
vault: Path,
tabelle: dict[str, str],
effort_index: int | None,
effort_tabelle: dict[str, str],
befunde: list[Befund],
) -> None:
for datei in sorted((vault / AGENTEN).glob("*.md")):
rel = datei.relative_to(vault).as_posix()
fm = frontmatter(lies(datei))
@@ -278,6 +356,47 @@ def pruefe_modelle(vault: Path, tabelle: dict[str, str], befunde: list[Befund])
)
)
# Effort: nur prüfbar, wenn die Effort-Spalte der Rollentabelle
# gefunden wurde (siehe main() für den Befund am Stamm, falls nicht).
if effort_index is None:
continue
soll = effort_tabelle.get(name, "")
if not soll:
befunde.append(
Befund("wichtig", "CLAUDE.md", f"Rollentabelle nennt für '{name}' keinen Effort")
)
effort_roh = fm.get("effort")
if not effort_roh:
befunde.append(
Befund(
"wichtig",
rel,
"kein effort: im Frontmatter — der Agent erbt den Effort der Hauptsitzung",
)
)
continue
effort_wert = entquotet(effort_roh)
if not effort_wert:
befunde.append(
Befund(
"wichtig",
rel,
"kein effort: im Frontmatter — der Agent erbt den Effort der Hauptsitzung",
)
)
elif effort_wert not in EFFORT_WERTE:
befunde.append(
Befund(
"blockierend",
rel,
f"effort: {effort_wert} ist nicht in der Vorgabe (erlaubt: low, medium, high, xhigh, max)",
)
)
elif soll and effort_wert != soll:
befunde.append(
Befund("blockierend", rel, f"effort: {effort_wert} — Rollentabelle im Stamm sagt {soll}")
)
def pruefe_planablage(vault: Path, dateien: list[Path], befunde: list[Befund]) -> None:
for datei in dateien:
@@ -339,6 +458,17 @@ def main() -> int:
Befund("blockierend", "CLAUDE.md", "keine Regelwerk-Verweise gefunden — Prüfung 3 kann nicht laufen")
)
effort_index = effort_spalte(stammtext) if tabelle else None
if tabelle and effort_index is None:
befunde.append(
Befund(
"blockierend",
"CLAUDE.md",
"Effort-Spalte der Rollentabelle nicht auffindbar — Prüfung 5 kann den Effort nicht prüfen",
)
)
effort_tabelle = rollentabelle_effort(stammtext, effort_index) if effort_index is not None else {}
dateien = dateien_im_geltungsbereich(vault)
pruefe_wikilinks(vault, dateien, befunde); gelaufen += 1
@@ -347,7 +477,7 @@ def main() -> int:
pruefe_erreichbarkeit(vault, verlinkt, befunde); gelaufen += 1
pruefe_freigabe(vault, befunde); gelaufen += 1
if tabelle and (vault / AGENTEN).is_dir():
pruefe_modelle(vault, tabelle, befunde); gelaufen += 1
pruefe_modelle(vault, tabelle, effort_index, effort_tabelle, befunde); gelaufen += 1
pruefe_planablage(vault, dateien, befunde); gelaufen += 1
except SystemExit:
+162 -1
View File
@@ -6,7 +6,10 @@ Angriffsliste und Begründung: .vault-tools/regelwerk-check/ENTWURF.md
Der Treiber baut aus dem echten Vault einen Minimalvault, in dem die Prüfung
sauber durchläuft — das ist die GRUNDLINIE. Läuft sie nicht grün, misst der
Rest nichts und der Treiber bricht ab.
Rest nichts und der Treiber bricht ab. Dazu normalisiert die Grundlinie jede
Frontmatter-Zeile status: pending-review auf status: reference — ohne das
bricht der Lauf nach jeder Selbstpflege an der Grundlinie ab, weil Prüfung 4
dann anschlägt (gemessen am 2026-09-23).
Danach je Angriffsfall: frische Kopie der Grundlinie, genau eine Beschädigung,
Prüfung fahren, Exit-Code und auslösende Meldung protokollieren. Ein Fall gilt
@@ -64,6 +67,15 @@ def baue_grundlinie(ziel: Path) -> None:
unter.mkdir(parents=True, exist_ok=True)
shutil.copy2(skill, unter / "SKILL.md")
# jede Zeile status: pending-review auf status: reference normalisieren.
# Ohne das bricht der Lauf nach jeder Selbstpflege an der Grundlinie ab,
# weil Prüfung 4 (Freigabe) dann anschlägt — gemessen am 2026-09-23.
for datei in (ziel / "AI" / "Regelwerk").glob("*.md"):
t = datei.read_text(encoding="utf-8")
neu = re.sub(r"^status: pending-review$", "status: reference", t, flags=re.M)
if neu != t:
datei.write_text(neu, encoding="utf-8")
# Jedes Linkziel außerhalb des Geltungsbereichs als leere Datei anlegen,
# damit die Grundlinie nicht an fehlendem Umfeld scheitert.
for datei in list(ziel.rglob("*.md")):
@@ -158,6 +170,144 @@ def s_keine_rollentabelle(v: Path) -> None:
f.write_text(re.sub(r"^\|.*$", "", t, flags=re.M), encoding="utf-8")
def s_effort_wertebereich_xhigh(v: Path) -> None:
f = v / ".claude" / "agents" / "pruefer.md"
t = f.read_text(encoding="utf-8")
muster = r"^effort: high$"
assert len(re.findall(muster, t, flags=re.M)) == 1
f.write_text(re.sub(muster, "effort: xhigh", t, count=1, flags=re.M), encoding="utf-8")
def s_effort_wertebereich_ungueltig(v: Path) -> None:
f = v / ".claude" / "agents" / "planer.md"
t = f.read_text(encoding="utf-8")
muster = r"^effort: high$"
assert len(re.findall(muster, t, flags=re.M)) == 1
f.write_text(re.sub(muster, "effort: hoch", t, count=1, flags=re.M), encoding="utf-8")
def s_effort_fehlt_erste(v: Path) -> None:
"""Reihenfolge-Fall: effort: fehlt in der ERSTEN Agentendatei (executer.md)."""
f = v / ".claude" / "agents" / "executer.md"
t = f.read_text(encoding="utf-8")
muster = r"^effort:.*\n"
assert len(re.findall(muster, t, flags=re.M)) == 1
f.write_text(re.sub(muster, "", t, count=1, flags=re.M), encoding="utf-8")
def s_effort_fehlt_letzte(v: Path) -> None:
"""Reihenfolge-Fall: derselbe Schaden in der LETZTEN Agentendatei (recherche.md)."""
f = v / ".claude" / "agents" / "recherche.md"
t = f.read_text(encoding="utf-8")
muster = r"^effort:.*\n"
assert len(re.findall(muster, t, flags=re.M)) == 1
f.write_text(re.sub(muster, "", t, count=1, flags=re.M), encoding="utf-8")
def s_effort_null(v: Path) -> None:
f = v / ".claude" / "agents" / "planer.md"
t = f.read_text(encoding="utf-8")
muster = r"^effort:.*$"
assert len(re.findall(muster, t, flags=re.M)) == 1
f.write_text(re.sub(muster, "effort:", t, count=1, flags=re.M), encoding="utf-8")
def s_effort_spalte_fehlt(v: Path) -> None:
"""Entfernt in der Rollentabelle bei der Kopfzeile und allen direkt
folgenden Tabellenzeilen die letzte Zelle — die Effort-Spalte."""
f = v / "CLAUDE.md"
zeilen = f.read_text(encoding="utf-8").split("\n")
kopfzeilen = [
i for i, z in enumerate(zeilen)
if z.startswith("|") and "Agent" in z and "Effort" in z
]
assert len(kopfzeilen) == 1
start = kopfzeilen[0]
ende = start
while ende < len(zeilen) and zeilen[ende].startswith("|"):
ende += 1
for i in range(start, ende):
teile = zeilen[i].split("|")
kern = teile[1:-1]
if len(kern) < 2:
continue
zeilen[i] = "|" + "|".join(kern[:-1]) + "|"
f.write_text("\n".join(zeilen), encoding="utf-8")
def s_effort_verweis_leer(v: Path) -> None:
"""Entfernt in der Tabellenzeile mit `pruefer` die Backticks um 'high'."""
f = v / "CLAUDE.md"
zeilen = f.read_text(encoding="utf-8").split("\n")
treffer = [i for i, z in enumerate(zeilen) if z.startswith("|") and "`pruefer`" in z]
assert len(treffer) == 1
i = treffer[0]
assert zeilen[i].count("`high`") == 1
zeilen[i] = zeilen[i].replace("`high`", "high", 1)
f.write_text("\n".join(zeilen), encoding="utf-8")
def s_effort_freigabe_pending(v: Path) -> None:
f = v / "AI" / "Regelwerk" / "schreibstil.md"
t = f.read_text(encoding="utf-8")
muster = r"^status:.*$"
assert len(re.findall(muster, t, flags=re.M)) == 1
f.write_text(re.sub(muster, "status: pending-review", t, count=1, flags=re.M), encoding="utf-8")
def g_effort_anfuehrung(v: Path) -> None:
"""Kein Schaden: effort in passenden Anführungszeichen ist gültiges YAML.
Muss stumm bleiben — Regressionslauf-Fall zu Punkt 4 der Effort-Prüfung
(Prüfwerkzeuge.md, Abschnitt 5: neuer Code bekommt seinen eigenen Fall).
"""
f = v / ".claude" / "agents" / "pruefer.md"
t = f.read_text(encoding="utf-8")
muster = r"^effort: high$"
assert len(re.findall(muster, t, flags=re.M)) == 1
f.write_text(re.sub(muster, 'effort: "high"', t, count=1, flags=re.M), encoding="utf-8")
def g_zusatzspalte(v: Path) -> None:
"""Kein Schaden: eine Spalte hinter Effort mit eigenem Backtick-Wert.
Muss stumm bleiben — die Effort-Spalte wird über ihre Überschrift
gefunden, nicht über die letzte Spalte.
"""
f = v / "CLAUDE.md"
zeilen = f.read_text(encoding="utf-8").split("\n")
kopfzeilen = [
i for i, z in enumerate(zeilen)
if z.startswith("|") and "Agent" in z and "Effort" in z
]
assert len(kopfzeilen) == 1
start = kopfzeilen[0]
zeilen[start] = zeilen[start].rstrip() + " Notiz |"
zeilen[start + 1] = zeilen[start + 1].rstrip() + " --- |"
i = start + 2
while i < len(zeilen) and zeilen[i].startswith("|"):
zeilen[i] = zeilen[i].rstrip() + " `xhigh` |"
i += 1
f.write_text("\n".join(zeilen), encoding="utf-8")
def g_zweiter_backtick(v: Path) -> None:
"""Kein Schaden: ein zweiter Backtick-Ausdruck in der Effort-Zelle.
Muss stumm bleiben — genommen wird der erste Backtick-Ausdruck.
"""
f = v / "CLAUDE.md"
zeilen = f.read_text(encoding="utf-8").split("\n")
treffer = [i for i, z in enumerate(zeilen) if z.startswith("|") and "`pruefer`" in z]
assert len(treffer) == 1
i = treffer[0]
assert zeilen[i].count("`high` (Frontmatter)") == 1
zeilen[i] = zeilen[i].replace(
"`high` (Frontmatter)", "`high` (Frontmatter, nicht `xhigh`)", 1
)
f.write_text("\n".join(zeilen), encoding="utf-8")
def g_link_im_codeblock(v: Path) -> None:
"""Kein Schaden, sondern eine Falle: Wikilink-artiges IN Code.
@@ -187,6 +337,9 @@ def g_link_im_codeblock(v: Path) -> None:
# seinen eigenen Fall (pruefwerkzeuge.md, Abschnitt 5).
GEGENFAELLE = [
("codeblock | Wikilink-artiges in Code und Backticks", g_link_im_codeblock),
("anfuehrung | effort in Anführungszeichen", g_effort_anfuehrung),
("zusatzspalte | Spalte mit Backtick-Wert hinter Effort", g_zusatzspalte),
("zweiter-backtick | Effort-Zelle mit zweitem Backtick-Wert", g_zweiter_backtick),
]
@@ -203,6 +356,14 @@ FAELLE = [
("umgebung | CLAUDE.md fehlt", s_kein_stamm, "fehlt — nichts prüfbar"),
("umgebung | AI/Regelwerk fehlt", s_kein_regelwerk, "Verzeichnis fehlt"),
("umgebung | Rollentabelle unauffindbar", s_keine_rollentabelle, "Rollentabelle nicht auffindbar"),
("wertebereich| effort: xhigh statt high", s_effort_wertebereich_xhigh, "effort: xhigh — Rollentabelle im Stamm sagt high"),
("wertebereich| effort: hoch (ungültig)", s_effort_wertebereich_ungueltig, "ist nicht in der Vorgabe"),
("fehlt | effort fehlt, ERSTE Agentendatei", s_effort_fehlt_erste, ".claude/agents/executer.md — kein effort: im Frontmatter"),
("fehlt | effort fehlt, LETZTE Agentendatei", s_effort_fehlt_letzte, ".claude/agents/recherche.md — kein effort: im Frontmatter"),
("null | effort ohne Wert", s_effort_null, ".claude/agents/planer.md — kein effort: im Frontmatter"),
("umgebung | Effort-Spalte fehlt", s_effort_spalte_fehlt, "Effort-Spalte der Rollentabelle nicht auffindbar"),
("verweis | Effort-Zelle ohne Wert", s_effort_verweis_leer, "Rollentabelle nennt für 'pruefer' keinen Effort"),
("freigabe | status: pending-review", s_effort_freigabe_pending, "Freigabe steht aus"),
]
+50 -5
View File
@@ -2,10 +2,10 @@
type: resource
status: reference
title: Codeworkflow
summary: Arbeitsweise für Code-Projekte — Standardschleife, Planaufteilung je Task, Subagenten und ihre Modellstufen, Gegenprüfung, Lauf mit echten Daten, Aufgabenmanagement.
summary: Arbeitsweise für Code-Projekte — Standardschleife, Planaufteilung je Task, Subagenten und ihre Modellstufen und Effort, Gegenprüfung, Lauf mit echten Daten, Aufgabenmanagement.
tags: [regelwerk, codeworkflow, code-projekte]
created: 2026-08-03
updated: 2026-09-20
updated: 2026-09-23
---
# Codeworkflow
@@ -55,6 +55,14 @@ Die Lehre daraus gilt über diesen Fall hinaus: **Eine gerechnete Ersparnis ist
Steht der Code wörtlich da, sind die Zieldateien namentlich genannt und die Verbote ausdrücklich, dann ist die Executer-Rolle **Abschrift, nicht Autorschaft** — dafür genügt Haiku. Bleibt Ermessen offen, ist es Fehlersuche, oder muss Fließtext formuliert statt abgeschrieben werden, dann Sonnet.
**Ausnahme für Abschrift mit typografischen Zeichen (seit 2026-09-23, Pascals Entscheidung):** Enthält ein Codeblock des Tasks Anführungszeichen wie „ “ ” ‚ ‘ ’ « », Gedanken- oder Streckenstriche – — oder Auslassungspunkte …, dann `modell: sonnet` und `effort: low` statt Haiku. Haiku hat solche Zeichen in der Messung still ersetzt, siehe „Effort je Rolle". Nachsehen lässt sich das je Taskdatei, die Ausgabe `[]` heißt Haiku geht:
```bash
python -X utf8 -c "import sys; t=open(sys.argv[1],encoding='utf-8').read(); b=''.join(t.split(chr(96)*3)[1::2]); print(sorted(set(b) & set('„“”‚‘’«»–—…')))" task-NN-thema.md
```
Das Feld `effort:` im Task-Frontmatter ist freiwillig. Fehlt es, gilt das Frontmatter des Executers (`medium`); bei `modell: haiku` bleibt es wirkungslos.
**4 Gegenprüfen — der Kern der Schleife.** Ein Agentenbericht ist eine Behauptung, kein Beleg. Pflicht sind drei Dinge:
- Testlauf selbst wiederholen, nicht die zitierte Ausgabe übernehmen.
@@ -88,6 +96,8 @@ Dabei die **Ertragskurve mitschreiben**: was hat Runde 1 gefunden, was Runde 2,
- **Escape-Folgen und unsichtbare Zeichen überleben die Schreibschicht nicht verlässlich.** Eine Unicode-Escape-Folge im Code (Backslash, `u`, vier Hex-Ziffern) wird auf dem Weg durch die JSON-Eingabe eines Schreibwerkzeugs mal zum echten Zeichen, mal bekommt sie einen zweiten Backslash — beim Executer wie beim Koordinator. Umgekehrt wird ein echtes unsichtbares Zeichen im Plan (etwa U+2028) beim Abschreiben zum Leerzeichen, und der Test dazu bleibt grün und blind. Am 2026-09-17 im Projekt „Trading Plattform" wichen vier von fünf Haiku-Tasks mit solchen Folgen ab, einmal mit sichtbarem Fehler auf der Seite bei grünen Tests; der Koordinator hat denselben Fehler beim Korrigieren des Plans selbst gemacht. Regel: Nach jedem Schreiben einer solchen Stelle die Bytes ansehen (`od -c`); Korrekturen daran per Skript mit `chr()`-Codepunkten; nach jeder programmatischen Plankorrektur die Codeblöcke auf Zeilen- und Absatztrenner, Format- und Steuerzeichen absuchen; der Planer vermeidet Escape-Folgen, wo ein sichtbares Zeichen erlaubt ist — und wo die Folge Absicht ist (Homoglyph, unsichtbares Zeichen), sichert ein Test den gerenderten Wortlaut. Die Selbstprüfung eines Executers läuft über das Read-Werkzeug, nicht über grep: Dort sieht man, ob ein oder zwei Backslashes dastehen.
- **pytest kürzt ohne Terminal.** Die `FAILED`-Zeilen der Kurzzusammenfassung enden bei 80 Zeichen, die Fehlerart fehlt dann („Failed: D..."). Am 2026-09-23 fanden das zwei Plankritik-Linsen unabhängig, nachgemessen an pytest 9.1.1: 80 Zeichen ohne, 103 mit `COLUMNS=300`. Ein Plan, der eine Fehlerzeile wörtlich erwartet, setzt deshalb `COLUMNS=300` in den Befehl; sonst hält der Executer an oder bestätigt ungesehen. Die Regel für den Planer steht in `.claude/agents/planer.md`.
Nach jeder programmatischen Änderung an einem Plan: eingebettete Codeblöcke parsen lassen und die Überschriften zählen. Beides kostet zwei Zeilen und hätte beide Fälle gefangen. Und nach jedem Executer-Lauf: jede vollständig ersetzte oder neu angelegte Datei byteweise gegen den Codeblock im Plan vergleichen — ein Bericht mit „keine Abweichungen" ist eine Behauptung, der Vergleich ist der Beleg.
**Die Messung verformt die Arbeit, wenn sie mehr misst als gemeint war.** Eine Nachweisvorgabe verlangte Zahlengleichheit über eine ganze Datei — damit musste ein Befehlszusatz auch in Überschriften stehen und machte das Kapitel unlesbar. Der Executer hat das als Abweichung gemeldet statt still davon abzuweichen; der Fehler lag in der Vorgabe, nicht in der Umsetzung. Prüfbefehle auf den Geltungsbereich einschränken, den sie meinen.
@@ -167,7 +177,7 @@ Dieselbe Quellenpflicht gilt für dich als Koordinator. Was du nicht an einer Qu
### Modellzuordnung und Superpowers
Die Rollentabelle steht im Stamm ([[CLAUDE|Claude.md]], Abschnitt „Agenten & Modelle"). Kurz: Denken und Prüfen auf **Opus 5**, Tippen und Suchen auf **Sonnet 5**.
Die Rollentabelle steht im Stamm ([[CLAUDE|Claude.md]], Abschnitt „Agenten & Modelle"). Kurz: Denken und Prüfen auf **Opus 5.5**, Tippen und Suchen auf **Sonnet 5**. Den Effort je Rolle regelt „Effort je Rolle" unten.
**Beim Start eines Subagenten das Modell immer explizit angeben.** Ohne Angabe erbt der Subagent das Sitzungsmodell — bei Opus-Sitzungen laufen dann auch mechanische Umsetzungsaufgaben auf Opus, was die Zuordnung still aushebelt.
@@ -180,8 +190,8 @@ Superpowers-Skills sprechen von „dispatch a `general-purpose` subagent". Diese
| `subagent-driven-development` → Task Reviewer / Re-Review | `pruefer` | `opus` |
| `requesting-code-review` → Code Reviewer | `pruefer` | `opus` |
| `verification-before-completion` | `pruefer` | `opus` |
| `dispatching-parallel-agents` (Recherche) | `Explore` / `general-purpose` | `sonnet` |
| `systematic-debugging` → Hypothese bilden | Koordinator selbst | Opus 5 |
| `dispatching-parallel-agents` (Recherche) | `recherche`; reine Dateisuche `Explore` | `sonnet` |
| `systematic-debugging` → Hypothese bilden | Koordinator selbst | Opus 5.5 |
Die Modellwahl-Empfehlung in `subagent-driven-development` („use the least powerful model that can handle each role") bleibt gültig und wird hier auf drei Stufen gebracht: alles Urteilende auf Opus, alles Ausführende auf Sonnet, reine Abschrift auf Haiku. Nur wenn der Executer in der Fehlerbehebungsschleife zweimal hintereinander scheitert, wird die Nachbesserung an einen Opus-Agenten hochgestuft.
@@ -201,6 +211,41 @@ Was das **nicht** belegt: Es sind zwei verschiedene Aufgaben, kein kontrollierte
Und der wichtigste Punkt zur Reihenfolge der Hebel: **Die teure Ressource ist nicht der Executer, sondern der Koordinatorkontext, den jede zusätzliche Korrekturrunde verbraucht.** Ein Modell, das eine Runde spart, schlägt jede Ersparnis am Tokenpreis. Deshalb steht die Frage „braucht dieser Task Ermessen" vor der Frage „welches Modell ist billiger".
### Effort je Rolle
Der Effort ist neben dem Modell der zweite Regler, und bis 2026-09-23 war er nirgends gesetzt. Ein Subagent ohne eigenen Effort erbt den der Hauptsitzung. Läuft die auf `xhigh` oder `max`, laufen Planer, Prüfer und Executer mit. Was das gekostet hat, zeigt die Historie über 751 Subagenten aus 47 Sitzungen (2026-08-05 bis 2026-09-23). Der Median je Lauf lag beim Planer auf Opus 5 bei 5,16 $ mit `high` und 18,55 $ mit `max`, beim Prüfer bei 7,11 $ gegen 13,24 $, beim Sonnet-Executer bei 0,76 $ gegen 1,59 $. Die Aufgaben waren verschieden groß, sauber vergleichbar ist das also nicht. Der Modellkatalog im Claude-Code-Programm nennt für Sonnet 5 aber selbst die Kostenfaktoren: `xhigh` das 2,4-Fache, `max` das 5,6-Fache von `high`.
Woher ein Agent seinen Effort bekommt, in dieser Rangfolge (nachgelesen im Programm 2.1.280): eine gesetzte Umgebungsvariable `CLAUDE_CODE_EFFORT_LEVEL` schlägt alles, ist im Vault aber nicht gesetzt (geprüft 2026-09-23); danach der `effort`-Parameter eines Workflow-`agent()`-Aufrufs, dann `effort:` im Frontmatter der Agentendatei, dann der Effort der Sitzung, dann der Standard des Modells. Das Agent-Werkzeug hat keinen Effort-Parameter; dort wirkt nur das Frontmatter. Belegt am 2026-09-23 in einer frischen Sitzung auf `xhigh`: `executer` lief mit `medium`, `pruefer` mit `high`, beides aus dem Transkriptfeld `effort`. Belegt ist das auch im Workflow: Mit `agentType` liefen `executer` und `recherche` mit `medium` aus dem Frontmatter, der Agent ohne Typ mit dem `xhigh` der Sitzung. Zwei Fallstricke dabei. Eine geänderte Agentendatei wirkt in der laufenden Sitzung nicht sofort: Die Änderung von 13:58 zeigte sich um 14:00 noch nicht, knapp eine Stunde später schon. Was das Nachladen auslöst, ist nicht bekannt; sicher ist nur der Blick ins Transkriptfeld `effort`. Einen neu angelegten gespeicherten Workflow kannte die laufende Sitzung zuerst nicht, eine frische schon. Gut eine Stunde später war er auch in der laufenden aufrufbar. Und Haiku 4.5 kennt keinen Effort; ein gesetzter Wert wird gar nicht gesendet.
Die Werte stammen aus einer Messung vom 2026-09-23 an drei Prüfständen mit bekanntem Ergebnis, je Stufe zwei Läufe:
| Rolle | Effort | Was gemessen wurde | Median je Lauf |
|---|---|---|---|
| `pruefer` (Opus 5.5) | `high` | Review-Fall mit neun eingebauten Fehlern gegen eine SPEC, Tests grün. Jede Stufe von `low` bis `xhigh` fand 9 von 9, keine Fehlalarme. Berechtigte Zusatzfunde je Lauf: `low` 1 und 1, `medium` 3 und 1, `high` 3 und 3, `xhigh` 3 und 3. | `low` 0,24 $, `medium` 0,29 $, `high` 0,34 $, `xhigh` 1,09 $ |
| `planer` (Opus 5.5) | `high` | Korrekturplan für dieselben neun Fehler, danach mit `plan_materialisiere.py` angewendet. Alle sechs Pläne ließen sich anwenden, Suite grün, 9 von 9 behoben (eigene Probe). Gegen den alten Code schlugen je Plan 9 bis 13 der neuen Tests fehl; die 1 bis 3 schon vorher grünen sind laut Planerbericht bewusste Absicherungstests. Bei Anwendbarkeit, Suite und Probe kein Unterschied zwischen den Stufen. | `medium` 0,75 $, `high` 0,82 $, `xhigh` 2,90 $ |
| `executer` (Sonnet 5) | `medium` | Abschrift-Task mit einem Prüf-grep, der den Docstring trifft. Alle Stufen schrieben gleich treu ab und meldeten den Widerspruch, statt den Docstring umzuformulieren. | `low` 0,13 $, `medium` 0,15 $, `high` 0,19 $ |
Warum nicht überall die billigste Stufe: Beim Prüfer ist Abdeckung der Zweck. Ab `high` brachten beide Läufe die drei Zusatzfunde, darunter nicht; `xhigh` brachte nicht mehr als `high`. Beim Planer war `medium` gleich gut und 9 Prozent billiger. Ein Fehler im Plan kostet aber Executer-Wellen und Korrekturrunden im Koordinatorkontext, deshalb `high`. Beim Executer entspricht `medium` laut Doku etwa Sonnet 4.6 bei `high`. Ob `medium` bei Tasks mit Ermessen hält, ist nicht gemessen; scheitert ein Executer dort, gilt die Hochstufung auf Opus aus dem Abschnitt oben.
`xhigh` kostete beim Prüfer das 3,2-Fache, beim Planer das 3,5-Fache von `high`, ohne einen zusätzlichen Treffer im Schlüssel. Ein Prüfer auf `xhigh` fuhr dafür laut seinem Bericht einen großen eigenen Datenlauf (600 Artikel, 5000 Buchungen). Das ist genau der Lauf mit echten Daten, der Fehler findet, die erst bei realistischen Mengen auftreten — der Prüfstand hatte keinen solchen Fehler. Für die Abschlussprüfung einer sicherheits- oder datenrelevanten Änderung gibst du im Workflow deshalb `effort: 'xhigh'` mit.
Was die Messung nicht belegt: Die Prüfstände waren klein, ein Modul mit 73 Zeilen. Bei Plänen und Reviews über viele Dateien kann der Abstand zwischen den Stufen anders aussehen. Die Recherche-Aufgabe (acht Fragen, Antworten verteilt auf fünf Regelwerk-Dateien) war zu leicht: Haiku und Sonnet beantworteten sie in jeder Stufe 8 von 8. Belegt ist dort nur, dass Nachschlagen keinen hohen Effort braucht.
**In Workflows** bekommt jeder `agent()`-Aufruf `model`. Den Effort liefert mit `agentType` (`planer`, `pruefer`, `executer`, `recherche`) dessen Frontmatter; ein `effort`-Parameter daneben schlägt das Frontmatter und steht nur für eine bewusste Abweichung. Ohne `agentType` erbt der Agent die Sitzung, dort wird der Effort immer mitgegeben. Plankritik, Executer-Welle und Prüfung liegen als gespeicherte Workflows unter `.claude/workflows/` (`plankritik`, `executer-welle`, `pruefung`); sie setzen genau das um und halten Opus bei höchstens zwei gleichzeitig. Was sie darüber hinaus tun:
- `plankritik` baut auf Wunsch die Kopie mit angewendetem Plan selbst (`materialisieren` mit Ausschlüssen) und lässt einen billigen Hilfsagenten die Planwerkzeuge fahren; die Linsen bekommen die Rohausgabe. Danach fasst sie doppelte Befunde zusammen und lässt jeden wichtigen oder blockierenden von einem zweiten `pruefer` zu widerlegen versuchen: „bestätigt" nur mit eigener Reproduktion, „widerlegt" nur mit Gegenbeleg, sonst „offen". Das filtert Fehlalarme, bevor du den Plan korrigierst, und kostet je Befund einen Opus-Lauf.
- `welle-mit-pruefung` fasst Schritt 3 und 4 zusammen: Kopie aus dem Stand **vor** der Welle, `executer-welle`, Byte-Vergleich und Suite durch den Hilfsagenten, dann `pruefung` mit diesen Rohbelegen. Ist die Welle unvollständig, prüft er nicht und sagt das.
- Eine selbst gebaute Kopie verlangt eine nicht leere Liste von Ausschlüssen (`ohne`), gedacht für `.git` und Geheimnisdateien; fehlt sie oder ist sie leer, bricht der Workflow vor dem ersten Agenten ab. Ob die Liste die Geheimnisse wirklich trifft, sieht er nicht — das bleibt deine Sorgfalt. Die Kopie bleibt im Temp liegen, der Rückgabewert nennt den Ordner (`temp_ordner`).
- Scheitert in `welle-mit-pruefung` ein verschachtelter Schritt, kommen Welle, Kopie und Vergleich trotzdem zurück, der Fehler steht unter `fehler`.
Deine eigene Gegenprüfung aus Schritt 4 ersetzt keiner davon; sie wird nur billiger, weil die Rohbelege schon da sind. Recherche und Sammeln `medium` — gemessen reichte fürs Nachschlagen sogar `low`, `medium` ist die Reserve für Recherche mit Synthese oder Webquellen, die der Prüfstand nicht hatte. Reine Abschrift `low` (gleich treu wie `high`, halbe Laufzeit), Urteil und Plan `high`. Höchstens zwei Opus-Agenten gleichzeitig: Am 2026-09-20 riss das Wochenkontingent bei neun parallelen Opus-Agenten, und alles stand zwei Stunden. Wo das richtige Ergebnis feststeht, wertet ein Muster im Skript aus statt eines Richter-Agenten.
**Bei Sonnet-Agenten treibt das Lesen die Kosten, nicht der Effort.** In den sechs Sonnet-Recherche-Läufen machte das Cache-Schreiben der gelesenen Dateien je Lauf ein Drittel bis gut 70 Prozent der Kosten aus, im Mittel gut die Hälfte; teurer oder billiger nach Effort sortierten sich die Läufe nicht. Gezielt lesen — `Grep` mit Zeilennummern, `Read` mit `offset` und `limit` — spart dort mehr als eine niedrigere Stufe. Bei Opus-Agenten dominiert die Ausgabe einschließlich Thinking, und genau die steuert der Effort.
**Haiku und typografische Zeichen.** Im Abschrift-Prüfstand ersetzte Haiku in beiden Läufen still ein deutsches schließendes Anführungszeichen (U+201C) durch ein gerades und meldete trotzdem „byte-genau". Sonnet tat das in keiner Stufe. Haiku kostete 0,08 $ gegen 0,13 $ für Sonnet auf `low`. Pascal hat am 2026-09-23 entschieden: Abschrift mit typografischen Zeichen läuft auf Sonnet `low`, die Regel steht in Schritt 3. Der Byte-Vergleich bleibt trotzdem Pflicht.
**Die Escape-Folge für U+2028 hat jedes Modell verloren.** Alle acht Abschrift-Läufe, Haiku wie Sonnet in jeder Stufe, schrieben sie als echtes Zeichen in die Datei. Alle acht Berichte nannten die Datei trotzdem „byte-genau" übernommen, die Tests blieben grün. Das liegt an der Schreibschicht, nicht am Modell. Der Byte-Vergleich nach jeder Welle bleibt Pflicht, egal mit welchen Parametern.
**Bei parallelem Einsatz zusätzlich:**
- **Disjunkte Zieldateien.** Jeder Subagent bekommt die exakten Pfade genannt, die er anlegen darf, und die Anweisung, nichts anderes anzufassen. Gemeinsam benötigte Dateien (Paket-`__init__.py`, `.gitignore`) legt der Koordinator vorher an.
+22 -3
View File
@@ -2,17 +2,18 @@
type: resource
status: reference
title: Prompting-Hinweise für Claude Opus 5
summary: Verhaltensunterschiede und Prompt-Muster von Claude Opus 5 (Teil A) sowie Spezifikation, API-Brüche, neue Funktionen, Preise (Teil B) aus der Anthropic-Doku. Betrifft Koordinator, Planer und Prüfer.
summary: Verhaltensunterschiede und Prompt-Muster von Claude Opus 5 (Teil A) sowie Spezifikation, API-Brüche, neue Funktionen, Preise (Teil B) aus der Anthropic-Doku; Teil D nennt, was bei Opus 5.5 abweicht. Betrifft Koordinator, Planer und Prüfer.
tags: [regelwerk, modelle, opus, prompting]
created: 2026-09-05
updated: 2026-09-05
updated: 2026-09-23
source: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-opus-5
---
# Prompting-Hinweise für Claude Opus 5
> **Geltungsbereich.** Diese Datei beschreibt, wie sich **Claude Opus 5** verhält und wie man Aufträge an es formuliert. Im Vault läuft Opus 5 als Koordinator, als `planer` und als `pruefer`. Zwei Leser: (1) **Fable 5.1**, wenn es als Koordinator einen Auftrag an einen Opus-Subagenten schreibt — dann ist das hier verbindlich für die Formulierung. (2) **Opus 5 selbst** — es arbeitet nach seinen eigenen Optimierungen und entscheidet selbst, ob es einen Punkt übernimmt. Die Rollentabelle im Stamm ([[CLAUDE|Claude.md]]) bleibt unverändert.
> **Geltungsbereich.** Diese Datei beschreibt, wie sich **Claude Opus 5** verhält und wie man Aufträge an es formuliert. Im Vault liefen Koordinator, `planer` und `pruefer` auf Opus 5, bis der Alias `opus` auf Opus 5.5 wechselte (siehe unten und Teil D). Zwei Leser: (1) **Fable 5.1**, wenn es als Koordinator einen Auftrag an einen Opus-Subagenten schreibt — dann ist das hier verbindlich für die Formulierung. (2) **Opus 5 selbst** — es arbeitet nach seinen eigenen Optimierungen und entscheidet selbst, ob es einen Punkt übernimmt. Die Rollentabelle im Stamm ([[CLAUDE|Claude.md]]) bleibt unverändert.
> Angeordnet am 2026-09-05. Quellen: „Prompting Claude Opus 5" (Teil A), „What's new in Claude Opus 5" (Teil B) und der Migrationsleitfaden (Teil C), alle abgerufen am 2026-09-05. Aussagen stammen aus der Doku, nicht aus eigener Messung.
> **Seit spätestens Claude Code 2.1.280 löst der Alias `opus` auf Opus 5.5 auf, nicht mehr auf Opus 5.** Laut Anthropic bleiben die Muster dieser Datei für Opus 5.5 ein vernünftiger Ausgangspunkt. Was abweicht, steht in Teil D.
## Wozu diese Datei
@@ -215,6 +216,24 @@ Gleiche API-Oberfläche — Thinking an, Effort `high`, 1M/128k, kein Priority T
Für den Vault heißt das konkret: Ein Harness, der rundenbezogene System-Nachrichten oder Werkzeugänderungen im Gespräch nutzt, läuft so nur auf Opus 5 und Fable 5.1, nicht auf dem Executer-Modell.
---
# Teil D — Opus 5.5: was abweicht
Am 2026-09-23 nachgelesen, zum einen im Modellkatalog von Claude Code 2.1.280 (Programmdatei), zum anderen in der Anthropic-Doku. Die Doku hat ein Recherche-Agent abgerufen; die Zitate unten stammen aus seinem Bericht, nicht aus eigenem Abruf. Eine eigene Vorlage wie für Opus 5 gibt es für Opus 5.5 noch nicht.
- **Der Alias.** `opus` löst in 2.1.280 auf `claude-opus-5-5` auf. Planer, Prüfer und eine Hauptsitzung mit `opus` laufen also auf Opus 5.5.
- **Standard-Effort ist `medium`**, bei Opus 5 war es `high`. Die Doku empfiehlt, bei `medium` zu starten und Stufen neu zu messen, statt die Einstellung von Opus 5 zu übernehmen. Sie sagt, Opus 5.5 habe bei `medium` Opus 5 auf `high` erreicht oder übertroffen, „in fewer steps and with fewer tokens". Für lange agentische Arbeit über 30 Minuten nennt die Effort-Seite `xhigh`. Was im Vault gilt, steht mit eigener Messung in [[AI/Regelwerk/codeworkflow|codeworkflow.md]], „Effort je Rolle".
- **Thinking lässt sich nicht abschalten**, auf keiner Stufe. `thinking: {"type": "disabled"}` und `budget_tokens` liefern 400. Abschnitt 9 sowie die Thinking-Zeilen in B1 und B2 gelten für Opus 5.5 deshalb nicht.
- **Preis:** 4 $ Eingabe, 20 $ Ausgabe, 0,20 $ Cache-Lesen je Million Tokens (Katalog-Tarif `tier_4_20_cache_read_0_20`). Opus 5 kostet 5 $, 25 $ und 0,50 $.
- **Kontext 1M, Ausgabe 128k.** Laut Doku ist Opus 5.5 mit Zero Data Retention verfügbar.
- **Kürzer und schneller.** Laut Prompting-Seite erzeugt es Ausgabetokens über 30 Prozent schneller als Opus 5 und braucht für dieselbe Aufgabe tendenziell weniger Tokens. Die Prozentzahlen zur Kürze auf der Ankündigungsseite sind Kundenzitate, keine Messung von Anthropic.
- **Erzwungene Werkzeugwahl** (`tool_choice` mit `any` oder `tool`) liefert laut Bericht des Recherche-Agenten 400. Das hat er als Nebenfund ohne Zitat gemeldet, belegt ist es also nicht.
Nicht eigens behandelt: Über-Verifikation (Abschnitt 5), Delegieren (6) und Schwellen im Prüfauftrag (8) greift die Opus-5.5-Seite nicht noch einmal auf. Dass sie weiter gelten, ist ein Rückschluss aus dem Satz, die Opus-5-Muster blieben ein Ausgangspunkt. Ein eigener Beleg dafür fehlt. Der Review-Prüfstand vom 2026-09-23 lief ohne Schwelle im Auftrag und sagt deshalb nichts darüber.
Quellen: https://platform.claude.com/docs/en/build-with-claude/effort, https://platform.claude.com/docs/en/models/opus-5-5/whats-new-opus-5-5, https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-opus-5-5, https://platform.claude.com/docs/en/models/opus-5-5/overview (abgerufen 2026-09-23).
## Wohin die Punkte im Regelwerk zeigen
| Regelwerk-Datei | Verweist auf Abschnitt |
+2 -2
View File
@@ -5,13 +5,13 @@ title: Prompting-Hinweise für Claude Sonnet 5
summary: Verhaltensunterschiede und Prompt-Muster von Claude Sonnet 5 (Teil A) sowie Spezifikation, API-Brüche, Tokenizer, Cyber-Schutz, ZDR-Fähigkeit (Teil B) aus der Anthropic-Doku. Betrifft den Executer und die Recherche-Agenten.
tags: [regelwerk, modelle, sonnet, prompting]
created: 2026-09-05
updated: 2026-09-05
updated: 2026-09-23
source: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-sonnet-5
---
# Prompting-Hinweise für Claude Sonnet 5
> **Geltungsbereich.** Diese Datei beschreibt, wie sich **Claude Sonnet 5** verhält und wie man Aufträge an es formuliert. Im Vault läuft Sonnet 5 als `executer` und als Recherche-Agent (`Explore`, `general-purpose`). Zwei Leser: (1) **Fable 5.1**, wenn es als Koordinator einen Auftrag an einen Sonnet-Subagenten schreibt — dann ist das hier verbindlich für die Formulierung. (2) **Sonnet 5 selbst** — es arbeitet nach seinen eigenen Optimierungen und entscheidet selbst, ob es einen Punkt übernimmt. Die Rollentabelle im Stamm ([[CLAUDE|Claude.md]]) bleibt unverändert.
> **Geltungsbereich.** Diese Datei beschreibt, wie sich **Claude Sonnet 5** verhält und wie man Aufträge an es formuliert. Im Vault läuft Sonnet 5 als `executer` (Effort `medium`), als Rechercheagent `recherche` (Effort `medium`, seit 2026-09-23 statt `general-purpose`) und im eingebauten `Explore`. Zwei Leser: (1) **Fable 5.1**, wenn es als Koordinator einen Auftrag an einen Sonnet-Subagenten schreibt — dann ist das hier verbindlich für die Formulierung. (2) **Sonnet 5 selbst** — es arbeitet nach seinen eigenen Optimierungen und entscheidet selbst, ob es einen Punkt übernimmt. Die Rollentabelle im Stamm ([[CLAUDE|Claude.md]]) bleibt unverändert.
> Angeordnet am 2026-09-05. Quellen: „Prompting Claude Sonnet 5" (Teil A), „What's new in Claude Sonnet 5" (Teil B) und der Migrationsleitfaden (Teil C), alle abgerufen am 2026-09-05. Aussagen stammen aus der Doku, nicht aus eigener Messung.
## Wozu diese Datei
+2 -2
View File
@@ -5,7 +5,7 @@ title: "Veröffentlichung der Anweisungsschicht"
summary: "Welcher Teil des Vaults in das Gitea-Repo claude-regelwerk geht, wie die Positivliste funktioniert und was der automatische Push tut. Laden, bevor der veröffentlichte Umfang geändert wird oder wenn ein Auto-Push-Befund im Kontext steht."
tags: [regelwerk, git, veroeffentlichung, werkzeuge]
created: 2026-09-17
updated: 2026-09-17
updated: 2026-09-23
---
# Veröffentlichung der Anweisungsschicht
@@ -24,7 +24,7 @@ Angeordnet am 2026-09-17. Die Anweisungsschicht des Vaults liegt seitdem in eine
Die `.gitignore` im Vault-Root ist eine **Positivliste**: Sie schließt mit `/*` erst alles aus und nimmt dann einzeln wieder auf. Eine Ausschlussliste wäre hier falsch herum — sie müsste jede künftige Notiz, jeden neuen Kundenordner und jede Depotdatei vorhersehen, und die erste vergessene Zeile ist eine Veröffentlichung.
Eingeschlossen sind: `CLAUDE.md`, `README.md`, `AI/Regelwerk/`, `.claude/agents/`, `.claude/skills/`, `.claude/settings.json`, `.vault-tools/`.
Eingeschlossen sind: `CLAUDE.md`, `README.md`, `AI/Regelwerk/`, `.claude/agents/`, `.claude/skills/`, `.claude/workflows/` (seit 2026-09-23, auf Pascals Wort), `.claude/settings.json`, `.vault-tools/`.
Draußen bleibt damit alles andere, insbesondere `Inbox/`, `Code Projekte/`, `Security - Compliance Projekte/`, `Finanzen/`, `IT Workspace/`, `AI/sessions/`, `AI/secondbrain/`, `Bookstack/`, `Import/` und `.claude/settings.local.json`.
+12 -10
View File
@@ -60,15 +60,17 @@ Reichen die MOCs nicht, suchst du gezielt mit Grep. Tote Links findet `.vault-to
Rollen sind fest auf Modelle verteilt. **Beim Starten eines Subagenten immer das Modell explizit setzen** — ein weggelassenes Modell erbt still das Sitzungsmodell und hebelt diese Zuordnung aus.
| Rolle | Agent | Modell |
| -------------------------------------------- | ----------------- | ----------------------------------------------- |
| Koordinator (klären, entscheiden, committen) | Hauptsitzung | Opus 5 (`opus`) |
| Planen, Spezifizieren | `planer` | Opus 5 (`opus`) |
| Prüfen, Review, Verifikation | `pruefer` | Opus 5 (`opus`) |
| Umsetzen (Code schreiben, ändern, testen) | `executer` | Sonnet 5 (`sonnet`), bei reiner Abschrift Haiku |
| Recherche, Suche, Erkundung | `general-purpose` | Sonnet 5 (`model: "sonnet"` mitgeben) |
| Rolle | Agent | Modell | Effort |
| -------------------------------------------- | ----------------- | ----------------------------------------------- | ---------------------------------------- |
| Koordinator (klären, entscheiden, committen) | Hauptsitzung | Opus 5.5 (`opus`) | Sitzungseinstellung |
| Planen, Spezifizieren | `planer` | Opus 5.5 (`opus`) | `high` (Frontmatter) |
| Prüfen, Review, Verifikation | `pruefer` | Opus 5.5 (`opus`) | `high` (Frontmatter) |
| Umsetzen (Code schreiben, ändern, testen) | `executer` | Sonnet 5 (`sonnet`), bei reiner Abschrift Haiku | `medium` (Frontmatter); Haiku kennt keinen |
| Recherche, Suche, Erkundung | `recherche` | Sonnet 5 (`sonnet`) | `medium` (Frontmatter) |
Die Aliasse `opus` und `sonnet` lösen in dieser Umgebung auf `claude-opus-5` bzw. `claude-sonnet-5` auf. Der eingebaute Agent `Explore` lädt diese Datei nicht und kennt die Vault-Regeln deshalb nicht — nur für reine Dateisuche. Wann Haiku statt Sonnet, die Belege dazu und die Zuordnung der Superpowers-Skills: [[AI/Regelwerk/codeworkflow|codeworkflow.md]].
Die Aliasse `opus` und `sonnet` lösen in Claude Code 2.1.280 auf `claude-opus-5-5` bzw. `claude-sonnet-5` auf (Modellkatalog im Programm, nachgelesen 2026-09-23). Der eingebaute Agent `Explore` lädt diese Datei nicht und kennt die Vault-Regeln deshalb nicht — nur für reine Dateisuche; `general-purpose` erbt den Sitzungs-Effort und ist durch `recherche` ersetzt. Wann Haiku statt Sonnet, die Belege dazu und die Zuordnung der Superpowers-Skills: [[AI/Regelwerk/codeworkflow|codeworkflow.md]].
**Ein Subagent ohne eigenen Effort erbt den der Hauptsitzung.** Läuft sie auf `xhigh` oder `max`, kostet ein Sonnet-Agent laut Katalog das 2,4- bzw. 5,6-Fache von `high`. Deshalb trägt jede Agentendatei `effort:`. In Workflows bekommt jeder `agent()`-Aufruf `model`; den Effort liefert mit `agentType` dessen Frontmatter, ohne `agentType` wird er mitgegeben, und ein `effort`-Parameter neben `agentType` steht nur für eine bewusste Abweichung. Höchstens zwei Opus-Agenten gleichzeitig. Für Plankritik, Executer-Welle und Prüfung gibt es gespeicherte Workflows unter `.claude/workflows/`, in denen das eingebaut ist, dazu `welle-mit-pruefung` für Schritt 3 und 4 in einem Aufruf. Eine geänderte Agentendatei oder ein neuer gespeicherter Workflow wirkt in der laufenden Sitzung nicht sofort, sondern erst nach einer Weile (am 2026-09-23 knapp eine Stunde) oder in der nächsten Sitzung. Ob ein Effort greift, zeigt nur das Feld `effort` im Transkript; ein noch unbekannter Workflow geht so lange über `scriptPath`. Messung und Begründung je Rolle: [[AI/Regelwerk/codeworkflow|codeworkflow.md]], „Effort je Rolle".
## Regelwerk (ausgelagert, nur bei Bedarf laden)
@@ -95,7 +97,7 @@ Jede Datei triggert nur den betreffenden Teil — nicht bei jeder Aufgabe alles
## Veröffentlichung
Der Vault-Root ist ein Git-Repo und pusht **täglich um 04:00** nach `https://git.nexhorizons.de/Nexhorizons-Public/claude-regelwerk.git` — über die Windows-Aufgabe `Claude-Vault Auto-Push`, nicht über eine Cloud-Routine. Veröffentlicht wird nur die Anweisungsschicht: `CLAUDE.md`, `README.md`, `AI/Regelwerk/`, `.claude/agents/`, `.claude/skills/`, `.claude/settings.json` und `.vault-tools/`.
Der Vault-Root ist ein Git-Repo und pusht **täglich um 04:00** nach `https://git.nexhorizons.de/Nexhorizons-Public/claude-regelwerk.git` — über die Windows-Aufgabe `Claude-Vault Auto-Push`, nicht über eine Cloud-Routine. Veröffentlicht wird nur die Anweisungsschicht: `CLAUDE.md`, `README.md`, `AI/Regelwerk/`, `.claude/agents/`, `.claude/skills/`, `.claude/workflows/`, `.claude/settings.json` und `.vault-tools/`.
Die `.gitignore` im Root ist eine **Positivliste** — sie schließt erst alles aus und nimmt dann einzeln wieder auf. Notizen, Projekte, Finanzen, Sitzungen und `settings.local.json` bleiben damit draußen, auch wenn morgen ein neuer Ordner dazukommt.
@@ -119,7 +121,7 @@ Nach jeder Änderung an Stamm, Regelwerk, Skills oder Agenten läuft die Prüfun
python .vault-tools/regelwerk-check/regelwerk_check.py --vault .
```
Sechs formale Prüfungen: tote Wikilinks, Frontmatter-Pflichtfelder, ob jede Regelwerk-Datei vom Stamm aus verlinkt ist, offene `pending-review`-Freigaben, `model:` gegen die Rollentabelle oben, und die Planablage-Konvention. **Das ist ein Fangnetz, kein Nachweis** — inhaltliche Widersprüche sieht es nicht. Geltungsbereich, benannte Lücken und die belegte Angriffsliste: `.vault-tools/regelwerk-check/ENTWURF.md`.
Sechs formale Prüfungen: tote Wikilinks, Frontmatter-Pflichtfelder, ob jede Regelwerk-Datei vom Stamm aus verlinkt ist, offene `pending-review`-Freigaben, `model:` und `effort:` gegen die Rollentabelle oben, und die Planablage-Konvention. **Das ist ein Fangnetz, kein Nachweis** — inhaltliche Widersprüche sieht es nicht. Geltungsbereich, benannte Lücken und die belegte Angriffsliste: `.vault-tools/regelwerk-check/ENTWURF.md`.
## Sitzungsprotokoll
Beginne jede Sitzung wie folgt. Die Zahlen zu den Schritten 1 bis 3 liefert der SessionStart-Hook schon in den Kontext; lesen musst du trotzdem selbst.