Erste Fassung: Claude-Werkzeuge aus dem Vault

.vault-tools (ohne trading-plattform-pruefung und watermarks-remover),
.claude/agents, .claude/skills, .claude/workflows, .claude/settings.json.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-10-14 11:27:59 +02:00
commit fa1c26b407
137 files changed
+43926

No files matched your search

@@ -0,0 +1,229 @@
---
type: plan
title: "Sitzungs-Hooks und Schließzaun im Linkcheck"
status: active
summary: "Nach dem Plan gibt ein SessionStart-Hook den Stand des Sitzungsprotokolls als Kontext aus, ein PostToolUse-Hook startet den Regelwerk-Check nach Änderungen an der Anweisungsschicht, und der Linkcheck schließt Codeblöcke nach CommonMark."
tags: [plan, vault-tools]
created: 2026-09-13
updated: 2026-09-13
---
# Sitzungs-Hooks und Schließzaun im Linkcheck
## Ziel
Nach diesem Plan liegen zwei Hook-Skripte unter `.vault-tools/session-hooks/`, jedes mit Regressionstreiber. `sitzungsstart.py` zählt zu Sitzungsbeginn, was das Sitzungsprotokoll in `CLAUDE.md` verlangt, und gibt es als Kontext aus. `regelwerk_nach_edit.py` startet `regelwerk_check.py`, sobald eine Datei von Stamm, Regelwerk, Agenten oder Skills geändert wurde, und meldet Befunde, eine fehlende, abstürzende oder hängende Prüfung. Außerdem schließt `strip_code()` im Linkcheck einen Codeblock nur noch mit einem passenden Zaun. Damit verschwindet der Fehlalarm `[[:space:]]` in `Code Projekte/active - Logging/docs/plans/2026-09-08-a-betriebsmodelle/task-09-gate.md`. Wirksam in Claude Code werden die Hooks erst mit dem Eintrag in `settings.json`, und der ist nicht Teil dieses Plans.
## Nicht im Umfang
- Einträge in `.claude/settings.json` oder `.claude/settings.local.json`. Die macht der Koordinator nach der Prüfung.
- Jede Änderung außerhalb von `.vault-tools/session-hooks/` und `.vault-tools/linkcheck/`. Auch der Kommentar in `regelwerk_check.py`, der die Zahl der Linkcheck-Fälle nennt, bleibt stehen.
- Die Öffnerregel des Linkchecks (Backtick im Info-String). Sie ist im Entwurf als Lücke benannt.
- `CHANGELOG.md`: `.vault-tools/` führt keins.
## Architektur
```text
Claude Code
SessionStart ──stdin JSON──> sitzungsstart.py ──stdout JSON──> additionalContext
│
└─ importiert strip_code() aus linkcheck/linkcheck.py
PostToolUse (Edit|Write|MultiEdit)
──stdin JSON──> regelwerk_nach_edit.py
│ Pfad im Geltungsbereich? nein -> still
└─ startet regelwerk-check/regelwerk_check.py --vault <wurzel>
ohne Befund -> still
sonst -> stdout JSON (additionalContext, systemMessage)
linkcheck/linkcheck.py strip_code() <── auch von regelwerk_check.py importiert
```
Beide Hooks enden immer mit Exit 0. Entscheidungen und Herkunft stehen in `.vault-tools/session-hooks/ENTWURF.md` (entsteht in Task 1), die Regel zum Schließzaun in `.vault-tools/linkcheck/ENTWURF-codeblock-erkennung.md` (Task 3).
## Globale Vorgaben
Diese Vorgaben gelten für jeden Task. Die Taskdateien verweisen hierher und wiederholen sie nicht.
1. **Abschrift, keine Autorschaft.** Code und Texte aus dem Plan werden Zeichen für Zeichen übernommen. Wer einen Widerspruch im Plan findet, meldet ihn und löst ihn nicht still auf. Kommentare und Docstrings werden nicht umformuliert.
2. **Test zuerst.** Erst den Treiber oder die Fälle anlegen, den Fehlschlag im Wortlaut beobachten und melden, dann die Umsetzung. Weicht der Fehlschlag vom Plan ab, anhalten und melden.
3. **Nur die Zieldateien des eigenen Tasks.** Keine weiteren Dateien, keine Hilfsskripte im Vault. Temporäres gehört ins Temp-Verzeichnis, das der Treiber selbst anlegt und löscht.
4. **Shell.** PowerShell, Arbeitsverzeichnis `D:\Claude`. Vor jedem Python-Aufruf `$env:PYTHONIOENCODING = "utf-8"`. Interpreter ist `python`, nie `python3`. Kein `bash`, kein Heredoc, kein `python -c`, kein `python -`.
5. **Dateien.** UTF-8 ohne BOM, Zeilenende LF. Nach dem Anlegen oder Ändern einer Python-Datei `python -m py_compile <datei>`.
6. **Nur Standardbibliothek**, Python 3.13.
7. **Kein Commit.** Der Vault-Root ist kein Git-Repository, und Subagenten committen nie.
8. **Keine Geheimnisse lesen.** `.claude/settings.json`, `.claude/settings.local.json` und Transkripte werden nicht geöffnet.
9. **Schlägt ein Fall fehl, wird nicht am Code gedreht, bis er grün ist.** Gemeldet werden Fall, vollständige Ausgabe und die vermutete Ursache. Die Korrektur passiert im Plan.
## Dateiübersicht
| Datei | Task | Art |
|---|---|---|
| `.vault-tools/session-hooks/sitzungsstart.py` | 1 | neu |
| `.vault-tools/session-hooks/faelle_sitzungsstart.py` | 1 | neu |
| `.vault-tools/session-hooks/ENTWURF.md` | 1 | neu |
| `.vault-tools/session-hooks/regelwerk_nach_edit.py` | 2 | neu |
| `.vault-tools/session-hooks/faelle_nach_edit.py` | 2 | neu |
| `.vault-tools/linkcheck/linkcheck.py` | 3 | ändern |
| `.vault-tools/linkcheck/faelle.py` | 3 | ändern |
| `.vault-tools/linkcheck/ENTWURF-codeblock-erkennung.md` | 3 | ändern |
## Tasks
| Nr | Datei | Zieldateien | Modell | parallel mit |
|---|---|---|---|---|
| 1 | `task-01-sitzungsstart.md` | `sitzungsstart.py`, `faelle_sitzungsstart.py`, `ENTWURF.md` in `session-hooks/` | sonnet | 2, 3 |
| 2 | `task-02-regelwerk-nach-edit.md` | `regelwerk_nach_edit.py`, `faelle_nach_edit.py` in `session-hooks/` | sonnet | 1, 3 |
| 3 | `task-03-schliesszaun.md` | `linkcheck.py`, `faelle.py`, `ENTWURF-codeblock-erkennung.md` in `linkcheck/` | haiku | 1, 2 |
Die Zieldateien sind disjunkt, alle drei Tasks können gleichzeitig laufen. Eine Abhängigkeit gibt es zur Laufzeit: `sitzungsstart.py` importiert `strip_code()` aus `linkcheck.py`, das Task 3 ändert. Die Fälle von Task 1 hängen nicht von der Schließzaun-Regel ab, ein Lauf während einer halb geschriebenen Datei kann aber scheitern. Deshalb wiederholt die Abnahme den Treiber von Task 1 nach dem Ende von Task 3. Wer Nebenläufigkeit ganz vermeiden will, startet Task 3 zuerst, er ist der kleinste.
## Vor dem Start (Koordinator)
Den Linkcheck einmal vor Task 3 laufen lassen und die Ausgabe sichern, sonst fehlt der Vergleich:
```powershell
Set-Location D:\Claude
$env:PYTHONIOENCODING = "utf-8"
python .vault-tools\linkcheck\linkcheck.py --vault . --alle > "$env:TEMP\linkcheck-vorher.txt"; "exit=$LASTEXITCODE"
```
Laut Sitzungsprotokoll `AI/sessions/2026-09-13-18.md` meldete er 3 tote Links bei 1006 Notizen, einer davon der Fehlalarm in `task-09-gate.md`.
## Abnahme
1. Die Verifikationsbefehle aller drei Taskdateien selbst wiederholen, nicht aus dem Bericht übernehmen.
2. Den Treiber von Task 1 nach dem Ende von Task 3 noch einmal fahren.
3. Jede neu angelegte Datei byteweise gegen ihren Codeblock im Plan vergleichen, bei Task 3 jede ersetzte Stelle gegen den Neutext. `.vault-tools/plan-diff/plan_diff.py` erwartet eine andere Form der Pfadzeile, als diese Taskdateien sie haben. Ob es hier trägt, ist nicht geprüft, im Zweifel von Hand vergleichen.
4. Je Treiber mindestens einen Fall absichtlich kaputt machen und rot sehen. In `sitzungsstart.py` `(?<!\S)` aus `_NEEDS_REVIEW` entfernen: Der Fall „aehnliche Tags zaehlen nicht" muss fallen. In `regelwerk_nach_edit.py` das `+ "/"` im Präfixvergleich streichen: Der Fall „Datei neben dem Vault mit gleichem Namensanfang" muss fallen. Der Fall „Nachbarordner mit gleichem Namensanfang" bleibt bei diesem Eingriff grün, weil der Rest des Pfads nicht passt; das ist kein blinder Fleck. In `faelle.py` von Task 3 genügt der Blick auf die drei Fälle, die vor der Umsetzung rot waren. Danach alles zurücksetzen.
5. Lauf mit echten Daten wie unten.
## Lauf mit echten Daten
**Task 1.** `python .vault-tools\session-hooks\faelle_sitzungsstart.py --echt` zweimal. Stand beim Planen, zum Vergleich:
- Tagesnotiz `Inbox/2026-09-13.md` vorhanden.
- Inbox ohne `type: daily`: `2026-09-13-vernetzung-vault-claude.md`. Die übrigen 28 Dateien in `Inbox/` tragen `type: daily`.
- `needs-review`: `Inbox/2026-09-13-vernetzung-vault-claude.md` über den Frontmatter-Tag. Den Text `#needs-review` gab es beim Planen in 17 anderen Dateien. In den gelesenen Zeilen steht er in Backticks, auch in `CLAUDE.md` Zeile 118, oder in Zeilen, die zu Codebeispielen gehören; ob diese Zeilen in einem Fenced-Block liegen, wurde nicht geprüft. Nicht gelesen wurden zwei lange Zeilen: `Code Projekte/active - Agentic Cockpit/docs/plans/2026-09-05-agentic-cockpit/task-04-vault-reader-teil-1.md` Zeile 53 und `Code Projekte/active - Agentic Cockpit/docs/code/vault_reader.md` Zeile 26. Jeden Treffer des Hooks an seiner Zeile nachlesen. Steht das Tag dort ohne Backticks im Fließtext, stimmt der Treffer.
- `review` ohne `review-done`: Zahl gegen `Review-Queue.base` in Obsidian halten. Eine Abweichung muss sich aus den Ausschlüssen (`Import/`, Codeblöcke, Inline-Code) erklären lassen, sonst ist sie ein Befund.
- `pending-review`: `pruefwerkzeuge.md`.
- Sitzungsdateien vom 2026-09-13: beim Planen `2026-09-13-16.md`, `2026-09-13-16b.md`, `2026-09-13-18.md`, dazu alles, was seitdem entstanden ist.
- Laufzeit beider Läufe unter 5 Sekunden.
Die Gegenprüfung für `needs-review` läuft mit eigener Suche, nicht mit dem Treiber: Grep über `*.md` nach `needs-review`, dann Ausschlüsse, Backticks und Codeblöcke von Hand abziehen.
**Task 2.** `python .vault-tools\session-hooks\faelle_nach_edit.py --echt`. Solange `pruefwerkzeuge.md` auf `pending-review` steht: `systemMessage` mit `Exit 1, 1 Befundzeile(n)` und im Kontext `[klein] AI/Regelwerk/pruefwerkzeuge.md — status: pending-review — Freigabe steht aus`. Danach einen Hook-Lauf mit einem Pfad außerhalb des Geltungsbereichs von Hand prüfen, er muss still bleiben:
```powershell
Set-Location D:\Claude
$env:PYTHONIOENCODING = "utf-8"
'{"cwd": "D:\\Claude", "tool_name": "Edit", "tool_input": {"file_path": "D:\\Claude\\Inbox\\2026-09-13.md"}}' | python .vault-tools\session-hooks\regelwerk_nach_edit.py; "exit=$LASTEXITCODE"
```
Erwartet: nur `exit=0`, keine Zeile davor.
**Task 3.** Den Linkcheck nach Task 3 erneut fahren und gegen `linkcheck-vorher.txt` halten. Erwartet: Die Zeile mit `task-09-gate.md -> [[:space:]]` fehlt, alle anderen Befunde sind gleich. Dazu der Vergleich alte gegen neue Zaunerkennung über alle Notizen, weil der Linkcheck nur tote Links zeigt und ein jetzt verschluckter gültiger Link dort nicht auffällt. Das Skript gehört ins Scratchpad, nicht in den Vault:
```python
"""Vergleicht alte und neue Zaunerkennung über den Vault: Welche Notizen liefern andere Wikilinks?
Nur für die Abnahme von Task 3, gehört nicht in den Vault.
"""
import re
import sys
VAULT = r"D:\Claude"
sys.path.insert(0, VAULT + r"\.vault-tools\linkcheck")
sys.path.insert(0, VAULT + r"\.vault-tools\obsidian-vault-navigator\scripts")
import vaultlib as V # noqa: E402
from linkcheck import strip_code as neu # noqa: E402
_FENCE_RE = re.compile(r"^\s{0,3}(`{3,}|~{3,})")
_INLINE_CODE_RE = re.compile(r"(`+)(?:(?!\1).)*?\1")
def alt(body):
"""strip_code() in der Fassung vor Task 3."""
if not body:
return ""
zaun = None
aus = []
for zeile in body.split("\n"):
m = _FENCE_RE.match(zeile)
if zaun is None:
if m:
zaun = m.group(1)[0] * 3
aus.append(" " * len(zeile))
continue
aus.append(_INLINE_CODE_RE.sub(lambda t: " " * len(t.group(0)), zeile))
else:
if m and m.group(1)[0] * 3 == zaun:
zaun = None
aus.append(" " * len(zeile))
return "\n".join(aus)
def links(text):
return {m.group(1).strip() for m in V._WIKILINK_RE.finditer(text)}
def main():
sys.stdout.reconfigure(encoding="utf-8")
geprueft = 0
anders = 0
for ap, rp in V.walk_vault(VAULT):
if not rp.endswith(".md"):
continue
with open(ap, "r", encoding="utf-8", errors="replace") as fh:
_, body = V.parse_frontmatter(fh.read())
geprueft += 1
a, n = links(alt(body)), links(neu(body))
if a != n:
anders += 1
print(rp)
print(" nur alt:", sorted(a - n))
print(" nur neu:", sorted(n - a))
print(f"{anders} von {geprueft} Notizen liefern andere Wikilinks.")
main()
```
Erwartet ist mindestens `task-09-gate.md` mit `nur alt: [':space:']`. Jede weitere Notiz mit `nur alt:` ist ein Link, den der Linkcheck nicht mehr sieht, und wird an der Stelle nachgelesen. Ist es ein echter Link hinter einem Block, den bisher eine Zeile mit Info-String versehentlich geschlossen hat, stimmt die neue Regel und die Notiz hat einen Zaunfehler. Ist es kein Zaunfehler, ist das ein Befund gegen Task 3.
## Risiken
- **Rauschen nach jedem Edit.** Solange `pruefwerkzeuge.md` auf `pending-review` steht, meldet `regelwerk_nach_edit.py` bei jeder Änderung an Stamm, Regelwerk, Agenten oder Skills denselben Befund. Das sieht man an der `systemMessage`. Ein Hook, der immer anschlägt, wird überlesen.
- **SessionStart feuert öfter als gedacht.** Ohne Matcher läuft der Hook auch bei `resume`, `clear`, `compact` und `fork` und legt denselben Kontext erneut ab. Die Laufzeit fällt dann jedes Mal an.
- **Verschluckte Links.** Ein Block, den bisher eine Zeile mit Info-String versehentlich geschlossen hat, läuft nach Task 3 weiter, notfalls bis Dateiende. Der Linkcheck prüft die Links dahinter dann nicht mehr. Sichtbar macht das nur der Vergleichslauf oben.
- **Timeout-Reihenfolge.** `regelwerk_nach_edit.py` bricht den Check nach 20 Sekunden selbst ab. Setzt der `settings.json`-Eintrag ein `timeout` unter etwa 25 Sekunden, beendet Claude Code den Hook, bevor er melden kann, und die Meldung fehlt.
- **Falsche Wurzel.** Beide Hooks nehmen `cwd` aus der Eingabe. Läuft eine Sitzung mit einem anderen `cwd` als `D:\Claude` und greift der Eintrag trotzdem, zeigt `sitzungsstart.py` das in der Kopfzeile. `regelwerk_nach_edit.py` bleibt dann bei Vault-Dateien still, und das sieht niemand. Ob Einträge aus `D:\Claude\.claude\settings.json` bei einem anderen Startordner überhaupt geladen werden, ist nicht geprüft.
- **Laufzeit.** Die 5 Sekunden sind ein Ziel, gemessen wird am echten Vault. Bei kaltem Dateicache oder wachsendem Vault kann `sitzungsstart.py` länger brauchen. Der erste `--echt`-Lauf zeigt eher den kalten Wert, der zweite den warmen.
- **Mitternacht.** Die Treiber rechnen das Datum einmal beim Start. Läuft ein Treiber über Mitternacht, schlagen datumsabhängige Fälle fehl, ohne dass am Hook etwas falsch ist.
- **Regressionslauf des Regelwerk-Checks.** `.vault-tools/regelwerk-check/regressionslauf.py` bricht nach dem Code schon heute an der Grundlinie ab, weil die Kopie des Regelwerks `pruefwerkzeuge.md` mit `pending-review` enthält. Das ist aus dem Code gelesen, nicht gefahren, und hat mit Task 3 nichts zu tun. Ein Abbruch dort ist kein Beleg gegen Task 3.
- **Python auf dem PATH.** Die Exec-Form startet `python` über den PATH. Ändert sich der PATH, scheitert der Hook mit einem Exit ungleich 0. Claude Code meldet das als nicht blockierenden Fehler, die Hook-Ausgabe fehlt dann.
## Getroffene Annahmen
- Inbox-Notizen werden auch in Unterordnern von `Inbox/` gezählt, nur `.md`-Dateien, Punktordner ausgenommen.
- Die Zählung `review` ohne `review-done` hat dieselben Ausschlüsse wie `needs-review` (`Import/`, `_index/`, Punktordner, `node_modules`).
- `Import/` und `_index/` fallen nur direkt unter der Wurzel heraus, Punktordner und `node_modules` in jeder Tiefe.
- Tags werden ohne Rücksicht auf Groß- und Kleinschreibung verglichen. Ein Inline-Tag steht am Zeilenanfang oder nach Leerraum. `#needs-review/x` und `#needs-review-done` zählen nicht als `needs-review`.
- Kaputtes YAML wird tolerant gelesen: Ein Tag in einer nicht geschlossenen Liste zählt. Ein Frontmatter ohne Schlusszeile gilt nicht als Frontmatter.
- `regelwerk_nach_edit.py` bestimmt die Wurzel wie `sitzungsstart.py` (`cwd`, sonst zwei Ebenen über dem Skript).
- Ein relativer `file_path` gilt relativ zur Wurzel. Der Pfadvergleich ignoriert Groß- und Kleinschreibung im ganzen Pfad, nicht nur beim Laufwerk.
- Still bleibt der Regelwerk-Hook nach dem Check nur bei Exit 0 mit der Schlusszeile „Keine der N gelaufenen Prüfungen hat angeschlagen." und ohne `ACHTUNG`-Zeile. Exit 0 mit `ACHTUNG` ist ein Befund.
- Interner Timeout 20 Sekunden, Kontext höchstens 40 Zeilen und 4000 Zeichen.
- `tool_name` wird nicht geprüft, das übernimmt der Matcher.
- `ENTWURF.md` für beide Hooks gehört zu Task 1, damit die Zieldateien disjunkt bleiben.
- Jeder Punkt des Sitzungsstart-Hooks läuft in einem eigenen `try`. Das geht über den Auftrag hinaus, dort war nur ein Fehler insgesamt verlangt.
- Der Wortlaut der CommonMark-Spezifikation wurde nicht gezogen. Die Schließzaun-Regel ist an markdown-it-py 4.2.0 belegt, das lokal im Projekt Agentic Cockpit liegt.
- In `ENTWURF-codeblock-erkennung.md` wird die gepflegte Zahl „vierzehn Angriffsfälle" durch „alle Angriffsfälle aus `faelle.py`" ersetzt.
## Offene Punkte für den Koordinator
- `settings.json`-Einträge: Matcher für SessionStart (alle Quellen oder nur `startup` und `clear`) und ein `timeout` von mindestens 30 Sekunden für den PostToolUse-Eintrag.
- Das Rauschen durch `pruefwerkzeuge.md` auf `pending-review`: freigeben lassen oder für die Zeit bis zur Freigabe hinnehmen.
- Den Abschnitt 4.5 der CommonMark-Spezifikation 0.31.2 gegen die Regel in `ENTWURF-codeblock-erkennung.md` halten und den Vermerk „Abgleich offen" danach streichen oder korrigieren.
- `regelwerk_check.py` nennt im Kommentar 17 Linkcheck-Fälle, nach Task 3 sind es mehr. Die Datei liegt außerhalb dieses Plans.
- `regressionslauf.py` des Regelwerk-Checks bricht wegen `pending-review` an der Grundlinie ab. Das ist unabhängig von diesem Plan, verdeckt aber jeden Regressionsnachweis dort.