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
d7ffc9a5b5
commit
67d674cd0b
32 files changed
+1618
-88
No files matched your search
@@ -20,7 +20,7 @@ Sechs Prüfungen, jede über eine GESCHLOSSENE Menge:
|
||||
2 Frontmatter Trägt jede Regelwerk-Datei die sieben Pflichtfelder?
|
||||
3 Erreichbarkeit Ist jede Regelwerk-Datei vom Stamm aus verlinkt?
|
||||
4 Freigabe Steht eine Regelwerk-Datei auf status: pending-review?
|
||||
5 Modell Weicht model: einer Agentendatei von der Rollentabelle ab?
|
||||
5 Modell/Effort Weichen model: oder effort: einer Agentendatei von der Rollentabelle ab?
|
||||
6 Planablage Nennt eine Datei die Dateiform ohne die Ordnerform?
|
||||
|
||||
Die Rollentabelle und die Liste der Regelwerk-Verweise werden AUS CLAUDE.md
|
||||
@@ -36,8 +36,12 @@ WAS DIESE PRÜFUNG NICHT SIEHT — benannte Lücken
|
||||
eine Heuristik im Positivlisten-Gewand. Bewusst weggelassen.
|
||||
|
||||
* Modellaussagen im Fließtext. "(`executer`, Sonnet — bei reiner Abschrift
|
||||
Haiku)" ist für einen Menschen eindeutig und für ein Skript nicht. Geprüft
|
||||
wird ausschließlich das model:-Feld im Frontmatter.
|
||||
Haiku)" ist für einen Menschen eindeutig und für ein Skript nicht.
|
||||
Geprüft werden ausschließlich die Felder model: und effort: im
|
||||
Frontmatter.
|
||||
|
||||
* Ein YAML-Kommentar hinter dem Wert (effort: high # …) wird nicht
|
||||
abgeschnitten und ergibt einen Befund; für model: galt das schon vorher.
|
||||
|
||||
* INHALTLICHE Widersprüche. Zwei Dateien können dieselbe Regel in
|
||||
verschiedenen Worten gegensätzlich formulieren, und diese Prüfung meldet
|
||||
@@ -46,6 +50,23 @@ WAS DIESE PRÜFUNG NICHT SIEHT — benannte Lücken
|
||||
aufgefallen: die Planablage (Prüfung 6) ja, "fortsetzen statt frisch
|
||||
starten" nein.
|
||||
|
||||
* effort: bei model: haiku. Haiku 4.5 kennt keinen Effort; Claude Code
|
||||
sendet ihn dann nicht (Claude Code 2.1.280, Programmdatei, nachgelesen
|
||||
2026-09-23). Die Rollentabelle führt keinen Agenten auf Haiku; die
|
||||
Abschrift-Stufe steht im Task-Frontmatter, nicht in einer Agentendatei.
|
||||
|
||||
* Doppelter Schlüssel effort:. Der flache Frontmatter-Leser nimmt den
|
||||
letzten Wert, wie bei model:.
|
||||
|
||||
* Ob Claude Code den Wert anwendet. Das zeigt nur das Feld effort im
|
||||
Transkript eines echten Laufs, und eine geänderte Agentendatei wirkt
|
||||
in der laufenden Sitzung nicht sofort. Belegt am 2026-09-23 in frischen
|
||||
Sitzungen über das Agent-Werkzeug und im Workflow.
|
||||
|
||||
* effort in gespeicherten Workflows unter .claude/workflows/. Sie setzen
|
||||
den Effort mit agentType absichtlich nicht; eine Abweichung dort sieht
|
||||
diese Prüfung nicht.
|
||||
|
||||
===============================================================================
|
||||
DIESES SKRIPT IST EIN FANGNETZ, KEIN NACHWEIS.
|
||||
===============================================================================
|
||||
@@ -103,6 +124,11 @@ def wikilinks(text: str) -> list[str]:
|
||||
PLANPFAD = re.compile(r"docs/plans/JJJJ-MM-TT[^\s`]*")
|
||||
BACKTICK = re.compile(r"`([^`]+)`")
|
||||
|
||||
# Die Stufen stammen aus Claude Code 2.1.280 (Programmdatei, nachgelesen
|
||||
# 2026-09-23). Claude Code selbst nimmt zusätzlich Großschreibung, "med"
|
||||
# und ganze Zahlen an; die Vorgabe hier ist bewusst enger.
|
||||
EFFORT_WERTE = ("low", "medium", "high", "xhigh", "max")
|
||||
|
||||
|
||||
class Befund:
|
||||
def __init__(self, schwere: str, ort: str, text: str) -> None:
|
||||
@@ -163,7 +189,7 @@ def rollentabelle(text: str) -> dict[str, str]:
|
||||
"""Liest die Zuordnung Agent -> Modellalias aus der Tabelle im Stamm.
|
||||
|
||||
Eine Zeile sieht so aus:
|
||||
| Planen, Spezifizieren | `planer` | Opus 5 (`opus`) |
|
||||
| Planen, Spezifizieren | `planer` | Opus 5.5 (`opus`) | `high` (Frontmatter) |
|
||||
Genommen wird der erste Backtick-Ausdruck der Agenten- und der
|
||||
Modellspalte. Zeilen ohne Backticks in der Agentenspalte (etwa
|
||||
'Hauptsitzung') werden übersprungen.
|
||||
@@ -184,6 +210,52 @@ def rollentabelle(text: str) -> dict[str, str]:
|
||||
return zuordnung
|
||||
|
||||
|
||||
def entquotet(wert: str) -> str:
|
||||
"""Nimmt passende einfache oder doppelte Anführungszeichen ab: 'high' -> high."""
|
||||
if len(wert) >= 2 and wert[0] == wert[-1] and wert[0] in ("'", '"'):
|
||||
return wert[1:-1]
|
||||
return wert
|
||||
|
||||
|
||||
def effort_spalte(text: str) -> int | None:
|
||||
"""Findet den Index der Effort-Spalte in der Rollentabelle im Stamm.
|
||||
|
||||
Gesucht wird die Tabellenzeile, deren Zellen (getrimmt) sowohl 'Agent'
|
||||
als auch 'Effort' enthalten — das ist die Kopfzeile. Der Index der Zelle
|
||||
'Effort' ist die Effort-Spalte.
|
||||
"""
|
||||
for zeile in text.split("\n"):
|
||||
if not zeile.startswith("|"):
|
||||
continue
|
||||
spalten = [s.strip() for s in zeile.strip("|").split("|")]
|
||||
if "Agent" in spalten and "Effort" in spalten:
|
||||
return spalten.index("Effort")
|
||||
return None
|
||||
|
||||
|
||||
def rollentabelle_effort(text: str, spalte: int) -> dict[str, str]:
|
||||
"""Liest den Effort je Agent aus der Rollentabelle im Stamm.
|
||||
|
||||
Genommen wird der erste Backtick-Ausdruck der Effort-Spalte (`spalte`).
|
||||
Fehlt ein Backtick-Ausdruck, ist der Wert für diesen Agenten leer.
|
||||
"""
|
||||
zuordnung: dict[str, str] = {}
|
||||
for zeile in text.split("\n"):
|
||||
if not zeile.startswith("|"):
|
||||
continue
|
||||
spalten = [s.strip() for s in zeile.strip("|").split("|")]
|
||||
if len(spalten) <= spalte:
|
||||
continue
|
||||
agenten = BACKTICK.findall(spalten[1])
|
||||
if not agenten:
|
||||
continue
|
||||
effort_treffer = BACKTICK.findall(spalten[spalte])
|
||||
wert = effort_treffer[0].strip() if effort_treffer else ""
|
||||
for name in agenten:
|
||||
zuordnung[name.strip()] = wert
|
||||
return zuordnung
|
||||
|
||||
|
||||
def stamm_regelwerkverweise(text: str) -> set[str]:
|
||||
"""Alle im Stamm verlinkten Regelwerk-Dateien, als Dateiname ohne Endung."""
|
||||
namen: set[str] = set()
|
||||
@@ -252,7 +324,13 @@ def pruefe_freigabe(vault: Path, befunde: list[Befund]) -> None:
|
||||
)
|
||||
|
||||
|
||||
def pruefe_modelle(vault: Path, tabelle: dict[str, str], befunde: list[Befund]) -> None:
|
||||
def pruefe_modelle(
|
||||
vault: Path,
|
||||
tabelle: dict[str, str],
|
||||
effort_index: int | None,
|
||||
effort_tabelle: dict[str, str],
|
||||
befunde: list[Befund],
|
||||
) -> None:
|
||||
for datei in sorted((vault / AGENTEN).glob("*.md")):
|
||||
rel = datei.relative_to(vault).as_posix()
|
||||
fm = frontmatter(lies(datei))
|
||||
@@ -278,6 +356,47 @@ def pruefe_modelle(vault: Path, tabelle: dict[str, str], befunde: list[Befund])
|
||||
)
|
||||
)
|
||||
|
||||
# Effort: nur prüfbar, wenn die Effort-Spalte der Rollentabelle
|
||||
# gefunden wurde (siehe main() für den Befund am Stamm, falls nicht).
|
||||
if effort_index is None:
|
||||
continue
|
||||
soll = effort_tabelle.get(name, "")
|
||||
if not soll:
|
||||
befunde.append(
|
||||
Befund("wichtig", "CLAUDE.md", f"Rollentabelle nennt für '{name}' keinen Effort")
|
||||
)
|
||||
effort_roh = fm.get("effort")
|
||||
if not effort_roh:
|
||||
befunde.append(
|
||||
Befund(
|
||||
"wichtig",
|
||||
rel,
|
||||
"kein effort: im Frontmatter — der Agent erbt den Effort der Hauptsitzung",
|
||||
)
|
||||
)
|
||||
continue
|
||||
effort_wert = entquotet(effort_roh)
|
||||
if not effort_wert:
|
||||
befunde.append(
|
||||
Befund(
|
||||
"wichtig",
|
||||
rel,
|
||||
"kein effort: im Frontmatter — der Agent erbt den Effort der Hauptsitzung",
|
||||
)
|
||||
)
|
||||
elif effort_wert not in EFFORT_WERTE:
|
||||
befunde.append(
|
||||
Befund(
|
||||
"blockierend",
|
||||
rel,
|
||||
f"effort: {effort_wert} ist nicht in der Vorgabe (erlaubt: low, medium, high, xhigh, max)",
|
||||
)
|
||||
)
|
||||
elif soll and effort_wert != soll:
|
||||
befunde.append(
|
||||
Befund("blockierend", rel, f"effort: {effort_wert} — Rollentabelle im Stamm sagt {soll}")
|
||||
)
|
||||
|
||||
|
||||
def pruefe_planablage(vault: Path, dateien: list[Path], befunde: list[Befund]) -> None:
|
||||
for datei in dateien:
|
||||
@@ -339,6 +458,17 @@ def main() -> int:
|
||||
Befund("blockierend", "CLAUDE.md", "keine Regelwerk-Verweise gefunden — Prüfung 3 kann nicht laufen")
|
||||
)
|
||||
|
||||
effort_index = effort_spalte(stammtext) if tabelle else None
|
||||
if tabelle and effort_index is None:
|
||||
befunde.append(
|
||||
Befund(
|
||||
"blockierend",
|
||||
"CLAUDE.md",
|
||||
"Effort-Spalte der Rollentabelle nicht auffindbar — Prüfung 5 kann den Effort nicht prüfen",
|
||||
)
|
||||
)
|
||||
effort_tabelle = rollentabelle_effort(stammtext, effort_index) if effort_index is not None else {}
|
||||
|
||||
dateien = dateien_im_geltungsbereich(vault)
|
||||
|
||||
pruefe_wikilinks(vault, dateien, befunde); gelaufen += 1
|
||||
@@ -347,7 +477,7 @@ def main() -> int:
|
||||
pruefe_erreichbarkeit(vault, verlinkt, befunde); gelaufen += 1
|
||||
pruefe_freigabe(vault, befunde); gelaufen += 1
|
||||
if tabelle and (vault / AGENTEN).is_dir():
|
||||
pruefe_modelle(vault, tabelle, befunde); gelaufen += 1
|
||||
pruefe_modelle(vault, tabelle, effort_index, effort_tabelle, befunde); gelaufen += 1
|
||||
pruefe_planablage(vault, dateien, befunde); gelaufen += 1
|
||||
|
||||
except SystemExit:
|
||||
|
||||
Reference in new issue
Block a user