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:
1 parent
b7cc7433a9
commit
c5da36cb41
18 files changed
+1055
-35
No files matched your search
@@ -2,6 +2,7 @@
|
|||||||
name: executer
|
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.
|
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
|
model: sonnet
|
||||||
|
effort: medium
|
||||||
tools: Read, Write, Edit, Bash, Glob, Grep, TodoWrite
|
tools: Read, Write, Edit, Bash, Glob, Grep, TodoWrite
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
name: planer
|
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.
|
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
|
model: opus
|
||||||
|
effort: high
|
||||||
tools: Read, Write, Glob, Grep, TodoWrite
|
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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
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
|
## Nur wenn du als Claude Fable 5.1 läufst
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
name: pruefer
|
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.
|
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
|
model: opus
|
||||||
|
effort: high
|
||||||
tools: Read, Glob, Grep, Bash, TodoWrite
|
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.
|
- **Verdrehte Vorzeichen, Skalierung, Wertebereiche.** Sichtbar erst bei realistischen Datenmengen.
|
||||||
- **Stille Abweichungen vom Plan.** Was tut der Code, was der Plan nicht vorsah — und umgekehrt.
|
- **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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
Ruder, sobald eine von beiden gepflegt wird — und die Kopie gewinnt, weil sie zuerst geladen
|
||||||
wird.
|
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:
|
Danach je nach Aufgabe zusätzlich:
|
||||||
|
|
||||||
- `AI/Regelwerk/codedoku.md`, sobald Produktivdateien entstehen oder angefasst werden.
|
- `AI/Regelwerk/codedoku.md`, sobald Produktivdateien entstehen oder angefasst werden.
|
||||||
|
|||||||
@@ -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.',
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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]) }
|
||||||
@@ -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.',
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
!/.claude/settings.json
|
!/.claude/settings.json
|
||||||
!/.claude/agents/
|
!/.claude/agents/
|
||||||
!/.claude/skills/
|
!/.claude/skills/
|
||||||
|
!/.claude/workflows/
|
||||||
|
|
||||||
!/.vault-tools/
|
!/.vault-tools/
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
→ **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
|
## 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.
|
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 |
|
| leer | Regelwerk-Datei auf 0 Byte gekürzt |
|
||||||
| zusätzlich | neue Regelwerk-Datei, die niemand verlinkt |
|
| zusätzlich | neue Regelwerk-Datei, die niemand verlinkt |
|
||||||
| Wertebereich | `model: sonnet` in `pruefer.md` (Tabelle sagt `opus`) |
|
| 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 |
|
| Verweis ins Leere | Wikilink auf nicht existierende Datei |
|
||||||
| Konvention | Dateiform der Planablage ohne Ordnerform |
|
| Konvention | Dateiform der Planablage ohne Ordnerform |
|
||||||
| **Reihenfolge** | toter Wikilink an **erster** und an **letzter** Position derselben Datei |
|
| **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
|
## 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
|
## Was diese Prüfung ausdrücklich nicht ist
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Sechs Prüfungen, jede über eine GESCHLOSSENE Menge:
|
|||||||
2 Frontmatter Trägt jede Regelwerk-Datei die sieben Pflichtfelder?
|
2 Frontmatter Trägt jede Regelwerk-Datei die sieben Pflichtfelder?
|
||||||
3 Erreichbarkeit Ist jede Regelwerk-Datei vom Stamm aus verlinkt?
|
3 Erreichbarkeit Ist jede Regelwerk-Datei vom Stamm aus verlinkt?
|
||||||
4 Freigabe Steht eine Regelwerk-Datei auf status: pending-review?
|
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?
|
6 Planablage Nennt eine Datei die Dateiform ohne die Ordnerform?
|
||||||
|
|
||||||
Die Rollentabelle und die Liste der Regelwerk-Verweise werden AUS CLAUDE.md
|
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.
|
eine Heuristik im Positivlisten-Gewand. Bewusst weggelassen.
|
||||||
|
|
||||||
* Modellaussagen im Fließtext. "(`executer`, Sonnet — bei reiner Abschrift
|
* Modellaussagen im Fließtext. "(`executer`, Sonnet — bei reiner Abschrift
|
||||||
Haiku)" ist für einen Menschen eindeutig und für ein Skript nicht. Geprüft
|
Haiku)" ist für einen Menschen eindeutig und für ein Skript nicht.
|
||||||
wird ausschließlich das model:-Feld im Frontmatter.
|
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
|
* INHALTLICHE Widersprüche. Zwei Dateien können dieselbe Regel in
|
||||||
verschiedenen Worten gegensätzlich formulieren, und diese Prüfung meldet
|
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
|
aufgefallen: die Planablage (Prüfung 6) ja, "fortsetzen statt frisch
|
||||||
starten" nein.
|
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.
|
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`]*")
|
PLANPFAD = re.compile(r"docs/plans/JJJJ-MM-TT[^\s`]*")
|
||||||
BACKTICK = re.compile(r"`([^`]+)`")
|
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:
|
class Befund:
|
||||||
def __init__(self, schwere: str, ort: str, text: str) -> None:
|
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.
|
"""Liest die Zuordnung Agent -> Modellalias aus der Tabelle im Stamm.
|
||||||
|
|
||||||
Eine Zeile sieht so aus:
|
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
|
Genommen wird der erste Backtick-Ausdruck der Agenten- und der
|
||||||
Modellspalte. Zeilen ohne Backticks in der Agentenspalte (etwa
|
Modellspalte. Zeilen ohne Backticks in der Agentenspalte (etwa
|
||||||
'Hauptsitzung') werden übersprungen.
|
'Hauptsitzung') werden übersprungen.
|
||||||
@@ -184,6 +210,52 @@ def rollentabelle(text: str) -> dict[str, str]:
|
|||||||
return zuordnung
|
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]:
|
def stamm_regelwerkverweise(text: str) -> set[str]:
|
||||||
"""Alle im Stamm verlinkten Regelwerk-Dateien, als Dateiname ohne Endung."""
|
"""Alle im Stamm verlinkten Regelwerk-Dateien, als Dateiname ohne Endung."""
|
||||||
namen: set[str] = set()
|
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")):
|
for datei in sorted((vault / AGENTEN).glob("*.md")):
|
||||||
rel = datei.relative_to(vault).as_posix()
|
rel = datei.relative_to(vault).as_posix()
|
||||||
fm = frontmatter(lies(datei))
|
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:
|
def pruefe_planablage(vault: Path, dateien: list[Path], befunde: list[Befund]) -> None:
|
||||||
for datei in dateien:
|
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")
|
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)
|
dateien = dateien_im_geltungsbereich(vault)
|
||||||
|
|
||||||
pruefe_wikilinks(vault, dateien, befunde); gelaufen += 1
|
pruefe_wikilinks(vault, dateien, befunde); gelaufen += 1
|
||||||
@@ -347,7 +477,7 @@ def main() -> int:
|
|||||||
pruefe_erreichbarkeit(vault, verlinkt, befunde); gelaufen += 1
|
pruefe_erreichbarkeit(vault, verlinkt, befunde); gelaufen += 1
|
||||||
pruefe_freigabe(vault, befunde); gelaufen += 1
|
pruefe_freigabe(vault, befunde); gelaufen += 1
|
||||||
if tabelle and (vault / AGENTEN).is_dir():
|
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
|
pruefe_planablage(vault, dateien, befunde); gelaufen += 1
|
||||||
|
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
|
|||||||
@@ -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
|
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
|
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,
|
Danach je Angriffsfall: frische Kopie der Grundlinie, genau eine Beschädigung,
|
||||||
Prüfung fahren, Exit-Code und auslösende Meldung protokollieren. Ein Fall gilt
|
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)
|
unter.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copy2(skill, unter / "SKILL.md")
|
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,
|
# Jedes Linkziel außerhalb des Geltungsbereichs als leere Datei anlegen,
|
||||||
# damit die Grundlinie nicht an fehlendem Umfeld scheitert.
|
# damit die Grundlinie nicht an fehlendem Umfeld scheitert.
|
||||||
for datei in list(ziel.rglob("*.md")):
|
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")
|
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:
|
def g_link_im_codeblock(v: Path) -> None:
|
||||||
"""Kein Schaden, sondern eine Falle: Wikilink-artiges IN Code.
|
"""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).
|
# seinen eigenen Fall (pruefwerkzeuge.md, Abschnitt 5).
|
||||||
GEGENFAELLE = [
|
GEGENFAELLE = [
|
||||||
("codeblock | Wikilink-artiges in Code und Backticks", g_link_im_codeblock),
|
("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 | CLAUDE.md fehlt", s_kein_stamm, "fehlt — nichts prüfbar"),
|
||||||
("umgebung | AI/Regelwerk fehlt", s_kein_regelwerk, "Verzeichnis fehlt"),
|
("umgebung | AI/Regelwerk fehlt", s_kein_regelwerk, "Verzeichnis fehlt"),
|
||||||
("umgebung | Rollentabelle unauffindbar", s_keine_rollentabelle, "Rollentabelle nicht auffindbar"),
|
("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"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
type: resource
|
type: resource
|
||||||
status: reference
|
status: reference
|
||||||
title: Codeworkflow
|
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]
|
tags: [regelwerk, codeworkflow, code-projekte]
|
||||||
created: 2026-08-03
|
created: 2026-08-03
|
||||||
updated: 2026-09-20
|
updated: 2026-09-23
|
||||||
---
|
---
|
||||||
|
|
||||||
# Codeworkflow
|
# 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.
|
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:
|
**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.
|
- 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.
|
- **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.
|
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.
|
**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
|
### 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.
|
**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` |
|
| `subagent-driven-development` → Task Reviewer / Re-Review | `pruefer` | `opus` |
|
||||||
| `requesting-code-review` → Code Reviewer | `pruefer` | `opus` |
|
| `requesting-code-review` → Code Reviewer | `pruefer` | `opus` |
|
||||||
| `verification-before-completion` | `pruefer` | `opus` |
|
| `verification-before-completion` | `pruefer` | `opus` |
|
||||||
| `dispatching-parallel-agents` (Recherche) | `Explore` / `general-purpose` | `sonnet` |
|
| `dispatching-parallel-agents` (Recherche) | `recherche`; reine Dateisuche `Explore` | `sonnet` |
|
||||||
| `systematic-debugging` → Hypothese bilden | Koordinator selbst | Opus 5 |
|
| `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.
|
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".
|
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:**
|
**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.
|
- **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
@@ -2,17 +2,18 @@
|
|||||||
type: resource
|
type: resource
|
||||||
status: reference
|
status: reference
|
||||||
title: Prompting-Hinweise für Claude Opus 5
|
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]
|
tags: [regelwerk, modelle, opus, prompting]
|
||||||
created: 2026-09-05
|
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
|
source: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-opus-5
|
||||||
---
|
---
|
||||||
|
|
||||||
# Prompting-Hinweise für 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.
|
> 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
|
## 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.
|
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
|
## Wohin die Punkte im Regelwerk zeigen
|
||||||
|
|
||||||
| Regelwerk-Datei | Verweist auf Abschnitt |
|
| Regelwerk-Datei | Verweist auf Abschnitt |
|
||||||
|
|||||||
@@ -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.
|
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]
|
tags: [regelwerk, modelle, sonnet, prompting]
|
||||||
created: 2026-09-05
|
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
|
source: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-sonnet-5
|
||||||
---
|
---
|
||||||
|
|
||||||
# Prompting-Hinweise für 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.
|
> 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
|
## Wozu diese Datei
|
||||||
|
|||||||
@@ -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."
|
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]
|
tags: [regelwerk, git, veroeffentlichung, werkzeuge]
|
||||||
created: 2026-09-17
|
created: 2026-09-17
|
||||||
updated: 2026-09-17
|
updated: 2026-09-23
|
||||||
---
|
---
|
||||||
|
|
||||||
# Veröffentlichung der Anweisungsschicht
|
# 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.
|
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`.
|
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`.
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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 |
|
| Rolle | Agent | Modell | Effort |
|
||||||
| -------------------------------------------- | ----------------- | ----------------------------------------------- |
|
| -------------------------------------------- | ----------------- | ----------------------------------------------- | ---------------------------------------- |
|
||||||
| Koordinator (klären, entscheiden, committen) | Hauptsitzung | Opus 5 (`opus`) |
|
| Koordinator (klären, entscheiden, committen) | Hauptsitzung | Opus 5.5 (`opus`) | Sitzungseinstellung |
|
||||||
| Planen, Spezifizieren | `planer` | Opus 5 (`opus`) |
|
| Planen, Spezifizieren | `planer` | Opus 5.5 (`opus`) | `high` (Frontmatter) |
|
||||||
| Prüfen, Review, Verifikation | `pruefer` | Opus 5 (`opus`) |
|
| Prüfen, Review, Verifikation | `pruefer` | Opus 5.5 (`opus`) | `high` (Frontmatter) |
|
||||||
| Umsetzen (Code schreiben, ändern, testen) | `executer` | Sonnet 5 (`sonnet`), bei reiner Abschrift Haiku |
|
| Umsetzen (Code schreiben, ändern, testen) | `executer` | Sonnet 5 (`sonnet`), bei reiner Abschrift Haiku | `medium` (Frontmatter); Haiku kennt keinen |
|
||||||
| Recherche, Suche, Erkundung | `general-purpose` | Sonnet 5 (`model: "sonnet"` mitgeben) |
|
| 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)
|
## Regelwerk (ausgelagert, nur bei Bedarf laden)
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ Jede Datei triggert nur den betreffenden Teil — nicht bei jeder Aufgabe alles
|
|||||||
|
|
||||||
## Veröffentlichung
|
## 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.
|
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 .
|
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
|
## 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.
|
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.
|
||||||
|
|||||||
Reference in new issue
Block a user