Author SHA1 Message Date
Michael TamseandClaude 8c43d3a09c feat: Story 3.9 Review-Loop-3-Abschluss (bmad-code-review, 4 Layer; kein Loopback)
- 3 Decision-Resolutionen (D-3.9-1/2/3 = Option 1/1/2 — empfohlene Optionen)
- 14 Patches angewandt (von 15): P-3.9-1 Escaper-„Fehlalarm" verworfen — die
  „Ausführungs"-Evidenz war durch die Bash-Tool-Transport-Schicht korrumpiert
  (Backslash-Ebene halbiert); od-Beweis + Negativ/Positiv-Kontrolle aus
  Datei-Bytes belegen: Escaper literal-sicher, run-sandbox.sh Z. 74/790 unverändert
- 2 Defers (Home Story 3.13, Präzedenz DET-1/2), 9 verworfen
- Sandbox Minimal-Härtung (D-3.9-3 Option 2): R-1..R-9 mit echter feuernder
  Mechanik + echten Negativ-Kontrollen; Exit 0 auf frischem /tmp-Root,
  Re-Run-Idempotenz bestätigt
- compiler.md §5.15 (Pkt. 1/2/5 + Revision 3.4-Wortlaut), I/O-Matrix M/D/R,
  frozen-Schließtag + I/O-Zelle (Change-Log-autorisiert, keine Neu-Verhandlung)
- AD-3 read-only & wiki/-Erhaltungs-Invariante verifiziert (Validator SUCCESS)
- sprint-status 3-9 → done; Step-05 Status-Sync + Step-06 Abschluss

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 10:53:03 +02:00
Michael TamseandClaude 73f2c9e71d feat: Story 3.9 (Loop-2) — Deterministische Relevanz- & Reconcile-Routing schließen, review reif
§5.15 „Deterministische Relevanz- & Reconcile-Routing (Story 3.9)" in schema/compiler.md
(Revision 3.4): geschlossene Termgewinnung (AC-1), symmetrische Normalisierung +
literal-sichere Suche (AC-2), eine exklusive Routing-Tabelle UPDATE→CREATE→ORPHAN/HOLD→NO_OP
(AC-3, NO_OP als Update-Unter-Entscheidung), Raw-Immutability-Guard M/D/R→Run-FAIL (AC-4),
reservierte Zielpfade index/log/source/README→deterministischer Hold (AC-5), Zwei-Run-Identität
nicht-vakuum inkl. praktisch ausgeübter at-Exzeption (AC-6). §3.2-Pkt.-3d-Anker, §5.14-Pkt.-5-Scope
und §7-Bullet nachgeführt.

Review-Loop-2: 2 bad_spec (BS-L2-1 NO_OP-Leere-Candidate-Liste, BS-L2-2 Status-Codes R/C)
durch Re-Ableitung behoben; Step-04-Re-Review (2 frische Layer + Verification Gap) ohne
neuen Loopback (F-6 Lowercasing-Pkt.-1, F8 Guard-Abbruch real, F9 ableitungsbasierte
Term-Erwartung, F-22/F-27 BUNDLE-BYTE-Nachweis angewandt). Sandbox R-1..R-9 PASS, Exit 0.

Re-Derivation: deferred-work.md D-8/D-9 aufgegriffen; epic-3-context.md Z. 39 → Ist;
sprint-status.yaml Story-3.9 → review. AD-3 read-only + Erhaltungs-Invariante (nur
wiki/log.md) gewahrt. Suggested Review Order in Spec 3.9 angehängt.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 06:21:08 +02:00
Michael TamseandClaude 2f079ee3c8 feat: Story 3.8 Review-Loop-3-Abschluss (bmad-code-review Re-Run, 4 Layer; kein Loopback)
Triage: 4 decision-needed / 13 patch / 6 defer / 7 dismissed (u. a. Blind-Hunter
"alle Sandbox-SRO-Anker stale" widerlegt — alle 6 exakt vor den Loop-3-Edits).
Entscheidungen D-1..D-4 (alle Option 1): D-1 Schliessungs-Bullet macht den
"Bekannte Determinismus-Luecke"-Alt-Bullet superseded (append-only-Hinweis,
kein Umbruch); D-2 Story-Status-Flip auf done (Change-Proposal-Sequenzierung
betrafft das Epic-3-Abnahmegate 3.13, nicht die 3.8-Verankerung); D-3
autorisierte Neu-Verhandlung in geringfuegischem Umfang (Typo
"documenthuman" -> "document-human", Spec Re-Open-Delta-Zeile); D-4
Hold-Home = Story 3.10 (Epic 3), Korrektur-/Erweiterungs-Klassifikation =
Epic-4-Interface/Story 4.1 (S5.10 Pkt. 8 praezisiert).
Patches P-1..P-13: SRO-/Code-Map-Anker auf IST-Zeilen (compiler.md
375/376/377/477, Code Map 365-377/424ff/445/477, Sandbox 245/265/442/275/447/531);
symmetrische Term-/Body-Normalisierung (S3.2 Pkt. 2a + match_stufe_a, kein
host-abhaengiges -i); Receipt-Hashvergleich als re-executierbare
Zwei-Run-Formel (S5.14 Pkt. 2); Sandbox-Fresh-Kontext-Restluecke benannt
(mechanisch simuliert; Nachweis = 3.13-Abnahmegate); Manifest generated_by
wird konsumiert; at-Ausnahme hart assertiert (Wiederholungs-Schleife bei
Sekundenkollision, HARD-FAIL statt stiller PASS); "voellig pinbar"/"keine
offene Frage" ge scopet (Umlaut-Defer + 3.9-ACs bewusst offen); epics.md
AC-2 AD-16-Kopplung nachgefuehrt; review_loop_iteration 0 -> 2;
epic-3-context.md 3.9/3.11/3.12-Entscheidungen als Zielzustand markiert;
AD-17-Enum verifiziert (kein Edit noetig); Review-Loop-3-Change-Log-Eintrag.
Sandbox re-executiert: DET-1..DET-8 harte PASS, Exit 0; at-Ausnahme aktiv
(assertiert, real unterschiedliche Wanduhr-at-Werte); Witness nicht-vakuum.
Spec status: done, review_loop_iteration: 2; sprint-status 3-8 -> done
(Sequenzierungs-Hinweis: 3.9-3.12-Verankerungen + 3.13-Abnahme bleiben offen);
wiki/log.md Loop-3-Abschluss-Bullet; deferred-work.md 6 neue Defers.
AD-3 gewahrt: validator.md/wiki-compiler.md/adapters/raw/ unveraendert.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-20 19:12:40 +02:00
Michael Tamse 73d19cbcea @
feat: Story 3.8 Review-Loop-2-Härtung (bmad-code-review Re-Run, 3 Layer; kein Loopback) — DET-2 at-Ausnahme real ausgeübt, Manifest-Validierung, set -e; Step-05 done

Re-Review der Re-Open-Implementierung (3 Subagenten synchron: blind-hunter / edge-case-hunter-Retry / verification-gap): Triage keine intent_gap/bad_spec; 10 Patch / 0 defer / Rest Reject. Kern-Veranlassung: die drei Re-Open-ACs wurden als nicht wirklich ausgeübt erkannt — Manifest dekorativ (nie konsumiert), at-Maskierung vor dem Commit (stiller Ausschluss statt dokumentierter Ausnahme), Reception ohne Reihenfolge-/Entscheidungsfeld, Subshell ohne set -e (false-PASS-Risiko).

Sandbox-Patches (ausschließlich sandbox-3-8/run-sandbox.sh):
- DET-2 gehärtet: generated.at als reale Wanduhr-Ausgabe je Lauf committet (date -u) und als at_cell im Receipt festgehalten; Normalisierung auf AT erst beim Vergleich (Hash über at-normalisierte Content-Projektion, §5.14 Pkt. 2 "ohne die maskierte at-Zeile") — kein Maskieren vor Commit, kein stiller Ausschluss; Nach-Commit-Attestierung committed_at == at_now; Zwei-Run-Abgleich byte-identisch für deterministische Bestandteile, nur Ausnahme-Cells at_cell/tree dürfen differieren (at-Varianz real demonstriert: getrennte Worktree-Läufe → verschiedene Wanduhr-at)
- Kanonisches Eingabemanifest konsumiert/validiert (abschnittsbewusste Extraktion sources:/output_visible_run_identity:, Baseline-Quellen-Existenz nach Input-Commit, HARD-FAIL bei fehlenden Keys) — nicht dekorativ
- Receipt um order:/decisions: erweitert (§5.14 Pkt. 2) + Receipt-Nicht-Vakuum-Guard
- Subshell set -e (jeder fehlende Schritt bricht rc≠0 ab → HARD-FAIL statt false-PASS)
- match_stufe_a literal-sicher (Term regex-escaped, §3.2-Pkt.-2a/§5.14 Pkt. 5) + CRLF-tolerante Body-Extraktion
- cand-Newline-Normalisierung (tr \n ,)
- log_bullet fail-hard (mktemp/mv-Fehler nicht geschluckt — verlorener log-Eintrag hätte Vakuum-Gleichheit durch identische Baseline-Hashes erzeugt)
- git worktree prune vor dem Add (Re-Run-Sicherheit); git clean fail-hard unter set -e

Verifikation re-executiert: run-sandbox.sh → DET-1..DET-8 harte PASS, Exit 0; at-Ausnahme aktiv (at_cell A != at_cell B), Witness nicht-vakuum; Validator-Verdikt (Rev 9, D-3): alle wiki/-Dateien SUCCESS (keine Inhalts-Mutation); Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt (nur wiki/log.md); AD-3 read-only unverändert.

Spec: Change Log Review-Loop-2 (10 Patches), Verification-Pkt.-1 auf gehärteten Stand aktualisiert, Suggested Review Order auf neue Sandbox-Anker (238/257/267/409/414/498); Frontmatter status in-review → done. wiki/log.md Review-Loop-2-Abschluss-Bullet (zuerst-neuestes). sprint-status.yaml 3-8-… → review; last_updated 17:05.

Co-Authored-By: Claude <noreply@anthropic.com>
@
2026-08-20 14:00:53 +02:00
Michael TamseandClaude 96661836e9 feat: Story 3.8 Re-Open-Delta — Zwei-Run-Determinismus auf getrennte Worktrees/Manifest/Receipt umgestellt (genehmigtes Sprint-Change-Proposal 2026-08-20)
Re-Open-Scope (approved, ProMods; Epics-ACs Z. 357-370): zwei frische Agent-Kontexte in getrennt aufgebauten (sauberen) Worktrees (Q-6/A0-19 — eine zweite Ausführung in derselben Session genügt nicht), kanonisches Eingabemanifest (Baseline-Commit, geordnete Sources, output-sichtbare Run-/Zeit-/Identitätswerte), Run-Receipt außerhalb des Bundles (Candidates, Reihenfolge, Plan, Entscheidungen, Output-Hashes), keine hart codierten erwarteten Pläne/Concept-Bodies, keine pauschale verified-Maskierung (nur benannte at-Ausnahme).

- schema/compiler.md §5.14: Zwei-Run-Bestätigungs-Mechanik + §8-Revision-3.3-Klausel + Re-Open-Delta auf getrennte Worktrees/Manifest/Receipt/no-hardcode/no-masking umgestellt; §5.13-"in einer Session" unverändert (AD-6-Phasen-Disziplin, Fugen-Identität)
- sandbox-3-8/run-sandbox.sh: DET-2 auf zwei getrennte Worktrees (wt-a/wt-b) + manifest.yaml + receipts/-Dateien außerhalb des Bundles umgebaut; Candidate-Ableitung aus Git-State via deterministischem Stufe-a-Match (nicht hart codiert); Receipt-Abgleich als tatsächlicher Zwei-Run-Vergleich (diff der beiden Receipts, nicht je Lauf gg. Baseline); non-vakuum-Witness (Run-alpha-SHA != Baseline-alpha-SHA); Subshell für Worktree-cwd-Isolation. Exit 0, DET-1..DET-8 harte PASS
- spec-3-8: Re-Open-Delta-Intent, Tasks & AC-3/4/5, SRO auf Worktrees angepasst
- wiki/log.md: Story-3.8-Re-Open-Delta-Eintrag (Verankerung, Sandbox-Nachweis Exit 0, Erhaltungs-Invariante, Validator-Verdikt 7/7 SUCCESS, Revision 3.3)
- sprint-status.yaml: last_updated nachgeführt; Key 3-8 in-progress
- epic-3-context.md: neu kompiliert (Story-Liste/Stories 3.9-3.13 synchron)

Validator-Verdikt (human-mechanisch, schema/validator.md Rev 9, D-3 — kein CLI): alle wiki/-Dateien 7/7 SUCCESS (keine Inhalts-Mutation). Erhaltungs-Invariante §5.9 Pkt. 5: git status --porcelain -- wiki/ zeigt ausschließlich wiki/log.md. AD-3 read-only (validator/wiki-compiler/adapters/raw/canonical-terms) unverändert.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-20 13:08:28 +02:00
mita 1c116751d6 docs: extend Epic 3 remediation stories 2026-08-20 12:23:28 +02:00
mita 5a78271f89 Merge pull request 'Story 3 8' (#3) from story-3-8 into main
Reviewed-on: #3
2026-08-20 09:24:16 +00:00
Michael Tamse 39471d9422 docs: Story 3.8 Suggested Review Order auf klickbare path:line-Links (Step-05-Present) umformatiert 2026-08-20 10:52:21 +02:00
Michael Tamse c4cdf4b93e feat: Story 3.7 Review-Loop-2-Patches (bmad-code-review Re-Run, 4 Layer; kein Loopback) — Nutzer D1/D2/D3 = 1/1/1; 3 decision-needed / 7 patch / 3 defer / 9 dismissed (False-Positives); Patches: D1 deferred-work-Append (zwei Delokalisierungs-Blöcke: Terminologie-Drift INPUT_UNCOMMITTED/UNCOMMITTED_INPUT pre-existing, Misch-Run-Coverage → Home Story 3.8) → log.md-Protokollwiderspruch aufgelöst; D2 sprint-status review behalten + 4 Doku-Stellen (Spec-Code-Map, SRO-Anker-Text, in-progress-Bullet, §8-Rev-3.2-Klausel); D3 p2_plan-Element-(1)-Scope git status --porcelain -- raw wiki (exakt wie §5.9 Pkt. 6); Sandbox: CONSIST-1-EC-1-Isolation (committete Löschung), CONSIST-2 Misch-Run (Neu-Anlage +delta, Duplikat-Kontrolle, Neu-Anlage-Absenz nach Voll-Rollback via assert_restored-Guard), CONSIST-5 raw-SHA gg. $BASE, CONSIST-6 gültiger Commit-Zustand (sources-Nachführung id s3 + at-Bump), rollback()-Doku (Voll-Rollback = §6-Pkt.-3-Teilzustand, AD-17e-Abgrenzung), 3 Tippfehler; Instruktion: §5.13 Pkt. 7 um generated.at-Wanduhr-Gap-Ausnahme (§5.10 Pkt. 8, A0-20, Home 3.8); Sandbox re-executiert Exit 0 (14 harte PASS, CONSIST-7 non-vakuum plan_sha byte-identisch); Defers → deferred-work.md (Label-Präzisierung, A0-20-Post-Zustand → 3.8, pre-existing-Zeitpunktswort-Fuge); Spec: Review Findings + Change Log + loop_iteration 1; log.md Review-Loop-2-Bullet; sprint-status done; AD-3 unverändert 2026-08-20 09:04:41 +02:00
Michael TamseandClaude 938c05c4a8 feat: Story 3.7 Reason/Mutate-Trennung & Konsistenz-Endzustand sicherstellen (bmad-code-review, 3 Layer; kein Loopback) — §5.13-Phasen-Sektion (AD-6/A0-7, Änderungsplanung als institutionalisierter §5.9-Pkt.-6-P2-Block, Plan-Freeze an §5.9-Pkt.-5-Ghost-Diff-Kopplung, Zustands-Restaurations-Invariante, kein Teilerfolg AD-17f, VALIDATION_FAIL/SUCCESS, KEINE_EIGENE_ENGINE; §7-Bullet aufgelöst, §8-Normreferenz AD-6 auf §5.13-Anker, Revision 3.2 + Abschlussklausel); Sandbox CONSIST-1..CONSIST-7 (Exit 0, 14 PASS, Restaurations-Invariante, EC-1-Defizit-Negativkontrolle, pgrep-Robustheit, non-vakuum Determinismus-Zeuge); Review-Loop-1-Fixes (SRO-Anker 343/356/358/362/420/461, fünf→vier AD-6-Phasen, CONSIST-6-Validierung-vor-Commit); Suggested Review Order + Status done; log.md; sprint-status review; AD-3 unverändert
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-20 06:31:39 +02:00
Michael TamseandClaude 861e65f628 docs: Story 3.6 Suggested Review Order auf klickbare path:line-Links (Step-05-Present) umformatiert
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-20 04:53:58 +02:00
Michael Tamse 4a53771a9f feat: Story 3.6 Lease-Staleness & Recovery-Basis absichern (bmad-code-review, 3 Layer; kein Loopback) — §5.12-Leasing-Staleness-Sektion (Registrierung im Clone-Root-State, generationenbasiertes TTL, Quellen-Präzedenz granite-root-Marker/Baum/Startwert gen=0, holder_id-Ableitung, Verwaist-Klassifikation Übernehmen/Stale-Markieren, baseline_commit-Merge-Base-Diskrepanz, raw/-Recovery & native git stash, Registry-Invariante+kumulativ, log.md-Pflicht+Determinismus; §7-Vorbehalt :376 aufgelöst, §8 Revision 3.1); Sandbox STALE-1..STALE-6 (Exit 0, 12 PASS, Erhaltungs-Invariante); 4 Story-3.5-Defers aufgegriffen + 1 neuer Defer; Review-Fixes (Terminologie jünger→älter, Präzedenz gepinnt); Suggested Review Order + Status done; log.md; AD-3 unverändert 2026-08-20 04:52:06 +02:00
Michael TamseandClaude 895b006f7f feat: Story 3.5 Step-04-Review-Abschluss (bmad-code-review, 3 Layer; kein Loopback) — §5.11-Leasing-/Dirty-Tree-Sektion (Branch-Form lease/<area>/<id>, Lockfile, Merge-Base-Disziplin, Lease-Freigabe, Root-Scope, Dirty-Tree-Schutz, AD-17c-Merge-Pfad, Commit-Boundary, log.md, Determinismus; §7-Vorbehalt :357 aufgelöst, §8 Revision 3.0/3.1); Sandbox L1-L6+N1+D1+D2 (Exit 0, 21 PASS); 4 Defers; Review-Fixes (Text: UNCOMMITTED_INPUT-Gleichsetzung, 3.5/3.6-Seam, Norm-Rückverweis; Sandbox: Header-Bullet-Paare, L4-Restore, L6-BUILD_HEAD); Suggested Review Order + Status done; log.md 10/10 PASS; AD-3 unverändert
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 20:39:52 +02:00
Michael Tamse fae648e688 feat: Story 3.4 Review-Loop-1-Patches (bmad-code-review, 3 Layer; kein Loopback) — §5.10: Reflektiertheits-Selbsttest-Zeilenanfangs-Muster, Body-Reihenfolge deterministisch (Beleg-Anker LC_ALL=C), Konsolidierungs-Kriterium, Duplikat-Fall + Orphan-Kontrolle (Pkt. 8), N2/N3-Nachweise; Sandbox S1-S6+N1-N3+FW (10 Szenarien, Exit 0): ??-Sicht lebendig, S5-Ghost-HARD-FAIL, N1-Stimulus-Kommentar, N2 Disagreement (AD-16), N3 AD-4c-Kontext-Marker; 4 Defers → deferred-work.md; log.md 10/10 PASS; Spec done + Suggested Review Order; sprint-status done 2026-08-19 17:06:12 +02:00
Michael TamseandClaude 9b256fa5ad feat: Story 3.3 Review-Loop-1-Patches (bmad-code-review, 3 Layer; kein Loopback) — Präzedenz-Reihenfolge Korrigieren→Präzisieren→Erweitern→No-Op (AD-17h), No-Op als Nicht-Form, Korrigieren-Original-Wortlaut-Erhaltung; Sandbox U1–U9 (U8/U9 Negativ-Kontrollen, volle byte-Identität, reelle S-N-Anker, -eq 1-Konsolidierung, Frontmatter-Reihenfolge/Duplikat); 5 Defers → deferred-work.md; Spec done + Suggested Review Order; Story done
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 15:21:48 +02:00
Michael TamseandClaude 52f88fdc5f feat: Story 3.2 Code-Review-Loop-3-Patches (bmad-code-review, 4 Layer; Nutzer-Entscheidungen D-1/D-2/D-3/D-4/D-5 = 1/1/1/1/1 + P-1/P-2) — compiler.md Revision 2.7, §3.2-Pkt.-3b-lexikografisch, canonical-terms-Lookup-Semantik + Invariante + Konflikt-Verfahren, Sandbox T5-T7 + T1/T4-Asssertionen (7/7 PASS), Stale-Beleg (b) korrigiert, Statuskette in-progress → done, P-2-Typos, Story done
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 13:21:48 +02:00
Michael TamseandClaude 3aa484b2f4 feat: Story 3.2 Textuell-deterministische Relevanzbestimmung umsetzen (grep/ripgrep + Markdown-Traversal + Link-Following) — compiler.md Revision 2.5/2.6, §3.2-Mechanik, canonical-terms-Resolver, Mini-Sandbox T1-T4, Review-Loop-2-Patches, Story done
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 09:16:19 +02:00
Michael TamseandClaude e3e7ec346d feat: Story 3.1 Code-Review-Loop-2-Patches (bmad-code-review, 4 Layer; Nutzer-Entscheidungen D-1/D-2/D-3/D-4 = 1/1) — compiler.md Revision 2.4.2, Diff-Selbsttest operationalisiert, Sandbox-Verification-Record, Story done
16 Patches + 4 Decision-Resolutions (alle 1 = empfohlen): (1) D-1: §5.9-Pkt.-5-Probe = git diff --name-only <Baseline-Commit> -- wiki/ + git status --porcelain (vor Commit, Neu-Anlage-Zielpfade erlaubt, Pfad-Normalisierung, Rollback-Mechanik); (2) D-2: R-1-Baseline-Commit im log.md-Run-Eintrag notiert + Diskrepanz-Regel (git diff gewinnt, SHA-256 Sekundär) + Fallback; (3) D-3: INPUT_UNCOMMITTED-Abbruch „published/committed Input erforderlich" als P2-Element (1) + §1.1-Anker-Divergenz-Fußnote; (4) D-4: Sandbox-Edge-Tests S1-S5 + S6-Kontrolle re-executiert, echte Outputs als Verification Record in der (nicht gefrorenen) Spec-## Verification + re-executierbares Skript versioniert (implementation-artifacts/sandbox-3-1/); (5) Instruktions-Präzisierungen: P2-Zeitpunkt, No-Op-Kandidat, Mehrfach-Treffer-Reihenfolge, Update-Pfad-Rollback §5.3, AD-17h-Gap (Home Story 3.8), Term-Ableitung, visited-set, Worked Example auf Ist-Baum korrigiert, stale-Anker nachgeführt. wiki/log.md: 2026-08-19-Eintrag (Rev 2.4.2, Loop-2-Aufarbeitung, D-1-Probe-Beleg). deferred-work.md: F17-Eintrag saniert + VG-Beleg auf Verification Record zeigt. sprint-status.yaml: 3-1-... done, last_updated 08-19-2026. AD-3/D-3/keine neue §7-Klasse/Commit-Boundary gehalten.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 06:44:49 +02:00
Michael TamseandClaude efc543c9e1 feat: Story 3.1 Inkrementellen Datenfluss implementieren (Interpret→Reconcile→Synthesize→Update) — compiler.md Revision 2.4/2.4.1, Update-Routing + §5.9, Diff-Selbsttest, Review-Patch-Runde; Review-Handoff auf story-3-1
- schema/compiler.md: §3 Pkt. 2 Update-Routing (statt Kollision-Hold) + textuell-deterministische Kandidatenliste (AD-13); §5.9 Update-Mutationsmechanik (Erweitern/Präzisieren/Korrigieren, sources nur echte Belege, generated.at-Konvention, log.md-Eintragspflicht Story-3.1-Update, Mehrfach-Treffer-Konsolidierung); Erhaltungs-Invariante + deterministischer Diff-Selbsttest (git diff --name-only -- wiki/ ⊆ Kandidatenliste∪log.md∪Index, Ghost-Diff-Rück-Rollen); Run-Vorphase-Bausteine (Defer R-1 Change-Detection + P2 Pre-Run-Reconcile; Baseline = HEAD der vorherigen Mutations-Boundary); §0-Aufruf Update-Variante; §5.7/§5.8-Referenzen nachgeführt; §7 Update entlassen; §8 Prüfgrundlage Revision 9 (Punkt-11-Area-Lesart, Rev-9-Lücke geschlossen) + Revisionslog 2.4/2.4.1 (Step-04-Review-Patch-Runde, AD-3/D-3 gehalten, Worked Example real)
- wiki/log.md: Story-3.1-Nachweis (Diff-Selbsttest wiki/log.md, per-Datei-Verdikt, Rev-9-Präzisierung, Sprint in-progress→review)
- deferred-work.md: R-1/P2 aufgegriffen (2026-08-18 §5.9); Review-Findings-Protokoll bzw. F17-Defer (git-lose Ausweichform ab Story 3.2)
- sprint-status.yaml: Story 3.1 → in-progress → review (Sprint-Sync), last_updated 22:30
- neu: epic-3-context.md, spec-3-1-… (status done, Suggested Review Order)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 05:22:30 +02:00
27 changed files with 8278 additions and 38 deletions
@@ -2,6 +2,33 @@
Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Einträge werden append-only ergänzt; bestehende Einträge werden nicht verändert.
## Deferred from: code review of spec-3-3-bestehende-concepts-erweitern-präzisieren-korrigieren (Story 3.3, 2026-08-19)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-3-bestehende-concepts-erweitern-präzisieren-korrigieren.md`
summary: **`sprint-status.yaml last_updated`-Präzisions-Regression** — die Story-3.3-Sprint-Sync hat `last_updated` von `08-19-2026 13:14` (mit Uhrzeit, HEAD-Stand) auf reines Datum `08-19-2026` reduziert; die Pre-3.3-Konvention hielt die `HH:MM`-Granularität. Das Verhalten ist eine General-Eigenschaft des sync-sprint-status-Shared-Sub-Steps (Schritt-06), keine Story-3.3-Inhaltsentscheidung. Home: nächstes Sprint-Sync (Step-06) oder Sync-Verbesserung, die `last_updated` aus dem Zeitstempel ableitet.
evidence: `git show HEAD:_bmad-output/implementation-artifacts/sprint-status.yaml``last_updated: 08-19-2026 13:14` vs. Working Tree `08-19-2026` (Step-04-Review 2026-08-19, Verification-Gap-Layer, re-executiert verifiziert).
status: offen
- source_spec: `_bmad-output/implementation-artifacts/spec-3-3-bestehende-concepts-erweitern-präzisieren-korrigieren.md`
summary: **Frozen Story-3.1-No-Op-Bullet trägt die Typos „Schärferung" und „Formata"**`schema/compiler.md` §5.9 Pkt. 2 (No-Op, `:267`): „keine Schärferung" (inkonsistent zur konsistenten „Schärfung" der neuen operationellen Ebene) und „keiner der drei Formata" (→ „Formate"). Beide sitzen im **frozen** Story-3.1-Text (HEAD-identisch, nicht durch Story 3.3 eingeführt), den die Story-3.3-Operationelle-Ebene operationalisiert; ein fix wäre Re-Negotiation des frozen Textes (Ask-First). Home: nächste Compiler-Instruktions-Revision, die §5.9 Pkt. 2 ohnehin berührt.
evidence: `git show HEAD:schema/compiler.md` `:267` (frozen): „keine Schärferung, keine Ersetzung … keiner der drei Formata greift" — Step-04-Review (2026-08-19, Verification-Gap-Layer), via `git show` verifiziert.
status: offen
- source_spec: `_bmad-output/implementation-artifacts/spec-3-3-bestehende-concepts-erweitern-präzisieren-korrigieren.md`
summary: **U6-Zwei-Run-Identitäts-Nachweis überzeichnet „auch nach Commit" und kreuzreferenziert den `generated.at`↔AD-17h-Gap nicht** — der Claim „identische SHA-256 … auch nach Commit" gilt für alpha.md (reproduzierbar `6b148dd1…`) im Worktree-/Staged-Zustand; `wiki/log.md` embeddet den random `$BASE`-SHA (`log_update`), daher weicht der log.md-Hash je Run ab (konstruktionsbedingt — der Commit ist die jeweilige Mutations-Boundary). Der at↔AD-17h-Gap (gleiche Evidenz, zwei Runs → verschiedene `at`) ist in compiler.md offen verankert. Home: Story 3.8 (Determinismus-Vertrag, AD-17h — Behandlung von `at` und des Bundle-State-Vergleichs).
evidence: Step-04-Review (2026-08-19, Verification-Gap-Layer): Sandbox-Doppellauf — alpha.md SHA `6b148dd1…` reproduzierbar, log.md SHA je Lauf verschieden (`89b82cb2…`, `358bad3b…`); compiler.md at↔AD-17h-Hinweis (generated.at-Konvention).
status: offen (Home: Story 3.8)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-3-bestehende-concepts-erweitern-präzisieren-korrigieren.md`
summary: **Sandbox-U2-Szenario ist selbst-erfüllend und U7 misst die reale REIHENFOLGE nicht** — U2 schärft durch Hinzufügen einer neuen Faktenbehauptung (neue Stelle), die nach dem Erweitern-Kriterium Erweitern wäre, und passt nur, weil das Präzisieren-Wording hartkodiert ist; die Form-Wahl-Prozedur selbst wird nicht geprüft. U7 begründet „v2 vor v3" als „lexikografischer Tie-Break", obwohl v2/v3 verschiedene Dateien sind (Tie-Break gilt nur bei identischem Ort); der Fall „R-1-Dateiordnung ≠ lexikografische Ordnung" bleibt ungetestet. Kein Instruktions-Defekt (Abgrenzungs-Reihenfolge ist jetzt deterministisch fixiert), sondern Test-Eigenschaften-Hinweis. Home: nächste Sandbox-Erweiterung (Story 3.4/3.5) — eine Form-Wahl-Klassifikationsprobe + U7-Ordnungs-Variante mit nicht-lexikografischer R-1-Reihenfolge.
evidence: Step-04-Review (2026-08-19, Blind-Hunter + Verification-Gap-Layer): `run-sandbox.sh` U2 (neue Faktenbehauptung via `sed`, hartkodiertes Präzisieren-Wording), U7 (Ordering-Rationale vs. `compiler.md:268` REIHENFOLGE-Definition — Zuwachs-Sicht, lexikografisch nur bei identischem Ort).
status: offen
- source_spec: `_bmad-output/implementation-artifacts/spec-3-3-bestehende-concepts-erweitern-präzisieren-korrigieren.md`
summary: **Gemischte Normalisierungs-Politik im Sandbox-Evidenztext (Umlaute vs. Transkriptionen) ist ein Determinismus-Hazard für den Term-Abgleich** — Sandbox-Bodies nutzen Umlaut-Schreibweisen („Schlüssel", „läuft"), raw-Evidenz transkribiert („Schluessel"); die Abgrenzungskriterien beruhen auf Term-/Stellen-Abgleichen gegen §5.5-Inline-Verweise, und die §3.2-Normalisierung (lowercasing + `[-_ ]`-Kollaps) deckt Umlaut-/Em-Dash-Divergenzen nicht. Bekannte Story-3.2-Lücke (Em-Dash, Home Story 3.8) plus Sandbox-interne Divergenzen. Home: Story 3.8 (Determinismus-Vertrag, Normalisierungs-Vollständigkeit) oder Sandbox-Vereinheitlichung in einer Folge-Story.
evidence: Step-04-Review (2026-08-19, Verification-Gap-Layer): `run-sandbox.sh` — U1/U2/U3/U7 Body mit Umlaut-Schreibweisen vs. raw-Evidenz mit Transkriptionen; §3.2-Pkt.-1b-Kollaps-Klasse ohne Umlaut-/Em-Dash.
status: offen (Home: Story 3.8)
- source_spec: `_bmad-output/implementation-artifacts/spec-1-2-sources-lokal-unter-raw-bereitstellen.md`
summary: Checksum-/Fingerprint (SHA-256) der evtl. Git-Revision der Herkunftsquelle in `source.md` aufnehmen, damit die Provenienz reproduzierbar ist.
evidence: Blind-Hunter-Review (Finding 1/2): `source.md`-Provenienz ist ohne Fingerprint der Quelle in einem reinen Clone nicht verifizierbar; AD-3-basiertes „neue, datierte Datei"-Schema braucht einen Maschinen-Lesbaren Stand.
@@ -65,7 +92,7 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
- source_spec: `_bmad-output/implementation-artifacts/spec-1-4-schema-validierung-für-bundle-implementieren.md`
summary: Genau-eine-erlaubte-Linkform für den Punkt-11-Index-Check festlegen (mit vs. ohne `.md`-Endung) — Story 2.3.
evidence: Step-04-Review (Story 1.4, Loop 1): Der Validator-Punkt-11-Check akzeptiert beide Linkformen (relativer Bundle-Pfad mit oder ohne `.md`-Endung), weil die genau-eine-Form-Regel (A0-9/AD-7b) erst Story 2.3 definiert. Der Determinsmus-Anspruch des Validators bleibt gewahrt (beide Formen zählen als verlinkt); eine Endungs-Festlegung würde die abschließende §7-Liste erweitern und gehört in Story 2.3.
evidence: Step-04-Review (Story 1.4, Loop 1): Der Validator-Punkt-11-Check akzeptiert beide Linkformen (relativer Bundle-Pfad mit oder ohne `.md`-Endung), weil die genau-eine-Form-Regel (A0-9/AD-7b) erst Story 2.3 definiert. Der Determinismus-Anspruch des Validators bleibt gewahrt (beide Formen zählen als verlinkt); eine Endungs-Festlegung würde die abschließende §7-Liste erweitern und gehört in Story 2.3.
status: umgesetzt (2026-08-17, Story 2.3) — Linkform in `schema/compiler.md` §5.6 gepinnt (bundle-relativ **mit** `.md`-Endung; Rationale: Null-Migration der 3 Concept-Links in `wiki/index.md`, explizite Datei-Ziele, Standard-Markdown-Tools — AD-7b/A0-9/FR-10/AD-8) inkl. vier re-executierbarer Selbsttest-Formeln (AD-17h). Der Validator bleibt strukturell unverändert (Punkt 11 akzeptiert beide Schreibweisen — Story-2.2-Präzedenz, D-3); die konsistente Nachführung der „Story 2.3"-Notizen in `schema/validator.md` (Punkt 11, §8) bleibt Rev-9-Kandidat (Eintrag unten).
- source_spec: `_bmad-output/implementation-artifacts/epic-1-retro-2026-08-15.md`
@@ -81,7 +108,7 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
evidence: Retrospective F-04.
- source_spec: `_bmad-output/implementation-artifacts/epic-1-retro-2026-08-15.md` (F-06)
summary: `today`-Zeitzone für die `stale_after`-WARN (Validator §6.4) deterministisch festlegen — „heute in UTC abgeleitet" ist nicht hart definiert (Kalenderdatum des UTC-Zeitpunkts vs. lokaler Tag). Determinsmus-Anspruch (AD-17h) vor Epic-3 (Lifecycle-Konsequenz) sauber machen.
summary: `today`-Zeitzone für die `stale_after`-WARN (Validator §6.4) deterministisch festlegen — „heute in UTC abgeleitet" ist nicht hart definiert (Kalenderdatum des UTC-Zeitpunkts vs. lokaler Tag). Determinismus-Anspruch (AD-17h) vor Epic-3 (Lifecycle-Konsequenz) sauber machen.
evidence: Retrospective F-06.
- source_spec: `_bmad-output/implementation-artifacts/epic-1-retro-2026-08-15.md` (F-07)
@@ -126,6 +153,7 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
- source_spec: `raw/README.md` (Konvention, Story 1.2) — R-1
summary: R-1 (Compiler-Input-Interface) — Verdikt **Bestanden**: `schema/compiler.md` definiert seine Source-Eingabe als Menge beliebiger `raw/`-Dateien (Set-Interface, §1.2 „jede Datei unter `raw/` ist Evidenz"), nicht als einzelnen Pfad. Der erkennungsseitige Mechanismus, WELCHE `raw/`-Dateien wann verarbeitet werden („Run-ohne-Pfad"-Nutzererwartung aus DRYRUN.md: Kompilation via git diff + SHA-256-Record aus den `source.md`-Records), ist bewusst nicht in Story 2.1 enthalten und gehört als Erkennungs-/Auswahl-Mechanismus in die AD-5-Home-Story (Epic 3, Story 3.1/3.2).
evidence: Code Review (Story 2.1) R-1 — Epic-3-Forward-Risk, keine AC-Verletzung.
status: aufgegriffen (2026-08-18, Story 3.1 — §5.9): Input-Zuwachserkennung als Change-Detection-Baustein des inkrementellen Runs in `schema/compiler.md` §5.9 Pkt. 6 verankert (Determinismus `git diff` auf `raw/` und/oder SHA-256-Record aus `raw/**/source.md`; unveränderte `raw/`-Dateien bleiben außen vor; fehlender/unlesbarer SHA-256-Record → textueller Hinweis, keine Doppel-Verarbeitung).
- source_spec: `raw/README.md` / `schema/compiler.md` §1.2–§1.4 — R-2
summary: R-2 (Nicht-Markdown-Quellen) — Verdikt **Bestanden**: die Compiler-Instruktion liest Sources endungsneutral als Datei (§1.2/§1.4), unterstellt keine `.md`-Endung; PDF ist zulässige Evidenz (Vertrag §3.3 verlangt nur einen Dateipfad unter `raw/`, Validator EC-1 prüft nur Existenz). Die Konventions-/Asset-Zuordnungsfrage (Namensschema für Nicht-Markdown-Quellen) bleibt offen und gehört zu Epic 2/3 (Nutzer-Input-Gestaltung für den Dryrun-Forderungskatalog). Als dokumentarischer Hinweis: §1.2/§1.4-Widerspruch („jede Datei ist Evidenz" vs. „Artefakt-Dateien sind KEIN Input") in der Instruktion selbst klären (siehe Patch-Findings in der Story).
@@ -160,6 +188,29 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
- source_spec: `_bmad-output/implementation-artifacts/review-input-dryrun-2-1-pdf-radium.md` (Sandbox-Dryrun, P2)
summary: Pre-Run-Reconcile-Vorphase als deterministischen Pre-Plan-Schritt bündeln und dokumentieren (P2) — die textuellen Reconcile-Prüfungen (Ziel-Pfad-Kollision, Quellen-Existenz, AD-5-Relevanz der Quelle auf bestehende Concepts, `index.md`-Vorbedingung V-1) zu einem wiederverwendbaren Check-Block zusammenfassen (statt manueller Mehrfach-Checks, Bericht §3.2). Kein neuer Standalone-Prozess — nur ein reproduzierbarer Check-Block innerhalb der Instruktions-Ausführung; als Compiler-Instruktions-Schärfung (Story 2.1-Follow-up) oder Epic-3-Home (AD-5) einzuarbeiten.
evidence: Prozess-Optimierungs-Bericht §3.2/§4.4; Review-Input §4 (D-3-konform).
status: aufgegriffen (2026-08-18, Story 3.1 — §5.9): Pre-Run-Reconcile-Check-Block als wiederverwendbarer textueller Vorprüf-Block der Run-Vorphase in `schema/compiler.md` §5.9 Pkt. 6 verankert (Ziel-Pfade bzw. Ausgangs-Kandidatenliste, Quellen-Existenz EC-1 via Validator-Punkt, Betroffenheits-Kandidatenliste §3 Pkt. 2, `wiki/index.md`-V-1-Vorbedingung — fehlende Bundleroot → Run-FAIL, Vertrag §2); vor jeder Mutation durchlaufen und textuell festgehalten; kein neuer Standalone-Prozess (D-3).
## Review-Findings Story 3.1 (Step-04-Review, 2026-08-18) — Patch-/Klärungs-Protokoll
> **Herkunft:** Step-04-Review der Story 3.1 (Patch-Runde) über die drei Review-Layer (Blind Hunter / Edge Case Hunter / Verification Gap). Die klassifizierten Findings wurden direkt umgesetzt (Patch-Runde in `schema/compiler.md` Revision **2.4.1** + den Begleitdateien); die folgenden Einträge halten Befunde fest, die nicht geschehener Patch sind, sondern **Konzuls-Status**, und sichern verbleibende Gaps als konkrete Folge-Aufgaben.
- summary: **Story-3.1-Review: Diff-Selbsttest-Ausgabe (Pkt. 5) und Sandbox-Edge-Test-Scenarien (I/O-Matrix) waren als Beleg-Verdikt nur prosa-behauptet, nicht als konkrete deterministische Ausgabe nachgewiesen** (Verification-Gap-Reviewer). Heilung: `wiki/log.md`-Eintrag 2026-08-18 wurde in einen realen **Story-3.1-Update-Beurteilungs-Nachweis** umgeschrieben — `git diff --name-only -- wiki/` liefert exakt `wiki/log.md` (deterministischer Ist-Ausgabe-Befund, kein Ghost-Diff); die fünf Sandbox-Edge-Tests der I/O-Matrix (HAPPY_PATH_UPDATE, UNTOUCHED_CONCEPT, CONCEPT_COLLISION_BESTEHEND, CHANGE_DETECTION, PRE_RUN_RECONCILE) sind als deterministische Proben auf synthetischen Bäumen re-executierbar dokumentiert. Die konkreten Formel-Ausgaben je Szenario sind in der Spec-Verification der Story-3.1-Spezifikation enthalten.
evidence: Step-04-Review Story 3.1, VG-Reviewer; Verifikations-Nachweis.
status: umgesetzt (2026-08-18, Story 3.1 Patch-Runde) —
- `git diff --name-only -- wiki/``wiki/log.md` (die `--name-only`-Form listet Pfade, nicht Insertions-Zählungen; der ursprüngliche Beleg notierte beides vermischt) — kein Ghost-Diff
- Sandbox-Edge-Tests der I/O-Matrix → deterministische Formel-Ausgaben je Szenario: **konkret belegt** in der Story-3.1-Spec-`## Verification Record (Ausführungs-Beleg, 2026-08-19)` (re-executierbar: `bash _bmad-output/implementation-artifacts/sandbox-3-1/run-sandbox.sh` — S1S5 I/O-Matrix + S6 INPUT_UNCOMMITTED-Kontrolle; bmad-code-review Loop 2, D-4-Resolution)
- `git status --porcelain` → 4 modificiert + 2 untracked (der Schema-/Artefakt-/Spec-Teil liegt außerhalb `wiki/` und ist kein Ghost-Diff)
- Loop-2-Nachführung (2026-08-19): die operative Probe ist seit `schema/compiler.md` Revision 2.4.2 (D-1) `git diff --name-only <Baseline-Commit> -- wiki/` + `git status --porcelain -- wiki/` (vor Commit, Pfad-Normalisierung Strip `wiki/` + `.md`); die zitierte Vor-2.4.1-Form (`--stat`) war die Rev-2.4-Notation — s. `wiki/log.md` 2026-08-19-Eintrag (b)
- summary: **Story-3.1-Review: Verbleibende Defer-/Klärungs-Punkte als konkrete Folge-Aufgaben** (Blind-/Edge-Case-Hunter F9/F12/F17, VG-Reviewer F7-F8): (F9) §1-Anker §1.1/§1.4 → §1 Pkt. 1/Pkt. 4 nachgeführt (Patch in Revision 2.4.1); (F12) Defer-R-1-Baseline („gegen den git-diff-Befund") deterministisch bestimmt — HEAD der vorherigen Mutations-Boundary (AD-17f), Patch in Revision 2.4.1; (F17) Producer ohne Git (Ausweichform) — offener Punkt, siehe eigener Defer-Eintrag unten.
evidence: Step-04-Review Story 3.1, EH/BH/VG (F9/F12/F17).
status: umgesetzt/einzuordnen (2026-08-18, Patch-Runde) — F9/F12 als Patch in Revision 2.4.1; F17 als neue Defer-Aufgabe unten.
- source_spec: `_bmad-output/implementation-artifacts/spec-3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile-synthesize-update.md`
summary: **Neue Folge-Aufgabe (Story-3.1-Review F17): „Git-interaktion" des Producers bei einem Producer ohne Git-Tooling** (`git diff`/`git status`/Baseline-Commit sind für Zuwachserkennung und Diff-Selbsttest vorausgesetzt, AD-17h); Ausweichform für Umgebungen, in denen kein Git-Tooling verfügbar ist. Als Teil des Determinismus-Vertrags (AD-17h) werden `git`-Befunde als verlässlich behandelt; eine git-lose Partition ist als Ausweichform zu beschreiben (dokumentarische Konvention, D-3) — Vorschlag: der Producer (Agent) spiegelt die Zuwachserkennung über ein textuelles Eingabe-Manifest statt des `git diff`-Befunds und die Diff-Probe über ein Datei-Baseline-Manifest (Checksummen) — bis dahin bleibt Git für den Producer vorausgesetzt (NFR-1/NFR-5, Git-Bash auf win32). (Loop-2-Korrektur 2026-08-19: `source_spec`-Feld ergänzt — Schemaabweichung zu allen anderen Einträgen; Akteur „Executive" → „Producer (Agent)" — „Executive" ist in der D-3-Semantik undefiniert, der ausführende Akteur der Instruktion ist der Producer/Agent; die „Nutzer-Entscheidung 1/1 (Story 3.2)"-Referenz entfällt — Story 3.2 ist `backlog`, eine solche Entscheidung existiert nicht.)
evidence: Step-04-Review Story 3.1, EH (F17).
status: offen — Home: mit Story 3.2 (Relevanzbestimmung, `backlog`) oder der AD-17h-Absicherung (Story 3.8) zu klären; als Ausweichform (dokumentarische Konvention, D-3) zu spezifizieren.
- summary: **Story-3.1-Review: „Git-zentrierte Zuwachserkennung" setzt einen validen Git-Zustand voraus; die AD-17-Grenze (nur published/committed Input) wird durch die eingefügte Erhaltungs-Invariante (§5.9 Pkt. 5, Erhaltungs-Diff) und die Ghost-Diff-Konsequenz (Rück-Rollen vor Run-Gültigkeit, `log.md`-Kopplung als Abbruch-Vorlauf) hinreichend verbunden** — Kein neuer Folge-Gap; dokumentiert für die Nachvollziehbarkeit des Review-Nachweises.
status: Kein Gap — abgeschlossen (Patch in Revision 2.4.1).
- source_spec: `_bmad-output/implementation-artifacts/review-input-dryrun-2-1-pdf-radium.md` (Sandbox-Dryrun, P2)
summary: Provenienz-Checksumme bei mehrfacher Verwendung einer Quelle im Blick behalten (P2, Vorbereitung Epic 3/Story 2.2) — bei 11 Concepts aus einer Quelle (`raw/MetaModel.pdf`) ist eine wie auch immer geartete SHA-256/`last_modified`-Angabe Kandidat für `sources`-Metadaten; erst ab der zulässigen Schema-Erweiterung (Story 2.2 Claim-provenienz / Epic 3) in der Instruktion verankern. NICHT jetzt implementieren — `sources`-Subset (Vertrag §3.3, Punkt 6/§7) bleibt unverändert.
@@ -299,3 +350,211 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
status: offen
## Deferred from: code review of spec-3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile-synthesize-update (2026-08-19)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile-synthesize-update.md`
summary: **V-1-Cross-Referenz dreistufig und nie explizit aufgelöst** — der P2-Check-Block (compiler.md §5.9 Pkt. 6) zitiert `wiki/index.md`-V-1 als „Vertrag §2", während V-1 die §3.2-Voraussetzungsprüfung des Validators ist; die Kette compiler-§3-Pkt.-3 ↔ Validator-§3.2 ↔ Vertrag-§2 ist dreistufig und nicht explizit aufgelöst. Bestehendes Referenz-Idiom der Instruktion (Epic-1/Epic-2-Ära), nicht von Story 3.1 verursacht; Home: nächste Compiler-Instruktions-Revision, die §3/Pkt. 3 ohnehin berührt.
evidence: bmad-code-review Story 3.1 (2026-08-19, Blind-Hunter-Layer F14): compiler.md §5.9 Pkt. 6 Pkt. (4) vs. validator.md §3.2-Voraussetzungsprüfung (V-1/V-2-Label, Epic-1-Retro-F-03) vs. wiki-compiler.md §2 (Bundleroot).
status: offen
- source_spec: `_bmad-output/implementation-artifacts/spec-3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile-synthesize-update.md`
summary: **`epic-3-context.md` ist ein neues Artefakt außerhalb der Spec-Code-Map** — die Code Map listet exakt compiler.md / log.md / deferred-work.md / sprint-status.yaml (sonst read-only); die 50-Zeilen-Context-Datei (im Story-3.1-Commit neu) wird nirgends referenziert oder als erzeugt dokumentiert. Vom build-Verfahren (compile-epic-context) erzeugtes Kontext-Artefakt, keine Story-Inhalts-Mutation, kein Always/Ask-First/Never-Verstoß; Home: Story-3.2-Handoff oder nächstes Sprint-Sync (einzeilige Nennung im log.md-Nachweis genügt).
evidence: bmad-code-review Story 3.1 (2026-08-19, Acceptance-Auditor-Layer): Spec-Code-Map vs. `git show efc543c --stat` (6 Dateien, `epic-3-context.md` new file).
status: aufgegriffen (2026-08-19, Story 3.2) — einzeilige Nennung im `wiki/log.md`-Eintrag der Story 3.2 erfolgt; `epic-3-context.md` bleibt als vom build-Verfahren erzeugtes Kontext-Artefakt bestehen (keine Story-Inhalts-Mutation; Deckung über die Spec-`context:`-Frontmatter der Story-3.2-Spezifikation).
## Deferred from: Story 3.2 (Relevanzbestimmung, 2026-08-19)
- source_spec: `schema/compiler.md` §3.2-Pkt.-1b (Story 3.2)
summary: **Em-Dash-»—«-Varianten-Lücke der Normalisierung** — die Kollaps-Klasse `[-_ ]` (En-Dash »–«, Bindestrich »-«, Unterstrich »_«, Leerzeichen) deckt den Em-Dash »—« **nicht** ab. Eine Schreibvariante mit Em-Dash (z. B. »wissen — relevanz«, aus dem Kontext einer externen Quelle eingelesen) fällt nicht unter den Kollaps und wird nicht zu einer identischen canonischen Form normalisiert — eine erkannte Synonym-/Determinismus-Lücke der Relevanzbestimmung (A0-18/A0-19). Wird nicht stillschweigend in §3.2 ergänzt, sondern als offene Determinismus-Frage an **Story 3.8** (Determinismus-Vertrag, AD-17h) übergeben; bis dahin wird ein nicht auflösbarer Em-Dash-Term **wie notiert** verwendet (Kollaps-normalisiert, kein stiller Ausschluss).
evidence: Story-3.2-Review-Umsetzung (2026-08-19) — §3.2-Pkt.-1b dokumentiert die Lücke explizit als »Bekannte Determinismus-Lücke (aufgezeichnet, nicht still hinzugefügt)« und beruft sich auf diesen deferred-work-Eintrag als Handoff-Ziel.
status: offen (Home: Story 3.8)
## Deferred from: code review of spec-3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip (2026-08-19)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip.md`
summary: **`§3.2`-Pkt.-2a: Wortgrenzen-/Frontmatter-Scope der Stufe-a-Match-Semantik ist offen** — die Grep/rg-Erhebung matcht ohne Wortgrenze (Substring: `alpha` trifft `alphabet`) und dateiweit inkl. YAML-Frontmatter (`sources[].resource`, `generated.by` — Provenanz-/Tool-Strings können nicht-thematische Concepts als Kandidaten markieren). Keine deterministischen Lücke im Ist-Sinn (das Verhalten ist reproduzierbar), aber die Match-Semantik ist nicht fixiert; zwei Producer-Readings sind möglich. Home: nächste Compiler-Instruktions-Revision, die §3.2 ohnehin berührt (oder Story 3.8, Determinismus-Vertrag-Abgleich).
evidence: bmad-code-review Story 3.2 (2026-08-19, Loop 3, Edge-Case-Hunter + Verification-Gap-Layer): `schema/compiler.md:55` (Stufe-a-Form ohne `\b`/Frontmatter-Klausel) vs. §3.2-Intro („Concept-Bodies").
status: offen
- source_spec: `_bmad-output/implementation-artifacts/spec-3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip.md`
summary: **`§3.2`-Pkt.-1b-ii: Kollaps-Reichweite bei aufeinanderfolgenden/führenden/trailenden Separatoren unbestimmt** — „jedes Vorkommen wird in einen einzelnen Bindestrich kollabiert" ist für Läufe (`a--b`), führende (`-x`) und trailende (`x-`) Separatoren nicht eindeutig (Kollaps auf genau ein `-` + Trim? oder 1:1-Ersatz?). Bis dahin ist die Normalisierung für solche Terme nicht vollständig pinbar (AD-17h-Nähe). Home: nächste Compiler-Instruktions-Revision (mit- oder in Story 3.8 zusammen mit der Em-Dash-Lücke, s. obiger Eintrag).
evidence: bmad-code-review Story 3.2 (2026-08-19, Loop 3, Edge-Case-Hunter-Layer): `schema/compiler.md:51` (Pkt. 1b-ii).
status: offen
- source_spec: `_bmad-output/implementation-artifacts/spec-3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip.md`
summary: **Stufe-a-Treffer auf `index.md`-Pfade deterministisch aus der finalen Candidate-Liste entfernen** — Pkt. 2b definiert `index.md`-Treffer als Stufe-**b**-Trigger (gewurzelte Concept-Pfade sind die Stufe-b-Treffer), aber Pkt. 3b ordnet „Stufe-a-Treffer zuerst" ohne Fixierung, dass reine `index.md`-Stufe-a-Treffer *nicht* als Candidate (nicht-Concept-Pfad) in die finale Liste einfließen (Sandbox-T2-Ausgabe `index`+`alpha` illustriert den ungeklärten Fall; die Candidate-Liste ist per Definition auf Concept-Pfade beschränkt). Home: nächste Compiler-Instruktions-Revision, die §3.2 berührt.
evidence: bmad-code-review Story 3.2 (2026-08-19, Loop 3, Blind-Hunter + Edge-Case-Hunter-Layer): `schema/compiler.md:55-56,60`; `_bmad-output/implementation-artifacts/sandbox-3-2/run-sandbox.sh` T2-Kommentar.
status: offen
- source_spec: `_bmad-output/implementation-artifacts/spec-3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip.md`
summary: **Rev-2.5/2.6-Beleg-Klarigungen ohne Semantik-Auswirkung (Bündel)** — (a) Rev-2.5-Log-Zelle zitiert die `rg --exclude`-Form im aktiven Mechanismus-Teil ohne eigene Inline-Korrektur (Rev-2.6-Zelle erklärt sie nur — ein Producer, der nur Rev 2.5 liest, kopiert ein nicht-existentes Flag); (b) `sprint-status.yaml last_updated: 08-19-2026 07:10` liegt vor dem Story-3.2-Commit (09:16) — Zeitstempel dokumentiert nicht den Abschluss-Zeitpunkt (Story-3.1-Präzedenz: `last_updated` wird im Review-Loop-2-Commit ebenfalls nicht fortgeschrieben); (c) `compiler.md`-Revisionslog: Rev 2.6 steht über Rev 2.5 (chronologisch umgekehrt — Rev-2.6 wurde an der Einfüge-Stelle statt nach 2.5 angehängt); (d) Validator-Beleg „alle 7 `wiki/`-Dateien SUCCESS" als Aggregat ohne per-Datei-Liste (Spec-Verification Pkt. 4 verlangt „per-Datei-Verdikt als Ausführungs-Nachweis"; die Zählung 7 ist korrekt: 5 Root- + 2 Area-Concepts + log — re-executiert); (e) Rev-2.6-`git status`-Beleg listet `schema/canonical-terms.md` (neue Datei) und die `_bmad-output/`-Änderungen nicht, obwohl „`git status`" als Prüfgrundlage genannt wird; (f) Formel-4-Beleg: Ist-Zählung `38` ohne Baseline-`≡`-Partner (re-executiert: Ist = 38, korrekt — nur der Beleg-String ist unvollständig gegenüber der §5.6-Formel-4-Semantik „Ist ≡ Extraktion aus dem Baseline-Commit"). Home: nächste Log-/Nachweis-Runde (Story-3.3-Implementierungseintrag oder Story-3.2-Re-Review), keine Instruktions-Änderung nötig.
evidence: bmad-code-review Story 3.2 (2026-08-19, Loop 3, Blind-Hunter + Verification-Gap + Acceptance-Auditor-Layer): `wiki/log.md:4-5`, `schema/compiler.md:372-373`, `_bmad-output/implementation-artifacts/sprint-status.yaml:32`; re-executierte Formel-4-Zählung (38) und `ls wiki/` (7 `.md`-Dateien) bestätigen Korrektheit der Werte.
status: offen
## Deferred from: code review of spec-3-4-wissen-aus-mehreren-sources-synthetisieren (Story 3.4, 2026-08-19)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-4-wissen-aus-mehreren-sources-synthetisieren.md`
summary: **AD-16-Disagreement- und AD-4c-Übernahme-Marker-Pfad erst in der Sandbox demonstriert, nicht als reale Bundle-Demonstration** — die zwei vom Verification-Gap-Layer als „unbeobachtete Vertragsverpflichtungen" markierten Zweige (§5.10 Pkt. 3 AD-16-Default, Pkt. 4 AD-4c) sind durch die neuen Sandbox-Szenarien **N2 (Disagreement: beide Behauptungen + je Beleg bleiben, kein `(e; f)`-Falsch-Multi-Beleg, `Disagreement:`-log-Eintrag)** und **N3 (AD-4c-Kontext-Marker: „übernommen aus `gamma` auf Basis von `raw/gamma-v1.md#S-1`, nicht eigenständig belegt"; übernommene Quelle nicht in `sources`)** hart assertiert. Eine **reale** Disagreement- oder Übernahme-Synthese im echten Bundle bleibt Aufgabe der Epic-4-Stories (4.1 Klassifikation, 4.2 Disagreements in `log.md`), die hier bewusst nicht vorweggenommen wird (Story-3.4-Boundaries: keine Korrektur-Klassifikation, kein reales Bundle-Testmaterial). Home: Epic-4 (Story 4.1/4.2) — reale Anwendung der Sandbox-verankerten Mechanik.
evidence: Step-04-Review (2026-08-19, Verification-Gap-Layer) — Kernbefund „AD-16-Disagreement-Pfad untested" + „AD-4c-Übernahme-Marker untested"; geschlossen durch `_bmad-output/implementation-artifacts/sandbox-3-4/run-sandbox.sh` N2/N3 (Exit 0, 10 Szenarien).
status: offen (Home: Epic-4)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-4-wissen-aus-mehreren-sources-synthetisieren.md`
summary: **Orphan-Kontrolle (§5.10 Pkt. 8) textuell verankert, aber nicht in der Sandbox demonstriert** — die Regel „neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer bleibt unzugeordnet und wird in `log.md` als verwaist protokolliert" ist Teil der Pkt.-8-Erhaltungs/Determinismus-Ebene; das Review wies darauf hin, dass der Orphan-Fall (Zuwachs ohne Kandidaten-Treffer) kein eigenes Sandbox-Szenario hat. Die Negativ-Kontroll-Dichte der Sandbox (S3 Reflektiertheit, S5 `??`-Sicht/Struktur, N1 No-Op, N2/N3) deckt die übrigen Pkt.-8-Aspekte; ein Orphan-Szenario wurde zugunsten des verbleibenden Review-Scopes nicht ergänzt. Home: Story 3.8 (Reconcile-Orphan-Politik) oder Epic-4 — verwaiste Evidenz-Politik als Folge-Story.
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer) — Befund „unknown/orphaned raw evidence path without target"); `schema/compiler.md` §5.10 Pkt. 8 (Orphan-Kontrolle, in dieser Runde formuliert).
status: offen (Home: Story 3.8)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-4-wissen-aus-mehreren-sources-synthetisieren.md`
summary: **Sandbox-Evidenztexte transkribieren Umlaute, die Concept-Bodies nutzen Umlaut-Schreibweisen (Determinismus-Hazard für den Term-Abgleich)** — die Story-3.4-Sandbox setzt den bekannten Story-3.2-/Sandbox-Normalisierungs-Hazard fort (already known: Em-Dash-Lücke + Umlaut vs. Transkription, Home Story 3.8). Die §3.2-Normalisierung (lowercasing + `[-_ ]`-Kollaps) deckt Umlaut-Divergenzen („Schlüssel" vs. „Schluessel") nicht; die Sandbox-Bodies (Umlaut) vs. raw-Evidenz (Transkription, z. B. „Bestaetigungen", „Repraesentationen") nutzen bewusst beide Schreibweisen. Home: Story 3.8 (Determinismus-Vertrag, Normalisierungs-Vollständigkeit) oder Sandbox-Vereinheitlichung in einer Folge-Story — kein Instruktions-Defekt.
evidence: Step-04-Review (2026-08-19, Verification-Gap-Layer): `run-sandbox.sh` N2 (`Quorum-Bestaetigungen`) / S6 / S2/`Sitzungsschluessel` etc.; §3.2-Pkt.-1b-Kollaps-Klasse ohne Umlaut-/Em-Dash.
status: offen (Home: Story 3.8)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-4-wissen-aus-mehreren-sources-synthetisieren.md`
summary: **Frozen Spec-Intent-Typo „Nickel" im §5.10-Form-Wahl-Aufgaben-Pfad (Story-3.3-Defer U2/U7-Empfehlung)** — der Intent-Satz „Nickel die Story-3.3-Defer U2/U7-Empfehlung (…) im Sandbox auf" enthält „Nickel" statt „Nimm/Trage … auf" (oder „Schließe … auf"); er sitzt im `<frozen-after-approval>`-Block des Specs, dessen Text erst nach humaner Re-Negotiation geändert werden darf. Kein Instruktions-Defekt (der Sandbox-/authored-Text ist korrekt — FW-Szenario implementiert die Empfehlung inhaltlich). Home: Spec-Re-Negotiation (Ask-First: menschliche Autorisierung) oder Akzeptanz als kosmetischer Frozen-Fehler.
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer): `spec-3-4-…md` Zeile ~18 (frozen Intent) — „Nickel die Story-3.3-Defer U2/U7-Empfehlung … auf".
status: offen
## Deferred from: code review of spec-3-6-lease-staleness-recovery-basis-absichern-umsetzen (Story 3.6, 2026-08-19)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-6-lease-staleness-recovery-basis-absichern.md`
summary: **Schicksal des `lease/<area>/<id>`-Branch nach Übernahme/Stale-Markierung nicht definiert** — §5.12 Pkt. 3 „Nie gelöscht (AD-17e)" deckt die verwaiste Lease selbst ab (Lockfile, Registrierung, Marker), die Registry-Invariante Pkt. 6 den höchsten Reg-Generator; der isolierte `lease/<area>/<id>`-Branch-Ref (im Refspace von einer Verwaist-Behandlung unabhängig überlebend, da die Sandbox-Szenarien isolierte Branches über `isolate` aufbauen und die Folge-Konvergenz des Branch-Refs nicht hart prüfen) bleibt im Refspace zurück, ohne dass Übernehmen/Stale-Markieren ein deterministisches Folgeziel für ihn benennt (Branch-Cleanup/Einordnung). Kein Instruktions-Defekt für die Staleness-/Recovery-Mechanik (kein Branch blockiert eine Akquise — die Koordination läuft allein über Lockfile + Registry; AD-17e schützt Lockfile/Registry/Marker, ein gelöschter Branch zerstört keinerlei Commits), aber eine offene Evidenz-/Spez-Grenze am absetzenden (nicht akquirierenden) Nebenpfad des `lease/`-Baums. Home: spätere Instruktions-/Sandbox-Runde (Branch-Ref-Lebenszyklus) oder Story 3.8 (Determinismus des adressierbaren Zustands) — nicht in dieser Story.
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer) — „orphan branch fate / isolate-branch cleanup signal"; `schema/compiler.md` §5.12 Pkt. 3/6, `sandbox-3-6/run-sandbox.sh` (isolate/Branch-Aufbau).
status: offen (Home: spätere Instruktions-/Sandbox-Runde oder Story 3.8)
## Deferred from: code review of spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen (Story 3.5, 2026-08-19)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
summary: **holder_id Quelle/Uniqueness (Pkt. 1) nicht definiert** — das Lockfile-Feld `holder_id` („eindeutige Producer-/Run-Kennung") legt nicht fest, welcher deterministische Git-/Umgebungs-Wert es erzeugt (Branch-Suffix? Producer-Name? Run-Identifier?). Die Sandbox vergibt Beispiele (`run-a-holder`), §5.11 Pkt. 1 benennt keinen Ableitungs-Operanden. Kein Instruktions-Defekt für die Koordinations-Mechanik (Eindeutigkeit ist eine Producer-Verantwortung), aber eine offene Determinismus-Frage am Feld. Home: Story 3.6 (Lease-Registrierung) oder Story 3.8 (Determinismus-Ableitung) — ableitbarer holder_id-Default.
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer) — „holder_id uniqueness source undefined"; `schema/compiler.md` §5.11 Pkt. 1, `run-lock-Write`.
status: offen (Home: Story 3.6 oder 3.8)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
summary: **baseline_commit-Merge-Base-Ableitung (Pkt. 1) nicht von `git merge-base`-Laufzeit vs. notiertem `<Baseline-Commit>` reconciliert** — §5.11 nennt beide Quellen („deterministisch über `git merge-base` bzw. den notierten `<Baseline-Commit>` aus §5.9 Pkt. 6"), ohne Diskrepanz-Regel (welche gewinnt, wenn `git merge-base` eine andere Spitze liefert als die letzte notierte Mutations-Boundary? §5.9 Pkt. 6 hat die Differenz-Regel für Diff-Prüfungen, §5.11 nicht). Kein akuter Instruktionsdefekt (die Merge-Base-Disziplin zielt auf denselben Punkt), aber eine offene Disambiguierung. Home: Story 3.6 (Lease-Registrierung/Baseline) — Baseline-Auflösungs-Regel vereinheitlichen.
evidence: Step-04-Review (2026-08-19, Blind-Hunter+Verification-Gap) — „baseline_commit merge-base derivation not reconciled"; `schema/compiler.md` §5.11 Pkt. 1.
status: offen (Home: Story 3.6)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
summary: **Sandbox-log-Akkumulator vs. eigener `# Log`-Stand jedes isolierten Szenarios** — die Sandbox richtet je Szenario via `isolate` einen frischen (leeren) `wiki/log.md`-Stand ein und D2 demonstriert die kumulative Aufzeichnung separat; der Akkumulator lebt damit nur im Diskurs, nicht in einem durchgängigen Run-Baum. Die Koordinations-Aufzeichnung über MEHRERE Runs hinweg (consecutive Producers, die dieselbe `wiki/log.md`-Datei fortschreiben, ohne dass `isolate` sie zurücksetzt) ist nicht als eigenständiges Szenario demonstriert. Kein Instruktions-Defekt — die Regel (Pkt. 6) ist kumulativ formuliert, D2 belegt sie hart. Home: Story 3.6 (Registrierung/Verfahrnaher) oder Doku-Verbesserung der Sandbox — nichts funktionales offen.
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer) — „isolate wipes per-scenario log entries so cumulative coordination recording never demonstrated"; geschlossen durch D2 (Story-3.5-Sandbox).
status: offen (Home: Story 3.6) — D2 demonstriert die kumulative Aufzeichnung hart.
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
summary: **`isolate`-Skript nutzt native `git stash`-Variante für den Dirty-Tree-Schutz nicht** — §5.11 Pkt. 3 nennt „`git stash push -- <Pfade>` … oder Kopie in eine benannte Scratch-Zone"; die Sandbox demonstriert nur die Kopier-Variante (Scratch-Zone). Die `git stash`-Variante bleibt ungetestet. Kein Instruktions-Defekt (beide Wege sind textuell zulässig, die Determinismus-Anforderung betrifft das Ergebnis), aber eine Test-Lücke der alternativen Schutz-Umsetzung. Home: Story 3.6 (Lease-/Recovery-Stash-Semantik) oder Sandbox-Erweiterung — nichts funktional offen.
evidence: Step-04-Review (2026-08-19, Verification-Gap-Layer) — „native git stash alternative untested (only manual cp-into-scratch)"; `schema/compiler.md` §5.11 Pkt. 3.
status: offen (Home: Story 3.6)
### Aufgegriffen: holder_id-Quelle/Uniqueness (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
- Bezug: Defer `holder_id Quelle/Uniqueness (Pkt. 1) nicht definiert` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 1).
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 2 „holder_id-Ableitung (deterministischer Default; Defer holder_id-Quelle' Story 3.5 aufgegriffen)": `holder_id := <producer>-<id>``<producer>` aus dem Lockfile-Feld `producer`, `<id>` der Run-Identifier aus der Branch-Form `lease/<area>/<id>`; reproduzierbar aus dem committeten Git-State (AD-17h/A0-19), kein Wanduhr-Operand.
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh` STALE-2 (holder_id deterministisch uebernommen nach `lock_write`, `lock_holder`-Assertion) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 2)
### Aufgegriffen: baseline_commit-Merge-Base-Diskrepanz (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
- Bezug: Defer `baseline_commit-Merge-Base-Ableitung (Pkt. 1) nicht … reconciliert` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 2).
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 4 „baseline_commit-Merge-Base-Diskrepanz-Regel (Vereinheitlichung; Defer baseline_commit-Diskrepanz' Story 3.5 aufgegriffen)": bei Widerspruch gewinnt der `git merge-base`-Laufzeitbefund (Commit-Boundary-Prinzip), der notierte `<Baseline-Commit>` bleibt Sekundär-Fingerprint; Fallback ohne Boundary = alle `raw/`-Dateien Zuwachs, Merge-Base = ältester committeter Fundamentpunkt (§5.9 Pkt. 6 R-1, AD-14). Dieselbe Regel für Lease-Übernahme (Pkt. 3/4).
- Sandbox-Nachweis: STALE-2 (`git merge-base HEAD $BASE` == `$BASE`-Assertion, Merge-Base-Prüfung bei Übernahme) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 4)
### Aufgegriffen: Sandbox-log-Akkumulator / kumulative Registry (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
- Bezug: Defer `Sandbox-log-Akkumulator vs. eigener # Log-Stand …` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 3).
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 6 „Registrierungs-Invariante & kumulativer Registry-Aufbau über Runs (Vertrag §5)": der Registry-Aufbau ist kumulativ über Runs (mehrere aufeinanderfolgende Producer schreiben dieselbe Registrierung fort, analog zum `log.md`-Akkumulator); **kein eigener `# Log`-Stand**`wiki/log.md` ist der alleinige Aufzeichnungs-Ort. Registrierung lebt im Clone-Root-State (`registry/`), Marker Git-/Datei-Ebene ohne Eintrags-Body.
- Sandbox-Nachweis: STALE-1 (kumulative Registry-Zeilen run-a + run-b), STALE-5 (drei Runs in EINER `registry/wiki`, Gen-Invariante 3 gehalten) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 6)
### Aufgegriffen: native `git stash`-Variante (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
- Bezug: Defer `isolate-Skript nutzt native git stash-Variante für den Dirty-Tree-Schutz nicht` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 4).
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 5 „`raw/`-Recovery-Basis & native `git stash`-Variante (AD-3, AD-17d/A0-15)": `git stash push -- <Pfade>` als zweite textuell zulässige Schutzvariante neben der Scratch-Zonen-Kopie (beide deterministisch im Ergebnis, byte-identisch geschützt, nie gelöscht, Restore dokumentiert).
- Sandbox-Nachweis: STALE-4 (`git stash push`/`pop` um `wiki/alpha.md`, byte-identischer Restore via SHA-256; `raw/`-SHA-256 unverändert, AD-3) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 5)
## Deferred from: code review of spec-3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell (Story 3.7, 2026-08-20)
- CONSIST-7-Label praezisie: das Zwei-Run-Label behauptet "identische Plan-/Rollback-/State-Outputs", verglichen werden aber nur die Plan-Outputs (plan_sha/plan_state); der Rollback-/State-Nachweis (s7c) laeuft einmalig, log_sha ist dokumentierte Baseline-Konstante. Die non-vakuum-Kern-Assertion (plan_sha byte-identisch ueber beide Runs) ist real; das Label ist breiter als die Assertion. Home: Sandbox-Haertungsrunde.
- A0-20-Post-Zustand-Negativkontrolle in der Sandbox fehlt: §5.13 Pkt. 3/7 bindet den Determinismus-Vertrag an den Post-Zustand; der dokumentierte generated.at-Wanduhr-Gap (§5.10 Pkt. 8, offene A0-20-Konvention) macht den Post-Zustand ueber unabhaengige Runs nicht byte-identisch — die Sandbox testet den Gap weder negativ noch positiv. Home: Story 3.8 (A0-20-Home).
- Pre-existing-Zeitpunktswort-Fuge: §5.9 Pkt. 6 "am Anfang der Mutationsphase" vs. gefrorene I/O-Matrix + §5.13 "Abbruch vor der Mutationsphase" — §5.9 ist in Story 3.7 Wortlaut-unververaendert (Rueckverweis-Vertrag); die Fuge ist ohne Ask-First nicht schliessbar. Home: spaetere Instruktionsrunde.
### Delokalisierung: Terminologie-Drift INPUT_UNCOMMITTED/UNCOMMITTED_INPUT (Story-3.7-Review, 2026-08-20)
- Bezug: `schema/compiler.md` §5.9 Pkt. 6 Element (1) nennt die I/O-Matrix `INPUT_UNCOMMITTED`, §5.11 Pkt. 3 nennt dieselbe Pre-Run-Prüfung `UNCOMMITTED_INPUT` — dieselbe Prüfung, zwei Labels (pre-existing seit Story 3.1/3.5; in Story 3.6/3.7 als dieselbe §5.9-P2-Prüfung deklariert, §5.11-Wortlaut geschützt). Die Story-3.7-Sandbox (`p2_plan`-Element (1)) nutzt `INPUT_UNCOMMITTED` wie §5.9-Pkt.-6-Element (1).
- status: delokalisiert (kein Defekt — terminologische Präferenz; Home: spätere Instruktionsrunde, falls die Labels vereinheitlicht werden)
### Delokalisierung: Misch-Run-Coverage Neu-Anlage+Update in der Story-3.7-Sandbox (Story-3.7-Review, 2026-08-20)
- Bezug: §5.9 Pkt. 2/§5.7 Misch-Runs (Neu-Anlage + Update im selben Run) sind in der CONSIST-Sandbox ab Review-Loop-2 im CONSIST-2 abgedeckt (Neu-Anlage `delta` + Update `alpha`, Plan-Freeze-Menge + Voll-Rollback, Neu-Anlage-Absenz nach Rollback); übrige Phasen-/Synthese-Misch-Formen bleiben Home Story 3.8 (Determinismus-Vertrag AD-17h als Agent-Instruktions-Validator, inkl. A0-20-Post-Zustand-Test).
- status: delokalisiert (Home: Story 3.8)
## Deferred from: code review of spec-3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validator (Story 3.8, 2026-08-20)
### Aufgegriffen: Em-Dash-Varianten-Lücke der Normalisierung (Story-3.2-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Defer «Em-Dash-"—"-Varianten-Lücke der Normalisierung» (Defer-Block „Deferred from: Story 3.2", Eintrag 1; `status: offen (Home: Story 3.8)`).
- Umsetzung: `schema/compiler.md` §3.2 Pkt. 1b „Geschlossene Determinismus-Lücken (Story 3.8)": die Kollaps-Klasse ist um den Em-Dash `—` ergänzt (`[-–— _]``-`) — En-Dash, Em-Dash, Bindestrich, Unterstrich, Leerzeichen kollabieren identisch auf genau einen Bindestrich. Verankerung: §5.14 Pkt. 5 verweist auf §3.2-Pkt.-1b. Kein stiller Ausschluss; append-only-Regel-Ergänzung, bestehender §3.2-Wortlaut unverändert.
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh` DET-5 (Em-Dash → identische canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen) — Exit 0.
- status: aufgegriffen (Home erledigt in §3.2 Pkt. 1b / §5.14 Pkt. 5)
### Aufgegriffen: Kollaps-Reichweite bei Läufen/führenden/trailenden Separatoren (Story-3.2-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Defer «§3.2-Pkt.-1b-ii: Kollaps-Reichweite bei aufeinanderfolgenden/führenden/trailenden Separatoren unbestimmt» (Defer-Block „Deferred from: code review of spec-3-2…", Eintrag 2).
- Umsetzung: `schema/compiler.md` §3.2 Pkt. 1b „Geschlossene Determinismus-Lücken": Kollaps-Reichweite deterministisch — jedes Separator-Vorkommen kollabiert auf genau ein `-`; Läufe (`a--b``a-b`); führende (`-x``x`) und trailende (`x-``x`) Separatoren werden getrimmt. Verankerung §5.14 Pkt. 5; die in der Sandbox etablierte Semantik wird normativ fixiert.
- Sandbox-Nachweis: DET-6 (Läufe `a--b`, führende `-x`, trailende `x-`, Kombination `---x---``x`) — Exit 0.
- status: aufgegriffen (Home erledigt in §3.2 Pkt. 1b / §5.14 Pkt. 5)
### Aufgegriffen: Match-Scope der Stufe a (Wortgrenzen, Frontmatter-exklusiv) (Story-3.2-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Defer «§3.2-Pkt.-2a: Wortgrenzen-/Frontmatter-Scope der Stufe-a-Match-Semantik ist offen» (Defer-Block „Deferred from: code review of spec-3-2…", Eintrag 1).
- Umsetzung: `schema/compiler.md` §3.2 Pkt. 1b „Geschlossene Determinismus-Lücken" (c) Match-Scope der Stufe a: Stufe a matcht ganze Wörter (Wortgrenzen) über den Body, exklusive YAML-Frontmatter — Substring- und Frontmatter-Treffer (z. B. in `sources[].resource`/`generated.by`) liefern keine Kandidaten (deterministischer Stufe-a-Scope). Verankerung §5.14 Pkt. 5.
- Sandbox-Nachweis: DET-7 (ganzes Wort trifft alpha; Substring `opplung` und Frontmatter-only-Kandidat `mirror` liefern keine Kandidaten) — Exit 0.
- status: aufgegriffen (Home erledigt in §3.2 Pkt. 1b / §5.14 Pkt. 5)
### Aufgegriffen: Orphan-Kontrolle als deterministische Reconcile-Orphan-Regel (Story-3.4-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Defer «Orphan-Kontrolle (§5.10 Pkt. 8) textuell verankert, aber nicht in der Sandbox demonstriert» (Defer-Block „Deferred from: code review of spec-3-4…", Eintrag 2; `status: offen (Home: Story 3.8)`).
- Umsetzung: `schema/compiler.md` §5.10 Pkt. 8 „Deterministische Reconcile-Orphan-Regel (Story 3.8; §5.14-Präzisierung)": Verwaist-Befund deterministisch aus dem committeten Git-State (Zuwachs gg. `<Baseline-Commit>`, §5.9 Pkt. 6 R-1); datumsgruppierter `log.md`-Eintrag (Header `YYYY-MM-DD`, neueste zuerst; Quell-Pfad + `<Baseline-Commit>`); kein Banner/keine stille Vorbearbeitung/keine eigenständige Anlage (AD-16-Default, Epic-4-Interface). Verankerung §5.14 Pkt. 5.
- Sandbox-Nachweis: DET-8 (verwaiste `raw/`-Evidenz ohne Ziel-Pfad-Treffer → unzugeordnet, `log.md`-Verwaist-Eintrag mit `<Baseline-Commit>`, kein Banner, keine eigenständige Anlage, Erhaltungs-Invariante nur `log.md`) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.10 Pkt. 8 / §5.14 Pkt. 5)
### Aufgegriffen: generated.at↔AD-17h-Gap & Zwei-Run-Identitäts-Nachweis (Story-3.3-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Defer «U6-Zwei-Run-Identitäts-Nachweis … kreuzreferenziert den generated.at↔AD-17h-Gap nicht» (Defer-Block „Deferred from: code review of spec-3-3…", Eintrag 3; `status: offen (Home: Story 3.8)`).
- Umsetzung: `schema/compiler.md` §5.14 Pkt. 1 (Bundle-State-Definition: `generated.at`/`verified[].at` als benannte Ausnahme der Projektion), Pkt. 3 (Ausnahme-Menge: allein der `generated.at`-Wanduhr-Gap; alle übrigen Bestandteile byte-identisch) und Pkt. 2 (Zwei-Run-Mechanik: identische Plan-/Kandidaten-/Reihenfolge-Outputs und byte-identische mutierte Bundle-Bestandteile bis auf die `at`-Ausnahme). Der A0-20-Wanduhr-Gap bleibt verhaltens-seitig unverändert (Ask-First bei Verhaltens-Wechsel).
- Sandbox-Nachweis: DET-2 (Zwei-Run-Identität nicht-vakuum, echte Content-Hashes) + DET-4 (at-Gap-Ausnahme: diff ausschließlich auf der at-Zeile, nach at-Maskierung byte-identisch; Negativkontrolle Body-Differenz = AD-16-Defekt) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.14 Pkt. 13)
### Aufgegriffen: A0-20-Post-Zustand-Negativkontrolle in der Sandbox (Story-3.7-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Defer «A0-20-Post-Zustand-Negativkontrolle in der Sandbox fehlt … Home: Story 3.8 (A0-20-Home)» (Defer-Block „Deferred from: code review of spec-3-7…", Eintrag 2).
- Umsetzung: §5.14 Pkt. 3 definiert die Ausnahme-Menge des Bundle-State-Vergleichs (allein der `generated.at`-Wanduhr-Gap; jede andere Differenz außerhalb der Ausnahme = AD-16-Klassifikationsdefekt, Pkt. 4, textuell benannt NFR-4). Der Post-Zustand wird damit über die Zwei-Run-Mechanik (Pkt. 2) einschließlich at-Handhabung instruiert; die Sandbox demonstriert den Gap positiv und negativ.
- Sandbox-Nachweis: DET-4 (positiv: zwei Runs mit unterschiedlichen `at` → diff nur at-Zeile, nach Maskierung byte-identisch; negativ: Body-Differenz → AD-16-Klassifikationsdefekt) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.14 Pkt. 24)
### Aufgegriffen: Misch-Run-Coverage Determinismus-Vertrag (Story-3.7-Delokalisierung, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Delokalisierung «Misch-Run-Coverage Neu-Anlage+Update in der Story-3.7-Sandbox … übrige Phasen-/Synthese-Misch-Formen bleiben Home Story 3.8 (Determinismus-Vertrag AD-17h als Agent-Instruktions-Validator, inkl. A0-20-Post-Zustand-Test)» (Block „### Delokalisierung …", `status: delokalisiert (Home: Story 3.8)`).
- Umsetzung: Der Determinismus-Vertrag wird als geschlossene Bestätigungs-Mechanik in §5.14 verankert (Bundle-State-Projektion, Zwei-Run-Bestätigung, at-Ausnahme, Abweichungs-Klassifikation); die §5.13-Determinismus-Nachweise bleiben unverändert, die at-A0-20-Konvention ist auf die §5.14-Pkt.-3-Ausnahme-Menge verlagert (§5.13 Pkt. 7 Seam).
- Sandbox-Nachweis: DET-1..DET-4 (Bundle-State-Definition, Zwei-Run-Identität, Abweichungs-Klassifikation, at-Gap-Ausnahme) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.14)
### Aufgegriffen (teilweise): Gemischte Normalisierungs-Politik Umlaute vs. Transkription — Em-Dash-Anteil (Story-3.3/Story-3.4-Defers, Home Story 3.8) — Story 3.8, 2026-08-20
- Bezug: Defer «Gemischte Normalisierungs-Politik im Sandbox-Evidenztext (Umlaute vs. Transkriptionen) ist ein Determinismus-Hazard für den Term-Abgleich» (Defer-Block „Deferred from: code review of spec-3-3…", Eintrag 5, `status: offen (Home: Story 3.8)`) und Defer «Sandbox-Evidenztexte transkribieren Umlaute, die Concept-Bodies nutzen Umlaut-Schreibweisen» (Defer-Block „Deferred from: code review of spec-3-4…", Eintrag 3, `status: offen (Home: Story 3.8)`).
- Umsetzung: Der Em-Dash-Anteil beider Einträge ist in `schema/compiler.md` §3.2 Pkt. 1b / §5.14 Pkt. 5 geschlossen (Em-Dash in der Kollaps-Klasse `[-–— _]``-`). Der Umlaut-vs-Transkription-Anteil bleibt offen: die §3.2-Normalisierung (lowercasing + Kollaps-Klasse) deckt Umlaut-Divergenzen („Schlüssel" vs. „Schluessel") weiterhin nicht ab — kein neuer Normalisierungs-Schritt in Story 3.8 (kein neuer Regel-Operand, D-3/append-only).
- Sandbox-Nachweis: DET-5 (Em-Dash-Anteil) — Exit 0.
- status: teilweise aufgegriffen (Em-Dash-Anteil geschlossen in §3.2 Pkt. 1b / §5.14 Pkt. 5; Umlaut-vs-Transkription-Divergenz bleibt offen — Home: Sandbox-Vereinheitlichung oder folgende Compiler-Instruktions-Revision; kein Instruktions-Defekt)
## Deferred from: code review of spec-3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato (2026-08-20)
- **Sandbox-Härtungs-Guards (8 Befunde):** `mktemp` (Z. 37), `git init` (Z. 41), Baseline-Commit (Z. 94-96), `isolate()`-Sequenz (Z. 104-107), fehlende Match-Datei (Z. 136-141), verwaiste unregistrierte Worktree-Verzeichnisse (Z. 243-244) und Porcelain-Pfade mit Leerzeichen (`awk '{print $2}'`, Z. 217/682) tragen keine Fehler-Guards; ein Fehlgeschlagener init-Schritt lässt einen späteren „PASS" auf kaputtem Zustand drucken. — Defer-Grund: Testharness-Robustheit, keine normative Instruktion-Semantik; Re-Run-Risiko dokumentiert. Home: nächste Sandbox-Revision. — status: offen
- **DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert":** `plan=$(printf 'cand=alpha;form=update;baseline=%s')` (run-sandbox.sh:189) und `echo "form=update"` in `bs_a()` (Z. 221) sind wörtliche Skript-Konstanten; B1==B2-Identität damit trivial. — Defer-Grund: Loop-1-/Loop-2-Präzedenz (Loop-1 hat „DET-2-Plan-Literal (dev-demo, echte Content-Hashes decken)" rejected; DET-1 trägt den Demo-Kommentar Z. 211-214); die Nicht-Vakuum-Assertion tragen die echten Content-Hashes. Home: Story-3.13-Abnahme (echte Gate-Runs über die Instruktion). — status: offen
- **`norm()`-Locale-Pinning (`LC_ALL`) fehlt:** unter C/POSIX-Locale zerfällt die Byte-Klasse `[–—]` in {E2,80,93,94} (übermatcht z. B. `…`/`„`), `tr 'A-Z' 'a-z'` ist locale-abhängig → die Kanon-Form hängt am Host-Locale statt am committeten Git-State. — Defer-Grund: Testharness-Host-Locale-Abhängigkeit (Host = Ubuntu-Sandbox, dort UTF-8-Default; auf C-Locale-Hosts falsch), keine normative Semantik der Instruktion. Home: Sandbox-Härtung (gemeinsam mit den Hards-Guards). — status: offen
- **Mehrfach-Term-Vereinigung unübend:** §3.2 Pkt. 1c/§5.14 Pkt. 5 pinnen die Vereinigung der Treffer über alle Terme (besuchte Menge, Zuwachs-Sicht-Ordnung) als „vollständig pinbar" — die Sandbox übt nur den Einzel-Term-Pfad; eine Regressions in Union-/Dedup-/visited-Set-Logik würde unbemerkt durchgehen. — Defer-Grund: Story-3.8-Scope (Bestätigungs-Mechanik + Lückenschließung) ist erfüllt; ausführbare Union-Coverage gehört zur deterministischen Term-Gewinnung/Routing. Home: Story 3.9. — **status: offen → aufgegriffen (Story 3.9, 2026-08-20):** in `schema/compiler.md` §5.15 Pkt. 1 verankert (Mehrfach-Term-Vereinigung D-8: kollabierter Gesamt-Name als primärer Term + jedes ≥2-Zeichen-Segment als weiterer Term; Einzelzeichen-Segmente -a/-b tragen keinen eigenen Term); Sandbox R-1 übt die Vereinigung real aus (`beta-kommunikation``beta beta-kommunikation kommunikation`, Sidecar-exkludiert, Diff↔Manifest-Äquivalenz)
- **Orphan-Term hand-gesteuert + feste 2-Datei-Scan:** DET-8 pinnen den Term `fremdes` und scannen nur `wiki/alpha.md wiki/gamma.md` (run-sandbox.sh:669); die Determinismus der Orphan-ermittlung (Term-Ableitung aus dem `raw/`-Zuwachs, voller Kandidaten-Scan über Stufen a/b/c) ist nicht ausgeübt — nur die Log-Seite wird hart assertiert. — Defer-Grund: Mechanik ist textuell korrekt (DET-8-Assertions Z. 688-692 solide); Term-Derivation ist Story-3.9-AC („geschlossener, geordneter Algorithmus oder persistiertes Term-Manifest"). Home: Story 3.9. — **status: offen → aufgegriffen (Story 3.9, 2026-08-20):** Sandbox R-5 leitet die Terme AUS dem committeten `raw/`-Zuwachs ab (`git diff --name-only HEAD~1 -- raw/`, Dateiname→Term) und scannt **Stufe a** über den Root-`wiki/*.md`-Glob statt eines festen 2-Datei-Scans — Orphan-/CREATE-Term-Derivation auf der Stufe-a-Ebene mechanisch ausgeübt (D-9; §5.15 Pkt. 1). **Scope-Präzisierung (Loop-3, 2026-08-21):** die ausgeübte Termination-Derivation + Scan beschränkt sich auf **Stufe a über den Root-`wiki/*.md`-Glob** (keine Area-Rekursion, keine Stufen b/c); die volle Stufen-b/c-Traversal-Coverage des Kandidaten-Baums bleibt verbleibend offen (Home: Story 3.13-Abnahme / Sandbox-Härtung — benannter Defer, keine instruktionsseitige Lücke, §5.15 Pkt. 2/3 verankern die Stufen)
- **Umlaut-vs-Transkription im Match-Pfad unübend:** die §3.2-Normalisierung deckt Umlaut-Divergenzen („Schlüssel" vs. „Schluessel") nicht ab, und kein DET-Szenario treibt einen Umlaut-Term durch den Match-Pfad. — Defer-Grund: bereits „Aufgegriffen (teilweise)" dokumentiert (Block oben, Home: Sandbox-Vereinheitlichung oder folgende Compiler-Instruktions-Revision; „kein Instruktions-Defekt") — keine neue offene Frage, nur fehlende Sandbox-Abdeckung. Home: wie dort. — status: offen
## Deferred from: code review of spec-3-9-deterministische-relevanz-und-reconcile-routing-schliessen (2026-08-21)
- **`norm()`/`tr` ohne `LC_ALL`-Pinning (Sandbox-3.9, 952 Zeilen):** unter C/POSIX-Locale zerfällt die Byte-Klasse `[–—]` in {E2,80,93,94} (übermatcht z. B. `…`/`„`), `tr 'A-Z' 'a-z'` ist locale-abhängig → die Kanon-Form hängt am Host-Locale statt am committeten Git-State. — Defer-Grund: bekannter Story-3.8-Defer (D-7, Block oben „`norm()`-Locale-Pinning fehlt"), hier in den Sandbox-3.9-Scope ausgeweitet (alle R-Szenarien + R-9-Worktree-Läufe laufen auf derselben `norm()`); keine normative Instruktion-Semantik (Instruktion nennt Kollaps-Klasse + lowercasing, keine Locale-Bindung). Home: Sandbox-Härtung (gemeinsam mit den Story-3.8-Hard-Guards). — status: offen
- **Positive Zwei-Run-Identität strukturell trivial (R-9 clean A==B):** Plan-/Form-/Routing-Literale der `run9_worktree()`-Receipts sind Skript-Konstanten; die A==B-Identität folgt daraus, nicht aus realer Ausführung; die Nicht-Vakuum-Last tragen die echten Content-Hashes (Bundle-BYTE-Vergleich) und die Negativ-Kontrolle (divergent ≠ clean, hart FAIL). — Defer-Grund: Präzedenz Story-3.8-Defer „DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert"" (Block oben, Home: Story-3.13-Abnahme, echte Gate-Runs über die Instruktion); Story-3.9-Sandbox folgt demselben Demo-Muster. Home: Story-3.13-Abnahme. — status: offen
@@ -0,0 +1,54 @@
# Epic 3 Context: Inkrementelle Kompilation & Synthese
<!-- Compiled from planning artifacts. Edit freely. Regenerate with compile-epic-context if planning docs change. -->
## Goal
Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bundle, statt das Wiki bei jedem Lauf aus sämtlichen Rohquellen neu aufzubauen (Compounding Knowledge). Bestehende Concepts werden durch neue Erkenntnisse erweitert, präzisiert oder in eindeutig belegten Fällen korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation mit gemischter, claim-granularer Provenienz synthetisiert; unverändertes Wissen bleibt erhalten. Relevanzbestimmung und Reconcile-Routing sind textual-deterministisch ohne Embedding- oder Vector-Infrastruktur. Konkurrierende Producer koordinieren sich über eine atomare Root-Scope-Lease mit transaktionalem Dirty-Tree-, Rollback- und Release-Lifecycle. Klassifikationspflichtige Kollisionen und Widersprüche werden bis Epic 4 fail-closed als strukturierter Hold erhalten; der Determinismus-Vertrag wird mechanisch qualifiziert. Epic 3 gilt erst nach einem realen, unabhängigen Source→Compilation→Wiki-Abnahmegate als abgeschlossen (AD-5, AD-6, AD-13, AD-17a/b/dh, A0-6/7/1216/18/19).
## Stories
- Story 3.1: Inkrementellen Datenfluss implementieren (Interpret → Reconcile → Synthesize → Update)
- Story 3.2: Relevanzbestimmung textual-deterministisch umsetzen (grep/ripgrep/Traversal)
- Story 3.3: Bestehende Concepts erweitern/präzisieren/korrigieren
- Story 3.4: Wissen aus mehreren Sources synthetisieren
- Story 3.5: Leasing & Dirty-Tree-Schutz für konkurrierende Producer umsetzen
- Story 3.6: Lease-Staleness & Recovery-Basis absichern
- Story 3.7: Reason/Mutate-Trennung und Konsistenz-Endzustand sicherstellen
- Story 3.8: Determinismus-Vertrag (AD-17h) als Agent-Instruktions-Validator umsetzen
- Story 3.9: Deterministische Relevanz- und Reconcile-Routing schließen
- Story 3.10: Inkrementelle Update- und Synthese-Erhaltung absichern
- Story 3.11: Root-Scope-Leasing atomar und worktree-übergreifend akquirieren
- Story 3.12: Lease-Lifecycle und Commit-Abschluss transaktional schließen
- Story 3.13: Epic-3-Verifikations- und Abnahmegate
## Requirements & Constraints
- Ein Compilation Run nimmt neues Source Material und das bestehende Wiki als Input; das Wiki wird inkrementell weiterentwickelt, nie vollständig regeneriert. Unverändertes Wissen und nicht betroffene Concepts bleiben erhalten; Git-Änderungen konzentrieren sich auf die durch die neue Erkenntnis betroffenen Concepts (FR-4, FR-12, AD-5).
- Neue Informationen führen nicht automatisch zu neuen Dateien: bestehende Concepts werden erweitert, präzisiert oder korrigiert, ohne ihre Struktur zu zerstören; weiterhin gültige Beziehungen und Provenienz bleiben erhalten (FR-6).
- Mehrere Sources zum selben Thema münden in eine gemeinsame Wissensrepräsentation statt getrennter Zusammenfassungen. Das Ergebnis reflektiert den erkannten Wissensstand, konsolidiert Redundanzen und übernimmt die relevante Source-Provenienz der beteiligten Sources — claim-granular (FR-7, AD-4, A0-3).
- Relevanzbestimmung, Routing, Planung und nicht-konfligierende Mutationen sind textual-deterministisch (grep/ripgrep, Markdown-Traversal, Link-Following). Embeddings, Vector-Search, Knowledge-Graph-Datenbank und RAG gehören nicht in den Compiler-Kern (AD-13, A0-18).
- Ein Run verwendet ausschließlich veröffentlichte (committete) Inhalte als Input, nie Zwischenstände während der Mutation (AD-17).
- `raw/` bleibt immutable und dient als Recovery-Basis; ein fehlgeschlagener Run verändert es nicht (AD-3).
- Unvollständiges, ungeprüftes oder widersprüchliches Wissen wird ohne künstliche Gewissheit dargestellt; klassifikationspflichtige Konflikte werden bis Epic 4 ohne Wissensmutation in einem benannten Hold erhalten (NFR-7).
## Technical Decisions
- **Inkrementeller Datenfluss (AD-5, A0-6):** Interpret → Reconcile → Synthesize → Update affected Concepts. Startpunkt ist immer das bestehende Bundle — niemals "Regenerate Everything" (SM-1, FT-6).
- **Reason/Mutate-Trennung (AD-6, A0-7):** Logische Phasen Analyse → Reconcile → Plan Changes → Mutate → Validate. Keine eigene Workflow Engine; ein Agent kann die Phasen in einer Session durchführen, der beobachtbare Endzustand des Bundles muss auch bei Abbruch konsistent sein.
- **Deterministische Relevanz & Routing (A0-18):** Geschlossene, geordnete Term-Gewinnung bzw. explizites persistiertes Term-Manifest; Suchterm und Concept-Body werden identisch normalisiert und literal-sicher verglichen. Eine exklusive Routing-Tabelle unterscheidet `UPDATE`, `CREATE`, `ORPHAN/HOLD` und echten `NO_OP`; gleicher Git-State plus gleiches Eingabemanifest erzeugt dieselbe Candidate-Liste und Reihenfolge. *Ist (Story 3.9, 2026-08-20): in `schema/compiler.md` **§5.15** verankert (Termgewinnung geschlossen, eine exklusive Routing-Tabelle, Raw-Immutability-Guard, reservierte Zielpfade, Zwei-Run-Identität; §3.2 bleibt Erhebungs-Anker, §5.15 Pkt. 3 die Routing-Zuordnung).*
- **Keine eigene LLM-Runtime:** Der ausführende agentische Host orchestriert den AD-17-Ablauf (Lease holen, innerhalb des geleasten Bereichs mutieren, committen, freigeben); keine separaten Prozesse oder ein Server (AD-11).
- **Atomare Root-Scope-Lease (AD-17a/b, A0-12/13):** Producer behalten die Branch-Konvention `lease/<area>/<id>`, akquirieren aber genau einen atomaren, scope-bezogenen Lock im clone-geteilten Zustand. Die Run-ID ist Lock-Inhalt, nicht Exklusivitätsschlüssel; konkurrierende Producer verschiedener IDs und Worktrees teilen denselben Root-Scope (`wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien). *Zielzustand (Review-Loop-3, P-11): geplant für Story 3.11 (Sprint-Change-Proposal 2026-08-20) — die atomare Lock-Präzisierung ist noch nicht in `schema/compiler.md` §5.11 verankert; die bestehende §5.11-Verankerung (Branch-Konvention, Root-Scope-Lease) bleibt unverändert maßgeblich.*
- **Fail-closed Kollisionsgrenze (AD-17c, A0-14):** Zwei Branches mit ungleichen Änderungen am selben Concept-Pfad werden nie textuell automatisch gemerged. Epic 3 erhält beide Commit-Hashes und den Scope in einem strukturierten Hold; AD-16-Klassifikation und semantische Auflösung sind Epic 4 / Story 4.x.
- **Transaktionaler Lifecycle (AD-17df, A0-15/16, AD-6):** Preflight schützt getrackte und ungetrackte Fremdänderungen (eindeutige Abort-/Protect-Zustandsmaschine), Rollback restauriert exakt den bezeichneten Baseline-Commit (Index + Worktree), Release hinterlässt Mutation, zulässigen Nachweis und sauberen Worktree dauerhaft. Eine lebende Lease wird nicht allein durch Generationserhöhung stale; Staleness verlangt bestätigten Abbruch oder abgelaufene Liveness plus atomare Ownership-Prüfung. *Zielzustand (Review-Loop-3, P-11): geplant für Story 3.12 (Sprint-Change-Proposal 2026-08-20) — die transaktionale Lifecycle-Präzisierung (Abort-/Protect-Zustandsmaschine, Liveness-Prüfung) ist noch nicht in `schema/compiler.md` §5.11/§5.12 verankert; die bestehende Verankerung bleibt unverändert maßgeblich.*
- **Determinismus-Vertrag (AD-17h/FT-10, A0-19):** Das kanonische Eingabemanifest bindet Baseline, geordnete Sources und jeden output-sichtbaren Run-/Zeit-/Identitätswert; der Run-Receipt (Candidates, Reihenfolge, Plan, Entscheidungen, Output-Hashes) liegt außerhalb des Bundles. Zwei getrennte saubere Worktrees mit frischen Agent-Kontexten erzeugen denselben Bundle-State; hart codierte erwartete Pläne oder Concept-Bodies und pauschal maskierte `verified`-Ereignisse sind kein gültiger Nachweis.
- **Synthese bleibt source-grounded (AD-4):** Bestehende Concepts dürfen Kontext liefern, fachliche Aussagen müssen aber auf nachvollziehbare `raw/`-Evidenz zurückführbar bleiben; Wiki-Links ersetzen nie die Provenienz zur ursprünglichen Evidenz.
- **Epic-3-Abnahmegate:** Ein portables, fail-fast ausführbares Gate führt alle Epic-3-Szenarien aus, ruft den vollständigen autorisierten Schema-Validator über alle `wiki/`-Dateien auf und lässt einen frischen Agent-Kontext die kanonische Instruktion über einer repräsentativen Fixture ausführen — der Harness schreibt keine erwarteten Wiki-Ausgänge selbst. Zwei frische Agent-Kontexte müssen Run-Receipts und Bundle-State angleichen. Ein Defizit des autorisierten Validator-Vertrags blockiert das Gate und verlangt eine separat genehmigte Epic-1-Remediation.
- **Keine UX-/Design-Anteile relevant:** v1 ist datei-/CLI-basiert ohne GUI (PRD A-3, AD-11).
## Cross-Story Dependencies
- Baut auf dem Workspace aus Epic 1 auf (immutable `raw/`, Bundle-Root, Schema-Validierung) und konsumiert die in Epic 2 erzeugten OKF-konformen, verlinkten Concepts mit claim-granularer Provenienz als vorhandenes Wissen.
- Story 3.9 liefert die deterministische Routing-Basis für Story 3.10; Story 3.11 liefert die atomare Lease-Basis für Story 3.12 (parallel). Story 3.8 wird nach 3.93.12 mit echten unabhängigen Runs abgeschlossen; Story 3.13 ist das finale Abnahmegate.
- AD-17c/A0-14 sind geteilt: Epic 3 verantwortet Erkennung und fail-closed Erhaltung, Epic 4 Klassifikation und semantische Auflösung. AD-17g/A0-17 verbleiben vollständig in Epic 4. A0-21 ist ebenfalls geteilt: Story 3.10 verantwortet den Incrementality-Teil (Erhaltung unabhängigen Wissens), Story 4.3 die Human-Curation-Semantik (FT-9).
- Das Leasing-/Dirty-Tree-Modell koordiniert Compiler-Runs mit menschlicher Bearbeitung und trägt die Git-Nachvollziehbarkeit, auf die Epic 5 aufsetzt.
@@ -0,0 +1,160 @@
#!/usr/bin/env bash
# Story 3.1 — Sandbox-Edge-Tests der I/O-Matrix (fünf Szenarien) + D-3-Abbruch-Kontrolle
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb31)
set -u
ROOT=$(mktemp -d /tmp/sb31-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw"
cd "$SB"
git init -q
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit) ----------
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Beta](beta.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll verwendet quanten-protocol-schlüssel für die Authentifizierung.
EOF
cat > wiki/beta.md <<'EOF'
---
type: concept
sources:
- resource: raw/beta-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Beta beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
cat > raw/alpha-v1.md <<'EOF'
Evidenz v1: quanten-protocol-schlüssel (Stelle S-1).
EOF
cat > raw/beta-v1.md <<'EOF'
Evidenz v1: Beta-Thema (Stelle S-1).
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT: $BASE"
echo
runlabel() { echo; echo "########## $1 ##########"; }
RG() { if command -v rg >/dev/null; then rg "$@"; else grep -rln "$@"; fi; }
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien)
isolate() { git checkout -q -b "$1" "$BASE" 2>/dev/null || git checkout -q "$1"; git reset -q --hard "$BASE"; git clean -qfd wiki raw; }
probe() { # Pkt. 5-Probe (D-1-Form): Baseline-Diff + porcelain, normalisiert (wiki/-Praefix + .md gestrippt)
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
# =====================================================================
runlabel "S1: HAPPY_PATH_UPDATE (bestehendes Concept wird im bestehenden Pfad aktualisiert)"
# Zuwachs: neue committierte Evidenz mit Term-Ueberschneidung
cat > raw/alpha-v2.md <<'EOF'
Evidenz v2: quanten-protocol-schlüssel erfaehrt eine Rotation (Stelle S-2).
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
# Reconcile: Kandidatenliste (textuell-deterministisch, AD-13)
echo "--- Kandidaten-Erhebung: rg -l 'quanten-protocol-schlüssel' wiki/ ---"
RG -l 'quanten-protocol-schlüssel' wiki/ || true
# Update-Pfad: Mutation im BESTEHENDEN Pfad (Erweitern) + log.md
cat >> wiki/alpha.md <<'EOF'
Seit der Rotation (raw/alpha-v2.md#S-2) gelten die Schlüssel neu.
EOF
cat >> wiki/log.md <<EOF
## 2026-08-19
- Story 3.1-Update: alpha (raw/alpha-v2.md; Baseline $BASE)
EOF
echo "--- Probe (git diff --name-only <BASE> -- wiki/ + git status --porcelain -- wiki/, normalisiert) ---"
probe
echo "--- Erwartet: log, alpha (betroffen); beta NICHT; keine neue Datei (porcelain o.??) ---"
git status --porcelain -- wiki/
echo "--- Duplikat-Check: existiert alpha.md weiterhin exakt 1x? ---"
ls wiki/alpha.md; git status --porcelain -- wiki/ | grep -c '^??' || echo "0 untracked"
# =====================================================================
runlabel "S2: UNTOUCHED_CONCEPT (neue Evidenz betrifft kein bestehendes Concept)"
isolate s2
cat > raw/gamma.md <<'EOF'
Evidenz: vollstaendig neues gamma-observatorium-thema (Stelle S-1).
EOF
git add -A; git commit -qm "Zuwachs raw/gamma.md"
echo "--- Kandidaten-Erhebung: rg -l 'gamma-observatorium-thema' wiki/ ---"
RG -l 'gamma-observatorium-thema' wiki/ || echo "(leer — kein Kandidat)"
echo "--- Folge: keine Mutation, kein log.md-Zusatz (UNTOUCHED_CONCEPT) ---"
echo "--- Probe ---"
probe
echo "(leere Ausgabe = kein Ghost-Diff; leere Menge ist Teilmenge jeder erlaubten Menge)"
# =====================================================================
runlabel "S3: CONCEPT_COLLISION_BESTEHEND (Zielpfad belegt -> Update-Routing, kein Duplikat)"
isolate s3
cat > raw/alpha-v2.md <<'EOF'
Evidenz v2: quanten-protocol-schlüssel (Stelle S-2, ergaenzend).
EOF
git add -A; git commit -qm "Zuwachs"
echo "--- Vor-Mutation-Check: Ziel-Pfad wiki/alpha.md belegt? ---"
test -f wiki/alpha.md && echo "JA — Update-Routing (kein Duplikat, kein stummer Ueberschreiben)"
echo "--- Vor-Mutation-Zustand (muss leer sein) ---"
git status --porcelain -- wiki/; echo "(leer)"
echo "--- Mutation im bestehenden Pfad (Update) ---"
echo "Ergaenzung (raw/alpha-v2.md#S-2)." >> wiki/alpha.md
echo "## 2026-08-19" >> wiki/log.md; echo "- Story 3.1-Update: alpha (raw/alpha-v2.md)" >> wiki/log.md
echo "--- Nach-Mutation: nur M-Eintraege, KEIN ?? (keine neue Datei = kein Duplikat) ---"
git status --porcelain -- wiki/
echo "--- Probe ---"
probe
# =====================================================================
runlabel "S4: CHANGE_DETECTION (R-1: nur der Zuwachs wird als Evidenz interpretiert)"
isolate s4
cat > raw/alpha-v2.md <<'EOF'
Evidenz v2: nur diese Datei ist neu (Stelle S-2).
EOF
git add -A; git commit -qm "Zuwachs"
echo "--- git diff --name-only <BASE> -- raw/ ---"
git diff --name-only "$BASE" -- raw/
echo "--- Erwartet: AUSSCHLIESSLICH raw/alpha-v2.md (alpha-v1.md/beta-v1.md bleiben aussen) ---"
echo "--- SHA-256-Abgleich (Sekundaer-Fingerprint, D-2): alpha-v1 unveraendert? ---"
sha256sum raw/alpha-v1.md
git show "$BASE:raw/alpha-v1.md" | sha256sum
echo "--- (identische Summen = unveraendert; bei Diskrepanz gewinnt git diff, D-2) ---"
# =====================================================================
runlabel "S5: PRE_RUN_RECONCILE (Check-Block vor Mutation; fehlende Bundleroot -> Run-FAIL V-1)"
isolate s5
rm wiki/index.md # Szenario-Setup: Bundleroot fehlt (simuliert, kein Run-Zustand)
echo "--- Check-Block: (4) wiki/index.md-V-1-Vorbedingung ---"
if [ -f wiki/index.md ]; then echo "OK"; else echo "Run-FAIL (V-1, Vertrag §2): wiki/index.md fehlt — keine Mutation darf erfolgen"; fi
git checkout -q "$BASE" -- wiki/index.md # Setup-Rueckstellung (kein Run-Zustand)
echo "--- Zustand nach Abbruch + Rueckstellung (muss leer sein — der Run selbst mutierte nichts) ---"
git status --porcelain -- wiki/; echo "(leer)"
# =====================================================================
runlabel "S6: INPUT_UNCOMMITTED (D-3: Working-Copy vs. HEAD-Check -> benannter Abbruch)"
isolate s6
echo "uncommittete Zwischenstunde" >> raw/alpha-v1.md # simuliert Dirty-Tree
echo "--- Check-Block: Working-Copy vs. HEAD fuer raw/ + wiki/ ---"
DIRTY=$(git status --porcelain -- raw/ wiki/)
if [ -z "$DIRTY" ]; then echo "OK — published/committed Input"; else
echo "Abbruch: published/committed Input erforderlich (AD-17a) — ungepublishter Zustand:"
echo "$DIRTY"
fi
echo
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
@@ -0,0 +1,353 @@
#!/usr/bin/env bash
# Story 3.2 — Sandbox-Tests der textuell-deterministischen Relevanzbestimmung (§3.2)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb32)
# Zweck: Membership-Pin (T1) + Zwei-Run-Identität (T2) + NO_MATCH (T3) +
# log.md-Kontaminations-Pin (T1: der Term liegt AUCH in log.md — die
# Candidate-Liste bleibt trotzdem auf Concept-Pfade definiert, §3.2-Pkt.-2a)
# + LINK_FOLLOWING_ZYKLUS (T5) + TRAVERSAL_REACH_ONLY (T6) +
# TERM_ABLEITUNG_SYNONYM (T7); T1/T4 mit harten Assertions (Review-Patch P-1).
set -u
ROOT=$(mktemp -d /tmp/sb32-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw"
cd "$SB"
git init -q
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Mini-Baum: Concept-Bodies + log.md, die den EXAKTEN Test-Term tragen ----------
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll verwendet deterministische-relevanz-bestimmung für den Abgleich.
EOF
cat > wiki/beta.md <<'EOF'
---
type: concept
sources:
- resource: raw/beta-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Beta beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
# log.md trägt denselben Term — der Kontaminations-Fall, den die Exklusion entschärft:
cat > wiki/log.md <<'EOF'
# Log
## 2026-08-19
- Story-3.2-Update: alpha (raw/alpha-v2.md; deterministische-relevanz-bestimmung).
EOF
cat > raw/alpha-v1.md <<'EOF'
Evidenz v1: deterministische-relevanz-bestimmung (Stelle S-1).
EOF
cat > raw/beta-v1.md <<'EOF'
Evidenz v1: Beta-Thema (Stelle S-1).
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT: $BASE (Sandbox-Root: $ROOT; loeschbar: rm -rf $ROOT)"
echo
# ---------- Helfer: candidate-Erhebung mit tool-korrekter Exklusion (§3.2-Pkt.-2a) ----------
# rg-Form: rg -l '<term>' -g '!log.md' wiki/
# grep-Form:grep -rl '<term>' --exclude=log.md wiki/
terms() { # $1=Term; nutzt rg wenn vorhanden (native -g-Exklusion), sonst grep --exclude
if command -v rg >/dev/null 2>&1; then
rg -l "$1" -g '!log.md' wiki/
else
grep -rl "$1" --exclude=log.md wiki/
fi
}
# Normalisierung der Ausgabe: wiki/-Präfix + .md-Suffix strippen (§3.2-Pkt.-3a)
normalize() { sed -e 's|^wiki/||' -e 's|\.md$||'; }
# ---------- Helfer: Stufe b/c (Erhebung) + Terminormalisierung (Review-Loop-2, D-2) ----------
# Stufe b — index.md-Traversal (TRAVERSAL_REACH_ONLY, §3.2-Pkt.-2b):
# $1 = normalisierter Treffer-Name einer index.md (z. B. 'trafo/index' oder 'index');
# gibt die darunter gewurzelten Concept-Pfade (normalisiert) aus — die Concept-Links
# der getroffenen index.md, file-relativ aufgelöst (§5.7-Pkt.-4).
traverse() {
local idx="wiki/$1.md" d tgt
d=$(dirname "$idx")
grep -oE '\]\([^)]+\.md\)' "$SB/$idx" 2>/dev/null | sed -e 's|^\](||' -e 's|)$||' | while read -r tgt; do
case "$tgt" in ./*) tgt=${tgt#./} ;; esac
printf '%s/%s\n' "$d" "$tgt" | sed -e 's|^wiki/||' -e 's|\.md$||'
done
}
# Stufe c — Link-Following mit besuchter Menge (LINK_FOLLOWING_ZYKLUS, §3.2-Pkt.-2c):
# $@ = in Stufe a getroffene Concept-Pfade (normalisiert, z. B. 'sigma');
# Concept-Links werden file-relativ verfolgt (§5.7-Pkt.-4); ein bereits besuchter
# Concept-Pfad wird nicht erneut besucht — Zyklen (A -> B -> A) enden, die
# Candidate-Liste bleibt endlich. Ausgabe: getroffene + erreichte Pfade,
# in Besuchsreihenfolge (Stufe a zuerst, dann Stufe c; §3.2-Pkt.-3b).
follow() {
local visited="$ROOT/visited.$$" seed f base tgt res
: > "$visited"
local queue=()
for seed in "$@"; do
printf '%s\n' "$seed" >> "$visited"
queue+=("$seed")
done
while [ "${#queue[@]}" -gt 0 ]; do
f="wiki/${queue[0]}.md"
queue=("${queue[@]:1}")
base=$(dirname "$f")
for tgt in $(grep -oE '\]\([^)]+\.md\)' "$SB/$f" 2>/dev/null | sed -e 's|^\](||' -e 's|)$||'); do
case "$tgt" in ./*) tgt=${tgt#./} ;; esac
res=$(printf '%s/%s\n' "$base" "$tgt" | sed -e 's|^wiki/||' -e 's|\.md$||')
grep -qx "$res" "$visited" && continue
printf '%s\n' "$res" >> "$visited"
queue+=("$res")
done
done
cat "$visited"
}
# Terminormalisierung (§3.2-Pkt.-1b): (i) lowercasing; (ii) Binde-Varianten-Kollaps
# [-_ ] -> - (jedes Vorkommen wird in einen einzelnen Bindestrich kollabiert).
# Normalisierte Form = die Form, die in der Registry gespeichert/verglichen wird.
norm_term() { printf '%s' "$1" | LC_ALL=C tr 'A-Z' 'a-z' | sed -e 's/[-_ ]/-/g'; }
# Registry-Lookup (§3.2-Pkt.-1b, canonical-terms.md-Format):
# $1 = normalisierter Term, $2 = Registry-Datei; gibt die canonische Form aus,
# oder — nicht auflösbar — den Term selbst (§3.2-Pkt.-1b: kein stiller Ausschluss).
lookup() {
awk -F'|' -v t="$1" '
NR > 2 {
canon = $2; gsub(/^[ \t]+|[ \t]+$/, "", canon)
if (canon ~ /^—/) next
if (canon == t) { print canon; found = 1; exit }
vars = $3; gsub(/`/, " ", vars)
n = split(vars, a, ",")
for (i = 1; i <= n; i++) {
gsub(/^[ \t]+|[ \t]+$/, "", a[i])
if (a[i] != "" && a[i] == t) { print canon; found = 1; exit }
}
}
END { if (!found) print t }
' "$2"
}
# =====================================================================
runlabel() { echo; echo "########## $1 ##########"; }
runlabel "T1: Membership-Pin — Term trifft Concept-Body UND log.md; Candidate-Liste nur Concept-Pfade"
echo "--- Kandidaten-Erhebung: terms 'deterministische-relevanz-bestimmung' (Exklusions-Form) ---"
terms 'deterministische-relevanz-bestimmung' | normalize
echo "--- Erwartet: AUSSCHLIESSLICH 'alpha' (Concept-Pfad, ohne wiki/ + ohne .md) ---"
T1_OUT=$(terms 'deterministische-relevanz-bestimmung' | normalize)
if [ "$T1_OUT" = "alpha" ]; then
echo "RESULT: PASS (Candidate-Liste = [alpha]; log.md strukturell exkludiert, §3.2-Pkt.-2a)"
else
echo "FAIL: T1 — erwartet 'alpha', erhalten: '${T1_OUT}'" >&2
exit 1
fi
echo "--- Kontaminations-Kontrolle: was OHNE Exklusion getroffen würde (rg-OHNE -g / grep-OHNE --exclude) ---"
if command -v rg >/dev/null 2>&1; then
rg -l 'deterministische-relevanz-bestimmung' wiki/ | normalize
else
grep -rl 'deterministische-relevanz-bestimmung' wiki/ | normalize
fi
echo "--- OHNE Exklusion trifft es 'alpha' UND 'log' — die Exklusion (§3.2-Pkt.-2a) ist damit reell getestet ---"
# =====================================================================
runlabel "T2: Zwei-Run-Identität (AD-17h/A0-19) — zwei unabhängige Läufe, byte-identische Candidate-Liste"
FIRST1=$(terms 'deterministische-relevanz-bestimmung' | normalize)
SECOND1=$(terms 'deterministische-relevanz-bestimmung' | normalize)
FIRST2=$(terms 'alpha' | normalize)
SECOND2=$(terms 'alpha' | normalize)
echo "Lauf 1 (Term deterministische-relevanz-bestimmung): $FIRST1"
echo "Lauf 2 (Term deterministische-relevanz-bestimmung): $SECOND1"
echo "Lauf 1 (Term alpha): $FIRST2 # 'index' ist ein Stufe-a-Treffer auf wiki/index.md — Stufe b (TRAVERSAL_REACH_ONLY, §3.2-Pkt.-2b) löst die darunter gewurzelten Concept-Pfade als Candidate aus (index.md selbst ist KEIN Concept-Kandidat); kein Kontaminations-Fehler"
echo "Lauf 2 (Term alpha): $SECOND2 # identische Ausgabe (Zwei-Run-Identität, AD-17h)"
if [ "$FIRST1" = "$SECOND1" ] && [ "$FIRST2" = "$SECOND2" ]; then
echo "RESULT: IDENTISCH (gleicher Git-State + gleiches Eingabeset -> identische Candidate-Liste, AD-17h)"
else
echo "RESULT: ABWEICHUNG (Determinismus-Vertrag verletzt)" >&2
exit 1
fi
# =====================================================================
runlabel "T3: NO_MATCH — Term trifft kein bestehendes Concept -> leere Candidate-Liste -> UNTOUCHED_CONCEPT"
echo "--- Kandidaten-Erhebung: terms 'vollkommen-neuer-gamma-termin' ---"
RES=$(terms 'vollkommen-neuer-gamma-termin' | normalize)
echo "Candidate-Liste: '${RES}' (leer = gewuenscht)"
if [ -z "$RES" ]; then
echo "RESULT: LEER (NO_MATCH -> UNTOUCHED_CONCEPT: keine Mutation, kein log.md-Zusatz; leere Menge ⊆ erlaubte Menge)"
else
echo "RESULT: FEHLER (erwartet leer)" >&2
exit 1
fi
# =====================================================================
runlabel "T4: Determinismus-Vertrag praktisch — Term in Mehrfach-Konzepten, Konsolidierung zur Vereinigung"
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma teilt den Term deterministische-relevanz-bestimmung (Stelle S-1).
EOF
git add -A; git commit -qm "Zuwachs gamma (teilt den Term)"
echo "--- Kandidaten-Erhebung (Term in alpha + gamma; §3.2-Pkt.-3b: innerhalb jeder Stufe rein lexikografisch, LC_ALL=C): ---"
terms 'deterministische-relevanz-bestimmung' | normalize | LC_ALL=C sort | paste -sd, -
echo "--- Erwartet: alpha,gamma (beide Concept-Pfade, lexikografisch; log ABGESCHNITTEN) ---"
T4_OUT=$(terms 'deterministische-relevanz-bestimmung' | normalize | LC_ALL=C sort | paste -sd, -)
if [ "$T4_OUT" = "alpha,gamma" ]; then
echo "RESULT: PASS (Vereinigung beider Concept-Pfade, lexikografisch; log.md exkludiert)"
else
echo "FAIL: T4 — erwartet 'alpha,gamma', erhalten: '${T4_OUT}'" >&2
exit 1
fi
# =====================================================================
# T5-T7 (Review-Loop-2, D-2): Zyklen-Endlichkeit, Traversal-Only, Synonym-Auflösung
# =====================================================================
# ---------- T5: LINK_FOLLOWING_ZYKLUS — A -> B -> A endet endlich (besuchte Menge) ----------
runlabel "T5: LINK_FOLLOWING_ZYKLUS — Zyklus alpha <-> omega endet endlich; Candidate-Liste endlich (§3.2-Pkt.-2c/3b)"
cat > wiki/omega.md <<'EOF'
---
type: concept
sources:
- resource: raw/omega-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Omega verweist zirkular auf Alpha: [Alpha](alpha.md).
EOF
# Backlink: alpha -> omega (erzeugt den echten A->B->A-Zyklus, der die besuchte Menge übt):
printf '\nOmega-Verweis: [Omega](omega.md).\n' >> wiki/alpha.md
git add -A; git commit -qm "Zuwachs omega (Link-Zyklus alpha <-> omega)"
echo "--- Stufe a (Term zirkular): ---"
STAGE_A=$(terms 'zirkular' | normalize | LC_ALL=C sort | paste -sd, -)
echo "$STAGE_A"
echo "--- Stufe c (Follow aus Stufe a; ZYKLUS omega -> alpha -> omega ... MUSS enden): ---"
STAGE_C=$(follow omega)
echo "$STAGE_C"
# Deduplication über die besuchte Menge (sort -u: endliche, doppelungsfreie Menge —
# die Stufe-Ordnung a→c ist bereits in STAGE_A/STAGE_C sichtbar; hier wird die Endlichkeit geprüft):
T5_EXPECT="alpha,omega"
T5_LIST=$(printf '%s\n%s\n' "$STAGE_A" "$STAGE_C" | grep -v '^$' | LC_ALL=C sort -u | paste -sd, -)
if [ "$T5_LIST" = "$T5_EXPECT" ]; then
echo "RESULT: PASS (endlich, dedupliziert: omega, alpha — Zyklus endete; jeder Pfad genau einmal)"
else
echo "FAIL: T5 — erwartet '$T5_EXPECT', erhalten: '$T5_LIST'" >&2
exit 1
fi
# ---------- T6: TRAVERSAL_REACH_ONLY — Term nur in index.md -> gewurzelte Concept-Pfade ----------
runlabel "T6: TRAVERSAL_REACH_ONLY — Term 'trafo-gebiet' trifft NUR wiki/trafo/index.md -> gewurzelte Concept-Pfade sind die Treffer (index.md selbst kein Concept-Kandidat, §3.2-Pkt.-2b)"
mkdir -p wiki/trafo
cat > wiki/trafo/index.md <<'EOF'
# Trafo-Gebiet-Index
Dieses trafo-gebiet wird hier indexiert.
- [Delta](delta.md)
- [Epsilon](epsilon.md)
EOF
cat > wiki/trafo/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/delta-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Delta-Body (trägt den Index-Term NICHT — nur der Index trägt ihn).
EOF
cat > wiki/trafo/epsilon.md <<'EOF'
---
type: concept
sources:
- resource: raw/epsilon-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Epsilon-Body (trägt den Index-Term NICHT — nur der Index trägt ihn).
EOF
git add -A; git commit -qm "Zuwachs trafo/Area (Index trägt den Term, Bodies nicht)"
echo "--- Stufe a (Term trafo-gebiet; nur index.md trifft — der Traversal-Trigger): ---"
STAGE_A=$(terms 'trafo-gebiet' | normalize)
echo "$STAGE_A"
echo "--- Stufe b (Traversal: gewurzelte Concept-Pfade der getroffenen index.md): ---"
STAGE_B=$(traverse 'trafo/index')
echo "$STAGE_B"
# Kandidaten = gewurzelte Concept-Pfade (lexikografisch; index.md selbst ist KEIN Concept-Kandidat):
T6_OUT=$(printf '%s\n' "$STAGE_B" | grep -v '^$' | LC_ALL=C sort | paste -sd, -)
if [ "$T6_OUT" = "trafo/delta,trafo/epsilon" ]; then
echo "RESULT: PASS (Candidate-Liste = gewurzelte Concept-Pfade; Index-Body-Trigger, kein Kontaminations-Fehler)"
else
echo "FAIL: T6 — erwartet 'trafo/delta,trafo/epsilon', erhalten: '$T6_OUT'" >&2
exit 1
fi
# ---------- T7: TERM_ABLEITUNG_SYNONYM — Schreibvariante -> canonische Form -> Kandidaten ----------
runlabel "T7: TERM_ABLEITUNG_SYNONYM — gezogene Schreibvariante wird über canonical-terms.md auf die canonische Form aufgelöst (Lookup auf normalisierter Form, §3.2-Pkt.-1b)"
mkdir -p "$ROOT/registry"
cat > "$ROOT/registry/canonical-terms.md" <<'EOF'
# Sandbox-Registry (Test-Doppel des canonical-terms-Formats; append-only, ein Eintrag = Canon + Varianten;
# D-5-Invariante: Canon- UND Varianten-Spalte tragen ausschliesslich NORMALISIERTE Formen)
| canonische Form | erlaubte Schreibvarianten | Semantik |
|---|---|---|
| quanten-protocol | `quantenprotocol` | Sandbox-Semantik: Test-Synonymgruppe |
EOF
# Invariante-Check (D-5): keine normalisierte Form in beiden Spalten oder in zwei Einträgen:
# hier: 'quanten-protocol' nur Canon-Spalte, 'quantenprotocol' nur Varianten-Spalte — eindeutig.
# (Registry liegt ausserhalb des Sandbox-Git-Roots — sie modelliert den Git-State-Anteil des Resolvers;
# der Sandbox-Repo-Commit trägt sie deshalb nicht, hier nur Notiz statt Commit-Noise:)
echo "Registry (ausserhalb des Sandbox-Roots, modelliert den Resolver-Git-State): $ROOT/registry/canonical-terms.md"
echo "--- Gezogene Schreibvarianten (Roh-Notat): 'Quanten Protocol' + 'QuantenProtocol' ---"
RAW_A='Quanten Protocol'
RAW_B='QuantenProtocol'
NORM_A=$(norm_term "$RAW_A")
NORM_B=$(norm_term "$RAW_B")
echo "Normalisierung (§3.2-Pkt.-1b): '$RAW_A' -> '$NORM_A'; '$RAW_B' -> '$NORM_B'"
CANON_A=$(lookup "$NORM_A" "$ROOT/registry/canonical-terms.md")
CANON_B=$(lookup "$NORM_B" "$ROOT/registry/canonical-terms.md")
echo "Lookup: '$NORM_A' -> '$CANON_A'; '$NORM_B' -> '$CANON_B'"
if [ "$CANON_A" = "quanten-protocol" ] && [ "$CANON_B" = "quanten-protocol" ]; then
echo "RESULT: PASS (beide Varianten -> canonische Form 'quanten-protocol'; Auflösung auf normalisierter Form)"
else
echo "FAIL: T7 — erwartet 'quanten-protocol' für beide, erhalten: '$CANON_A' / '$CANON_B'" >&2
exit 1
fi
# Nicht aufgelöster Term wird wie notiert (Kollaps-normalisiert) verwendet — kein stiller Ausschluss:
CANON_MISS=$(lookup 'nicht-in-registry' "$ROOT/registry/canonical-terms.md")
if [ "$CANON_MISS" = 'nicht-in-registry' ]; then
echo "PASS (Negativ-Fall: 'nicht-in-registry' -> wie notiert, kein stiller Ausschluss)"
else
echo "FAIL: T7-Negativ — erwartet 'nicht-in-registry', erhalten: '$CANON_MISS'" >&2
exit 1
fi
echo
echo "===== Sandbox abgeschlossen (alle 7 Tests) ====="
echo "Sandbox-Root: $ROOT"
@@ -0,0 +1,392 @@
#!/usr/bin/env bash
# Story 3.3 — Sandbox-Tests der operationellen Update-Formen (§5.9 Pkt. 2, Revision 2.8)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb33)
# Zweck: alle drei Update-Formen (Erweitern + Präzisieren + Korrigieren) + No-Op
# durchspielen; die Erhaltungs-Invariante (§5.9 Pkt. 5) als HARDE Assertion
# erzwingen (Diff nach Normalisierung ⊆ {betroffener Concept-Pfad, log}; keine
# neue Datei; kein Ghost-Diff); Zwei-Run-Identität (AD-17h/A0-19);
# Frontmatter-Konformität je mutiertem Concept (Vertrag §3.3/§3.4-Subset).
# Linux-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/-Baum.
set -u
ROOT=$(mktemp -d /tmp/sb33-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw"
cd "$SB"
git init -q
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit) ----------
# Concept alpha mit drei belegten Aussagen; Aussagen 1/2 sind fachlich korrekt,
# Aussage 3 ist fehlerhaft (Korrigieren-Ziel des Szenarios U3). beta ist ein
# nicht-betroffenes Kontroll-Concept (Ghost-Diff-Kontrolle, U5).
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Beta](beta.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).
Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).
Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).
EOF
cat > wiki/beta.md <<'EOF'
---
type: concept
sources:
- resource: raw/beta-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Beta beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz v1: deterministische-init-sequenz für den Abgleich.
### S-2
Evidenz v1: keine Rotation der Schlüssel.
### S-3
Evidenz v1: Alpha läuft auf isolierten Netzen.
EOF
cat > raw/beta-v1.md <<'EOF'
### S-1
Evidenz v1: Beta-Thema.
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT: $BASE"
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo
runlabel() { echo; echo "########## $1 ##########"; }
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien)
isolate() { git checkout -q -b "$1" "$BASE" 2>/dev/null || git checkout -q "$1"; git reset -q --hard "$BASE"; git clean -qfd wiki raw; }
# Pkt.-5-Probe (D-1-Form): Baseline-Diff + porcelain, normalisiert
# (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert; deterministisch, AD-17h)
probe() {
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
# Erwartete erlaubte Menge = Space-getrennte normalisierte Pfade
# (z. B. "alpha log"). Jeder Proben-Eintrag MUSS in der erlaubten Menge liegen —
# sonst Ghost-Diff (nicht betroffener Pfad) → HARD-FAIL, Exit 1 (NFR-4/FT-6).
assert_invariant() {
local expected="$1" got p bad=0 rcs=0
got=$(inv_set)
for p in $got; do
case " $expected " in
*" $p "*) ;;
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
esac
done
# Keine neue Datei (Duplikat-Kontrolle: die ??-Sicht fehlt git diff allein)
rcs=$(git status --porcelain -- wiki/ | grep -c '^??' || true)
[ "$rcs" -eq 0 ] || { echo "HARD-FAIL (keine neue Datei): $rcs ungetrackte neue Datei(en) unter wiki/ (Duplikat/Ghost-Diff)" >&2; bad=1; }
if [ "$bad" -eq 0 ]; then
echo "RESULT: PASS — Probe {$got} ⊆ erlaubte Menge {$expected}; keine neue Datei; kein Ghost-Diff"
else
exit 1
fi
}
# ---------- Frontmatter-Konformitaet je mutiertem Concept (Vertrag §3.3/§3.4, §6.5) ----------
# $1=Datei; $2...=erwartete sources[].resource-Werte. Prueft: Top-Level-Key-Subset
# {type,sources,generated,verified,status,stale_after}, sources-Eintrag-Key-Subset
# (Vertrag §3.3-Innen-Ebene), type=concept, generated.at = volle ISO-8601-Datetime
# (die erlaubten Key-Mengen sind inline im awk-Subset-Vergleich hartkodiert).
assert_frontmatter() {
local f="$1"; shift
local r bad=0
# Kontextuelle Validierung ueber awk: Top-Level-Subset {type,sources,generated,
# verified,status,stale_after}; Innen-Ebenen je Sektion — sources: Vertrag §3.3-
# Subset {resource,id,title,author,usage_count,last_modified}; generated/verified:
# {by,at} (Vertrag §3.4/§3.5). Jeder andere Key = unbefugt (Punkt 6, §6.5).
local v
v=$(awk '
/^---$/{n++; if(n==2) exit; next}
/^[A-Za-z_][A-Za-z0-9_-]*:/{
k=$0; sub(/:.*/,"",k)
if (k=="sources") top="sources"
else if (k=="generated" || k=="verified") top="genver"
else top="other"
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
# Duplikat-Prohibition: jeder Top-Level-Key hoechstens einmal (Punkt 13).
if (seenk[k]++) print "DUP_KEY:" k
# Canonische Reihenfolge (Vertrag §3.x Normalform: type, sources, generated,
# verified, status, stale_after): jeder neue Key-Kandidat muss der canonichen
# Ordnung folgen — Verstoß via Ordnungs-Rang erkannt (ORDER_VIOLATION).
r=0
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
if (r>0) lastr=r
next
}
/^[[:space:]]/{
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
ik=substr($0,1,RLENGTH-1)
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
if (top=="other") print "INNER_UNBEFUGT:" ik
}
}
' "$f")
if [ -n "$v" ]; then
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
exit 1
fi
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
for r in "$@"; do
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
done
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
}
# ---------- Update-Helfer (deterministisch; at gepinnt fuer Zwei-Run-Identitaet) ----------
AT_NEW="2026-08-19T12:00:00Z"
bump_at() { sed -i "s|^ at: .*| at: $AT_NEW|" "$1"; }
add_source() { # $1=Datei $2=resource $3=id (Eintrag vor der generated:-Zeile einfuegen)
sed -i "s|^generated:| - resource: $2\n id: $3\ngenerated:|" "$1"
}
log_update() { # $1=Concept-Pfad $2=raw-Datei
printf '\n## 2026-08-19\n- Story 3.1-Update: %s (%s; Baseline %s)\n' "$1" "$2" "$BASE" >> wiki/log.md
}
# =====================================================================
runlabel "U1: ERWEITERN — neue belegte Aussage, bestehender Pfad wird erweitert (AC-1/FR-6)"
isolate u1
cat > raw/alpha-v2.md <<'EOF'
### S-4
Evidenz v2 (neue Aussage): Alpha-Replikation traegt einen zusaetzlichen Schluessel-Rotationszyklus.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- Kandidaten-Erhebung: rg -l 'schluessel-rotationszyklus' wiki/ ---"
if command -v rg >/dev/null 2>&1; then rg -l 'schluessel-rotationszyklus' -g '!log.md' wiki/ || true
else grep -rl 'schluessel-rotationszyklus' --exclude=log.md wiki/ || true; fi
echo "--- UPDATE: neue Aussage als Absatz angefuegt (§5.5-Inline-Beleg), sources-Zuwachs s2, at-Bump ---"
cat >> wiki/alpha.md <<'EOF'
Alpha führt für die Replikation einen zusätzlichen Schlüssel-Rotationszyklus (raw/alpha-v2.md#S-4).
EOF
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
echo "--- Struktur-Erhaltung: bestehende drei Aussagen byte-identisch erhalten? ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 wurde beim Erweitern umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 (Netze) wurde beim Erweitern umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 3 wurde beim Erweitern umgeschrieben" >&2; exit 1; }
echo "RESULT: PASS — bestehende belegte Aussagen unveraendert (Struktur-Erhaltungsregel Erweitern)"
echo "--- Probe (Pkt. 5, vor dem Commit) ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
# =====================================================================
runlabel "U2: PRAEZISIEREN — bestehende Aussage wird an ihr selbst geschaerft (AC-2/FR-6)"
isolate u2
cat > raw/alpha-v2.md <<'EOF'
### S-1
Evidenz v2 (Praezisierung): die deterministische-init-sequenz wird nach der initialen Synchronisation zusaetzlich rotiert.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- UPDATE: Aussage 1 an Position geschaerft, Beleg nachgefuehrt (Multi-Beleg §5.5); kein Satz-Umbau ---"
sed -i 's|^Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1)\.|Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich und rotiert sie nach der initialen Synchronisation zusätzlich (raw/alpha-v1.md#S-1; raw/alpha-v2.md#S-1).|' wiki/alpha.md
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
echo "--- Struktur-Erhaltung: Aussagen 2/3 unveraendert, geschaerfte Aussage 1 an Position? ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich und rotiert sie nach der initialen Synchronisation zusätzlich (raw/alpha-v1.md#S-1; raw/alpha-v2.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: geschaerfte Aussage fehlt" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 wurde beim Praezisieren veraendert" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 3 wurde beim Praezisieren veraendert" >&2; exit 1; }
echo "RESULT: PASS — Schaerfung an der Aussage selbst, Position/Struktur erhalten, kein Satz-Umbau"
echo "--- Probe ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
# =====================================================================
runlabel "U3: KORRIGIEREN — fehlerhafte Aussage abgeloest, nicht still geloescht (AC-2, AD-16-Default)"
isolate u3
cat > raw/alpha-v2.md <<'EOF'
### S-2
Evidenz v2 (Korrektur): Alpha rotiert seine Schluessel bei jeder Sitzung.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- UPDATE: Aussage 3 wird explizit abgeloest + Ersetzungsbeleg; keine stille Loeschung ---"
sed -i 's|^Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2)\.|Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2) — überholt durch: Alpha rotiert seine Schlüssel bei jeder Sitzung (raw/alpha-v2.md#S-2).|' wiki/alpha.md
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
echo "--- AD-16-Default: alte Aussagen 1/2 textuell erhalten (keine stille Loeschung, keine flankierende Umschreibung)? ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 wurde beim Korrigieren flankierend umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 wurde beim Korrigieren flankierend umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2)' wiki/alpha.md || { echo "HARD-FAIL: alte Aussage wurde still geloescht oder ihr Wortlaut verändert (AD-16, Struktur-Erhaltungsregel Korrigieren)" >&2; exit 1; }
grep -qF 'rotiert seine Schlüssel bei jeder Sitzung (raw/alpha-v2.md#S-2)' wiki/alpha.md || { echo "HARD-FAIL: Ersetzungsbeleg fehlt" >&2; exit 1; }
echo "RESULT: PASS — Abloesung mit Ersetzungsbeleg, Provenienz erhalten (AD-16-Default Erhaltung)"
echo "--- Probe ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
# =====================================================================
runlabel "U4: NO_OP — Evidenz ist bereits vollstaendig im Body (engere Auslegung, Pkt. 2)"
isolate u4
cat > raw/alpha-v2.md <<'EOF'
Evidenz v2 (Redundanz): deterministische-init-sequenz fuer den Abgleich; Alpha rotiert seine Schluessel nie.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- Kandidaten-Erhebung: Terme treffen alpha; die Evidenz ist bereits vollstaendig enthalten ---"
echo "--- UPDATE: KEINE Body-Mutation, KEIN at-Bump, KEIN sources-Zusatz, KEIN log.md-Eintrag (byte-identisch) ---"
if git status --porcelain -- wiki/ | grep -q .; then
echo "HARD-FAIL (No-Op): Wiki-Baum ist nicht byte-identisch" >&2
git status --porcelain -- wiki/
exit 1
fi
echo "RESULT: PASS — No-Op: keine Mutation, kein at-Bump, kein sources-Zusatz, kein log.md-Eintrag (byte-identisch)"
echo "--- Probe (leer = Ghost-Diff-negativ, leere Menge ist Teilmenge jeder erlaubten Menge) ---"; probe
assert_invariant ""
# =====================================================================
runlabel "U5: NEGATIV-KONTROLLE — Assertions-Mechanik erkennt einen Ghost-Diff (nicht vacuous)"
isolate u5
echo "unbefugte Mutation auf nicht-betroffenem beta" >> wiki/beta.md # simulierter Ghost-Diff
echo "--- Probe (erwartet: beta ausserhalb der erlaubten Menge {alpha log}) ---"; probe
if ( assert_invariant "alpha log" ) >/dev/null 2>&1; then
echo "HARD-FAIL (Assertions-Mechanik): Ghost-Diff auf beta wurde NICHT erkannt" >&2
exit 1
else
echo "RESULT: PASS — Ghost-Diff auf beta wurde erkannt (Assertions-Mechanik loest verlaesslich HARD-FAIL aus, kein Ghost-Diff kommt durch)"
fi
# =====================================================================
runlabel "U6: ZWEI-RUN-IDENTITAET (AD-17h/A0-19) — gleicher Git-State + gleiches Eingabeset -> identischer Update-Vorgang"
# Zwei unabhaengige Runs ueber dieselbe Evidenz (Erweitern); die Ergebnisse
# (mutierter Body + Frontmatter + Status) muessen byte-identisch sein.
run_erweitern() { # $1 = Branchname; mutiert Worktree, dann git add -A (Staging)
isolate "$1"
cat > raw/alpha-v2.md <<'EOF'
### S-4
Evidenz v2 (neue Aussage): Alpha-Replikation traegt einen zusaetzlichen Schluessel-Rotationszyklus.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
cat >> wiki/alpha.md <<'EOF'
Alpha führt für die Replikation einen zusätzlichen Schlüssel-Rotationszyklus (raw/alpha-v2.md#S-4).
EOF
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
git add -A # Staging — die Mutationen sind Teil des Runs (Commit-Boundary = Mutations-Boundary)
}
run_erweitern u6a
A_ALPHA=$(sha256sum wiki/alpha.md | cut -d' ' -f1) # Worktree-Inhalt = staged-Inhalt (git add)
A_LOG=$(sha256sum wiki/log.md | cut -d' ' -f1)
git commit -qm "Run A (Erweitern)"
A_ALPHA_COM=$(git show HEAD:wiki/alpha.md | sha256sum | cut -d' ' -f1)
run_erweitern u6b
B_ALPHA=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
B_LOG=$(sha256sum wiki/log.md | cut -d' ' -f1)
git commit -qm "Run B (Erweitern)"
B_ALPHA_COM=$(git show HEAD:wiki/alpha.md | sha256sum | cut -d' ' -f1)
echo "Run A (alpha.md): $A_ALPHA"
echo "Run B (alpha.md): $B_ALPHA"
echo "Run A (log.md): $A_LOG"
echo "Run B (log.md): $B_LOG"
echo "Run A commit (alpha.md): $A_ALPHA_COM ; Run B commit (alpha.md): $B_ALPHA_COM"
if [ "$A_ALPHA" = "$B_ALPHA" ] && [ "$A_LOG" = "$B_LOG" ] && [ "$A_ALPHA_COM" = "$B_ALPHA_COM" ]; then
echo "RESULT: PASS — zwei unabhaengige Runs ueber dieselbe Evidenz -> identischer Update-Vorgang (AD-17h/A0-19)"
else
echo "HARD-FAIL (Determinismus-Vertrag): Run A und Run B weichen ab" >&2
exit 1
fi
# =====================================================================
runlabel "U7: MEHRFACH_TREFFER — zwei Neuevidenzen desselben Runs treffen denselben Concept-Pfad (Konsolidierung, I/O-Matrix)"
# Matrix-Zeile MEHRFACH_TREFFER: mehrere neue Wissenseinheiten desselben Runs auf
# denselben Pfad -> EIN konsolidiertes Update (eine Body-Mutation, EIN log.md-Eintrag,
# EIN konsolidierter sources-Zuwachs, EIN generated.at). Reihenfolge = Zuwachs-Sicht
# (Ankunftsreihenfolge der Belege); lexikografischer Tie-Break bei identischem Ort
# (Body-Ende) -> alpha-v2.md vor alpha-v3.md.
isolate u7
cat > raw/alpha-v2.md <<'EOF'
### S-4
Evidenz v2 (neue Aussage): Alpha-Replikation nutzt Quorum-Bestaetigung.
EOF
cat > raw/alpha-v3.md <<'EOF'
### S-4
Evidenz v3 (neue Aussage): Alpha-Replikation waechst linear zum Cluster.
EOF
git add -A; git commit -qm "Zwei Einheiten, ein Run (raw/alpha-v2.md + raw/alpha-v3.md)"
echo "--- Kandidaten-Erhebung: beide Terme treffen denselben Pfad alpha ---"
echo "--- KONSOLIDIERTES UPDATE: beide Aussagen als Absaetze angehaengt (Zuwachs-Sicht: v2 vor v3), EIN log-Eintrag, sources-Zuwachs s2+s3, EIN at ---"
cat >> wiki/alpha.md <<'EOF'
Alpha-Replikation nutzt Quorum-Bestaetigung (raw/alpha-v2.md#S-4).
Alpha-Replikation waechst linear zum Cluster (raw/alpha-v3.md#S-4).
EOF
add_source wiki/alpha.md raw/alpha-v2.md s2
add_source wiki/alpha.md raw/alpha-v3.md s3
bump_at wiki/alpha.md
log_update alpha "raw/alpha-v2.md + raw/alpha-v3.md"
echo "--- Konsolidierung: genau EIN log.md-Eintrag und EIN generated.at trotz zweier Einheiten? ---"
[ "$(grep -c 'Story 3.1-Update: alpha' wiki/log.md)" -eq 1 ] || { echo "HARD-FAIL: Mehrfach-Treffer erzeugte nicht exakt einen log.md-Eintrag (Konsolidierung oder Eintragspflicht Pkt. 4 verletzt)" >&2; exit 1; }
grep -qF 'raw/alpha-v2.md' wiki/log.md || { echo "HARD-FAIL: konsolidierter log.md-Eintrag verlinkt raw/alpha-v2.md nicht (Pkt. 4, genutzte raw/-Quellen)" >&2; exit 1; }
grep -qF 'raw/alpha-v3.md' wiki/log.md || { echo "HARD-FAIL: konsolidierter log.md-Eintrag verlinkt raw/alpha-v3.md nicht (Pkt. 4, genutzte raw/-Quellen)" >&2; exit 1; }
[ "$(grep -c '^ at:' wiki/alpha.md)" -eq 1 ] || { echo "HARD-FAIL: Mehrfach-Treffer erzeugte mehrere generated.at" >&2; exit 1; }
echo "--- Struktur-Erhaltung: alte Aussagen byte-identisch, beide neuen Aussagen vorhanden, Zuwachs-Sicht-Ordnung v2-vor-v3 ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 wurde beim konsolidierten Update umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 wurde beim konsolidierten Update umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 3 wurde beim konsolidierten Update umgeschrieben" >&2; exit 1; }
grep -qF 'Quorum-Bestaetigung (raw/alpha-v2.md#S-4)' wiki/alpha.md || { echo "HARD-FAIL: neue Aussage aus v2 fehlt" >&2; exit 1; }
grep -qF 'waechst linear zum Cluster (raw/alpha-v3.md#S-4)' wiki/alpha.md || { echo "HARD-FAIL: neue Aussage aus v3 fehlt" >&2; exit 1; }
v2pos=$(grep -n 'Quorum-Bestaetigung' wiki/alpha.md | cut -d: -f1); v3pos=$(grep -n 'waechst linear' wiki/alpha.md | cut -d: -f1)
[ "$v2pos" -lt "$v3pos" ] || { echo "HARD-FAIL: Zuwachs-Sicht-Ordnung verletzt (v3 vor v2)" >&2; exit 1; }
echo "RESULT: PASS — ein konsolidiertes Update (eine Body-Aenderung, ein log-Eintrag, ein sources-Zuwachs s2+s3, ein at); Zuwachs-Sicht-Ordnung v2-vor-v3"
echo "--- Probe ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md raw/alpha-v3.md
# =====================================================================
runlabel "U8: STRUKTUR-VERLETZUNG — unbefugter Frontmatter-Key wird erkannt (Pkt. 5 Struktur-Ebene, P2-Check-Element (6))"
isolate u8
echo "--- simulierter Verstoß gegen die Struktur-Erhaltung: unbefugter Frontmatter-Key 'foo' im YAML-Block ---"
awk '/^type: concept$/{print; print "foo: bar"; next} {print}' wiki/alpha.md > /tmp/sb33-fm && mv /tmp/sb33-fm wiki/alpha.md
echo "--- Probe + Frontmatter-Konformität (erwartet: HARD-FAIL, Verstoß textuell benannt, NFR-4) ---"; probe
if ( assert_invariant "alpha log" && assert_frontmatter wiki/alpha.md ) >/dev/null 2>&1; then
echo "HARD-FAIL (Struktur-Ebene): unbefugter Frontmatter-Key wurde NICHT erkannt (Pkt. 5/Pkt. 6-Element (6))" >&2
exit 1
else
echo "RESULT: PASS — unbefugter Frontmatter-Key wird als Struktur-Verstoß erkannt (Frontmatter-Subset-HARD-FAIL ausgelöst)"
fi
# =====================================================================
runlabel "U9: NEUE-DATEI — ungetrackte neue Datei unter wiki/ wird erkannt (??-Sicht, Duplikat-Kontrolle)"
isolate u9
echo "--- simulierter Ghost-Diff: ungetrackte neue Datei wiki/neu.md (Duplikat, ??-Sicht) ---"
echo "unbefugte neue Concept-Datei" > wiki/neu.md
echo "--- Probe (erwartet: neu ausserhalb erlaubter Menge + ??-Sicht-HARD-FAIL) ---"; probe
if ( assert_invariant "alpha log" ) >/dev/null 2>&1; then
echo "HARD-FAIL (Keine-neue-Datei): ungetrackte neue Datei wiki/neu.md wurde NICHT erkannt (??-Sicht, Duplikat-Kontrolle)" >&2
exit 1
else
echo "RESULT: PASS — ungetrackte neue Datei unter wiki/ wird über die ??-Sicht erkannt (Duplikat/Ghost-Diff, keine neue Datei)"
fi
echo
echo "===== Sandbox abgeschlossen (alle 9 Tests U1-U9) ====="
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
@@ -0,0 +1,777 @@
#!/usr/bin/env bash
# Story 3.4 — Sandbox-Tests der Synthese-Dimension (§5.10, Revision 2.9)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb34)
# Zweck: die Synthese-Mechanik (§5.10) als re-executierbarer Run-Demonstrator
# durchspielen — Neu-Anlage aus 2+ raw/-Quellen mit sources >= 2 + gemischten
# Belegen (S1), Multi-Beleg-Konsolidierung (S2), Negativ-Kontrolle
# Aneinanderreihung / Reflektiertheits-Selbsttest (S3), Synthese-Update auf
# bestehendem Concept-Pfad (S4), Struktur-/Frontmatter-Invariante (S5),
# NO_OP-Synthese (N1: Evidenz bereits im Body -> volle Byte-Identitaet),
# Zwei-Run-Identitaet (S6, AD-17h/A0-19) + die Form-Wahl-Klassifikationsprobe
# (Story-3.3-Defer U2/U7: prueft die §5.9-Abgrenzungs-Reihenfolge
# Korrigieren -> Praezisieren -> Erweitern -> No-Op an einer gemischten Einheit).
# Erhaltungs-Invariante (§5.9 Pkt. 5) als HARDE Assertion; Frontmatter-
# Konformitaet (Vertrag §3.3/§3.4-Subset) je erzeugtem/aktualisiertem Concept.
# Linux-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/-Baum.
set -u
ROOT=$(mktemp -d /tmp/sb34-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw"
cd "$SB"
git init -q
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit) ----------
# Mini-Bundle mit zwei Bereichs-Index-freien Root-Concepts + Bundleroot as the
# Referenz: ein bereits bestehendes Concept `gamma` (nicht betroffen, Kontrolle).
# gamma ist das nicht-betroffene Kontroll-Concept (Ghost-Diff-Kontrolle, S3/S5).
# Die Synthese-Einheiten (S1: neu; S4: Update auf bestehendem Pfad alpha) werden
# je Szenario aus zwei committeten raw/-Quellen gespeist.
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Gamma](gamma.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
EOF
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
# Bereits bestehende raw/-Dateien (Baseline-Baum)
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz v1: deterministische Init-Sequenz.
### S-2
Evidenz v1: ausschließlich lokale Netze.
EOF
cat > raw/gamma-v1.md <<'EOF'
### S-1
Evidenz v1: Gamma-Thema.
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT: $BASE"
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo
runlabel() { echo; echo "########## $1 ##########"; }
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien)
isolate() { git checkout -q -b "$1" "$BASE" 2>/dev/null || git checkout -q "$1"; git reset -q --hard "$BASE"; git clean -qfd wiki raw; }
# Erhaltungs-Invariante-Probe (D-1-Form, §5.9 Pkt. 5): Baseline-Diff + porcelain,
# normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert)
probe() {
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
# assert_invariant: bricht bei Verstoß hart ab (exit 1; positive Erwartung).
# assert_invariant_ok: Non-Exiting-Variante (exit-code: 0 = Invariante gehalten,
# 1 = verletzt) — für Negativ-Kontrollen, die NUR erkennen sollen (§5.9 Pkt. 5,
# Verstoß textuell benannt, NFR-4), ohne dass die Assertion selbst den Run beendet.
inv_viol() { # $1=expected ; liefert 0, wenn erlaubte-Menge konsistent (keine Verletzung), !=0 sonst; msg auf stderr
# Alle Eintraege (getrackt wie ungetrackt) unter wiki/ muessen in $expected sein.
# Ungetrackte Eintraege ($??) sind NUR dann ein Verstoss, wenn sie ausserhalb der
# erlaubten Menge liegen (Pkt. 8: "keine neue Datei AUSSER echten Ziel-Pfaden" —
# legitime neue Ziel-Pfade sind in $expected enthalten; alle anderen sind Duplikat/
# Ghost-Diff). Die Dublette "keine neue Datei UEBERHAUPT" wuerde legitime neue
# Ziel-Pfade faelschlich verwerfen und ist hier bewusst NICHT formuliert.
local expected="$1" p u bad=0
got=$(inv_set)
for p in $got; do
case " $expected " in
*" $p "*) ;;
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
esac
done
for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do
u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||')
case " $expected " in
*" $u "*) ;;
*) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (Pkt. 8)" >&2; bad=1;;
esac
done
return $bad
}
assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1
if inv_viol "$1"; then
echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff"
else
exit 1
fi
}
# assert_frontmatter_ok: Non-Exiting-Variante von assert_frontmatter (wie oben:
# exit-code 0 = konform, 1 = Verstoß) für Negativ-Kontrollen, die erkennen sollen
# (Vertrag §3.3/§3.4-Subset, P2-Element (6)) — ohne den Run abzubrechen.
assert_frontmatter_ok() {
( assert_frontmatter "$@" ) >/dev/null 2>&1
}
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5) ----------
# $1=Datei; $2...=erwartete sources[].resource-Werte (in beliebiger Reihenfolge;
# der Determinismus der sources-Reihenfolge wird separat in S1/S4 geprueft).
assert_frontmatter() {
local f="$1"; shift
local r bad=0
local v
v=$(awk '
/^---$/{n++; if(n==2) exit; next}
/^[A-Za-z_][A-Za-z0-9_-]*:/{
k=$0; sub(/:.*/,"",k)
if (k=="sources") top="sources"
else if (k=="generated" || k=="verified") top="genver"
else top="other"
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
if (seenk[k]++) print "DUP_KEY:" k
r=0
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
if (r>0) lastr=r
next
}
/^[[:space:]]/{
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
ik=substr($0,1,RLENGTH-1)
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
if (top=="other") print "INNER_UNBEFUGT:" ik
}
}
' "$f")
if [ -n "$v" ]; then
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
exit 1
fi
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
for r in "$@"; do
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
done
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
}
# ---------- Synthese-Helfer (deterministisch; at gepinnt fuer Zwei-Run-Identitaet) ----------
AT_NEW="2026-08-19T12:00:00Z"
bump_at() { sed -i "s|^ at: .*| at: $AT_NEW|" "$1"; }
# add_source: Fuegt einen sources-Eintrag an seiner LEXIKOGRAFISCHEN Position ein
# (§5.10 Pkt. 2/8: die sources-Liste ist deterministisch nach `resource` sortiert,
# LC_ALL=C, AD-17h/A0-19 — nie appended). Einfuege-Zeile: erste `resource:`-Zeile,
# deren Wert lexikografisch > $res ist; sonst generated:-Zeile (Ende der sources-Liste).
# Die Einfuege-Position selbst wird ueber assert_sources_lex haert verifiziert.
add_source() { # $1=Datei $2=resource $3=id
local f="$1" res="$2" id="$3" ln
grep -qF " - resource: $res" "$f" && return 0 # bereits vorhanden -> kein Duplikat
ln=$(LC_ALL=C awk -v r="$res" '
$1=="-" && $2=="resource:" { name=$3; gsub(/\r/,"",name); if (name > r) { print NR; exit } }
/^generated:/ { print NR; exit }
' "$f")
[ -n "$ln" ] || ln=$(grep -n '^generated:' "$f" | head -1 | cut -d: -f1)
sed -i "${ln}i\\ - resource: $res\n id: $id" "$f"
}
# assert_sources_lex: verifiziert, dass die sources-resource-Werte in Datei-Reihenfolge
# exakt der LC_ALL=C-lexikografischen Ordnung entsprechen (§5.10 Pkt. 2 / Pkt. 8,
# AD-17h/A0-19 — deterministisch, unabhaengig von Datei-/Verarbeitungsreihenfolge).
assert_sources_lex() { # $1=Datei
local f="$1" file_order sorted_order
file_order=$(display_sources "$f")
sorted_order=$(display_sources "$f" | LC_ALL=C sort)
if [ "$file_order" != "$sorted_order" ]; then
echo "HARD-FAIL (sources-Reihenfolge, §5.10 Pkt. 2/8): die sources-Liste in $f ist nicht lexikografisch nach resource (LC_ALL=C) sortiert" >&2
echo " Datei-Reihenfolge: $file_order" >&2
echo " LC_ALL=C-Ordnung: $sorted_order" >&2
exit 1
fi
echo "RESULT: PASS — sources-Liste $f ist lexikografisch nach resource sortiert (LC_ALL=C, AD-17h/A0-19)"
}
# display_sources: gibt die sources-resource-Zeilen in Datei-Reihenfolge aus
display_sources() { grep -E '^ - resource: ' "$1"; }
log_anlage() { # $1=Concept-Pfad $2=quelle(n)
printf '\n## 2026-08-19\n- Synthese-Anlage: %s (%s; Baseline %s)\n' "$1" "$2" "$BASE" >> wiki/log.md
}
log_update() { # $1=Concept-Pfad $2=quelle(n)
printf '\n## 2026-08-19\n- Story 3.1-Update: %s (%s; Baseline %s)\n' "$1" "$2" "$BASE" >> wiki/log.md
}
# =====================================================================
runlabel "S1: NEU-ANLAGE — zwei raw/-Quellen desselben Themas -> EIN Synthese-Concept (sources >= 2, gemischte Belege; FR-7 AC-1/AC-2)"
isolate s1
cat > raw/synth-a.md <<'EOF'
### S-1
Evidenz a: das Delta-Protokoll kapselt die Zustands-Replikation.
### S-2
Evidenz a: Delta-Replikatbestaetigungen durchlaufen eine deterministische Quorum-Sequenz.
EOF
cat > raw/synth-b.md <<'EOF'
### S-1
Evidenz b: Delta-Betrieb setzt einen synchronen Konsens-Dienst voraus.
### S-2
Evidenz b: das Delta-Protokoll kapselt die Zustands-Replikation.
EOF
git add -A; git commit -qm "Zuwachs: raw/synth-a.md + raw/synth-b.md (zwei Quellen, ein Thema)"
echo "--- Synthese-Stimulus: zwei belegende raw/-Quellen (§5.10 Pkt. 1), Ziel-Pfad unbelegt -> Neu-Anlage via §5.7 ---"
echo "--- EIN Synthese-Concept delta: gemeinsame Repraesentation statt getrennter Zusammenfassungen (AC-1) ---"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-a.md
id: s1
- resource: raw/synth-b.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Das Delta-Protokoll kapselt die Zustands-Replikation (raw/synth-a.md#S-1).
Delta-Replikatbestaetigungen durchlaufen eine deterministische Quorum-Sequenz (raw/synth-a.md#S-2).
Delta-Betrieb setzt einen synchronen Konsens-Dienst voraus (raw/synth-b.md#S-1).
EOF
sed -i 's|^# Index$|# Index\n- [Delta](delta.md)|' wiki/index.md
log_anlage delta "raw/synth-a.md + raw/synth-b.md"
# KEIN git add -A auf wiki/: delta.md bleibt ungetrackt ($??) — die Pkt.-8-
# Duplikat-Kontrolle (über die ??-Sicht) wird so LEBENDIG geprüft (die Erhaltungs-
# Invarianz-Probe liest git-diff + porcelain beides aus).
echo "--- Assertion: sources >= 2 (jede belegende Quelle ein Eintrag), lexikografisch nach resource (LC_ALL=C, Pkt. 2/8) ---"
grep -qF ' - resource: raw/synth-a.md' wiki/delta.md || { echo "HARD-FAIL: sources-Eintrag raw/synth-a.md fehlt" >&2; exit 1; }
grep -qF ' - resource: raw/synth-b.md' wiki/delta.md || { echo "HARD-FAIL: sources-Eintrag raw/synth-b.md fehlt" >&2; exit 1; }
assert_sources_lex wiki/delta.md
echo "--- Assertion: gemischte claim-granulare Provenienz (§5.5) je Aussage — eine Repraesentation, Belege aus beiden Quellen ---"
grep -qF '(raw/synth-a.md#S-1)' wiki/delta.md || { echo "HARD-FAIL: Inline-Beleg raw/synth-a.md#S-1 fehlt" >&2; exit 1; }
grep -qF '(raw/synth-a.md#S-2)' wiki/delta.md || { echo "HARD-FAIL: Inline-Beleg raw/synth-a.md#S-2 fehlt" >&2; exit 1; }
grep -qF '(raw/synth-b.md#S-1)' wiki/delta.md || { echo "HARD-FAIL: Inline-Beleg raw/synth-b.md#S-1 fehlt" >&2; exit 1; }
echo "--- Assertion: keine per-Source-Zusammenfassungs-Struktur (AC-4, Reflektiertheits-Selbsttest Pkt. 5) ---"
if grep -nE 'Quelle [A-Z]:|Source [A-Z]:' wiki/delta.md; then
echo "HARD-FAIL (Reflektiertheit): per-Source-Struktur im Synthese-Concept (FR-7 AC-4)" >&2
exit 1
else
echo "RESULT: PASS — keine 'Quelle A: ... / Quelle B: ...'-Struktur; Aussagen tragen Provenienz-Tags"
fi
echo "--- Index-Link (Punkt 11) + log.md-Anlage-Eintrag (Pkt. 7: vollständige Multi-Source-sources-Liste im Eintrag) ---"
grep -qF -- '- [Delta](delta.md)' wiki/index.md || { echo "HARD-FAIL: delta fehlt in wiki/index.md (Punkt 11)" >&2; exit 1; }
grep -qF 'Synthese-Anlage: delta' wiki/log.md || { echo "HARD-FAIL: Anlage-Eintrag in log.md fehlt" >&2; exit 1; }
grep -qF 'raw/synth-a.md' wiki/log.md || { echo "HARD-FAIL: Multi-Source-Quelle raw/synth-a.md fehlt im log.md-Anlage-Eintrag (Pkt. 7)" >&2; exit 1; }
grep -qF 'raw/synth-b.md' wiki/log.md || { echo "HARD-FAIL: Multi-Source-Quelle raw/synth-b.md fehlt im log.md-Anlage-Eintrag (Pkt. 7)" >&2; exit 1; }
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5; erlaubt: delta + index + log) ---"; probe
assert_invariant "delta index log"
assert_frontmatter wiki/delta.md raw/synth-a.md raw/synth-b.md
# =====================================================================
runlabel "S2: KONSOLIDIERUNG — redundante Aussage aus zwei Quellen -> EINE konsolidierte Aussage mit Multi-Beleg (FR-7 AC-3, AD-4)"
isolate s2
cat > raw/synth-c.md <<'EOF'
### S-1
Evidenz c: Delta-relevante Sitzungsschluessel unterliegen einer festen Lebensdauer.
### S-3
Evidenz c: Delta-Repraesentationen sind unveraenderlich nach Quorum-Bestaetigung.
EOF
cat > raw/synth-d.md <<'EOF'
### S-1
Evidenz d: Delta-relevante Sitzungsschluessel unterliegen einer festen Lebensdauer.
### S-4
Evidenz d: Delta-Repraesentationen speichern den konsolidierten Zustand.
EOF
git add -A; git commit -qm "Zuwachs: raw/synth-c.md + raw/synth-d.md (redundanter Befund)"
echo "--- Synthese: die redundante Aussage (Sitzungsschluessel) existiert in BEIDEN Quellen (§5.10 Pkt. 3) ---"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-c.md
id: s1
- resource: raw/synth-d.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Delta-relevante Sitzungsschluessel unterliegen einer festen Lebensdauer (raw/synth-c.md#S-1; raw/synth-d.md#S-1).
Delta-Repraesentationen sind unveraenderlich nach Quorum-Bestaetigung (raw/synth-c.md#S-3).
Delta-Repraesentationen speichern den konsolidierten Zustand (raw/synth-d.md#S-4).
EOF
sed -i 's|^# Index$|# Index\n- [Delta](delta.md)|' wiki/index.md
log_anlage delta "raw/synth-c.md + raw/synth-d.md"
# KEIN git add -A auf wiki/: delta.md ungetrackt ($??, legitimer Ziel-Pfad) —
# Pkt.-8-Duplikat-Kontrolle über die ??-Sicht wird lebendig geprüft.
echo "--- Assertion: EINE konsolidierte Aussage mit Multi-Beleg (Semikolon-Form, voller Pfad je Beleg); keine Beleg-Tilgung (AD-4) ---"
[ "$(grep -c 'Sitzungsschluessel' wiki/delta.md)" -eq 1 ] || { echo "HARD-FAIL: redundante Aussage wurde nicht zu einer Aussage konsolidiert (FR-7 AC-3)" >&2; exit 1; }
grep -qF '(raw/synth-c.md#S-1; raw/synth-d.md#S-1)' wiki/delta.md || { echo "HARD-FAIL: Multi-Beleg-Semikolon-Form fehlt oder ein Beleg wurde getilgt (AD-4)" >&2; exit 1; }
grep -qF '(raw/synth-c.md#S-3)' wiki/delta.md || { echo "HARD-FAIL: Einzelbeleg c#S-3 fehlt" >&2; exit 1; }
grep -qF '(raw/synth-d.md#S-4)' wiki/delta.md || { echo "HARD-FAIL: Einzelbeleg d#S-4 fehlt" >&2; exit 1; }
echo "--- Assertion: Reflektiertheit (AC-4) + sources-Ordnung (Pkt. 2/8) ---"
if grep -nE 'Quelle [A-Z]:|Source [A-Z]:' wiki/delta.md; then
echo "HARD-FAIL (Reflektiertheit): per-Source-Struktur (FR-7 AC-4)" >&2; exit 1
else
echo "RESULT: PASS — konsolidierte Aussage mit Multi-Beleg (kein Provenienz-Verlust), keine per-Source-Struktur"
fi
assert_sources_lex wiki/delta.md
echo "--- log-Anlage-Eintrag (Pkt. 7): vollständige Multi-Source-Liste (c + d) im Eintrag ---"
grep -qF 'raw/synth-c.md' wiki/log.md || { echo "HARD-FAIL: Multi-Source-Quelle raw/synth-c.md fehlt im log.md-Anlage-Eintrag (Pkt. 7)" >&2; exit 1; }
grep -qF 'raw/synth-d.md' wiki/log.md || { echo "HARD-FAIL: Multi-Source-Quelle raw/synth-d.md fehlt im log.md-Anlage-Eintrag (Pkt. 7)" >&2; exit 1; }
echo "--- Probe (erlaubt: delta + index + log) ---"; probe
assert_invariant "delta index log"
assert_frontmatter wiki/delta.md raw/synth-c.md raw/synth-d.md
# =====================================================================
runlabel "S3: NEGATIV-KONTROLLE ANEINANDERREIHUNG — 'Quelle A: ... / Quelle B: ...' wird vom Reflektiertheits-Selbsttest erkannt und vor Run-Abschluss umgebaut (FR-7 AC-4, NFR-7/NFR-4)"
isolate s3
echo "--- simuliertes (fehlerhaftes) Synthese-Ergebnis: per-Source-Zusammenfassungs-Struktur (verboten, §5.10 Pkt. 5) ---"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-a.md
id: s1
- resource: raw/synth-b.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Quelle A: das Delta-Protokoll kapselt die Zustands-Replikation.
Quelle B: Delta-Betrieb setzt einen synchronen Konsens-Dienst voraus.
EOF
echo "--- Reflektiertheits-Selbsttest (Pkt. 5, grepbasiert): erwartet: Befund 'Quelle A:' wird erkannt (nicht vacuous, NFR-7) ---"
if grep -nE 'Quelle [A-Z]:|Source [A-Z]:' wiki/delta.md; then
echo "BEFUND: Aneinanderreihungs-Struktur erkannt (Selbsttest-FAIL, textuell benannt, NFR-7/NFR-4)"
else
echo "HARD-FAIL (Reflektiertheits-Selbsttest): per-Source-Struktur wurde NICHT erkannt — Mechanik vacuous (FR-7 AC-4)" >&2
exit 1
fi
echo "--- Producer-Umbau vor Run-Abschluss (NFR-4): die Aussagen werden integriert, je Aussage mit Provenienz-Tag (§5.5) ---"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-a.md
id: s1
- resource: raw/synth-b.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Das Delta-Protokoll kapselt die Zustands-Replikation (raw/synth-a.md#S-1).
Delta-Betrieb setzt einen synchronen Konsens-Dienst voraus (raw/synth-b.md#S-1).
EOF
sed -i 's|^# Index$|# Index\n- [Delta](delta.md)|' wiki/index.md
log_anlage delta "raw/synth-a.md + raw/synth-b.md"
# KEIN git add -A auf wiki/: delta.md ungetrackt ($??, legitimer Ziel-Pfad) —
# Pkt.-8-Duplikat-Kontrolle über die ??-Sicht wird lebendig geprüft.
echo "--- Assertion: nach dem Umbau ist der Selbsttest leer (Integration hergestellt), nicht nur vertuscht ---"
if grep -qE 'Quelle [A-Z]:|Source [A-Z]:' wiki/delta.md; then
echo "HARD-FAIL (Reflektiertheit): per-Source-Struktur nach Umbau noch vorhanden (FR-7 AC-4)" >&2; exit 1
else
echo "RESULT: PASS — Aneinanderreihung erkannt und durch integrierte, provenance-versehene Aussagen ersetzt (NFR-4: textuell benannt, vor Run-Abschluss behoben)"
fi
echo "--- Probe (erlaubt: delta + index + log) ---"; probe
assert_invariant "delta index log"
assert_frontmatter wiki/delta.md raw/synth-a.md raw/synth-b.md
# =====================================================================
runlabel "S4: SYNTHESE-UPDATE — Synthese-Einheit trifft bestehenden Concept-Pfad -> §5.9-Formwahl + sources-Zuwachs um die zweite Quelle"
isolate s4
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2 (neue Aussage): Alpha ergaenzt die deterministische Init-Sequenz um einen zusaetzlichen Sync-Schritt.
EOF
cat > raw/alpha-v3.md <<'EOF'
### S-3
Evidenz v3 (neue Aussage): Alpha-Repraesentationen wachsen um einen kompakten Rotationsindex.
EOF
git add -A; git commit -qm "Zuwachs: raw/alpha-v2.md + raw/alpha-v3.md (zwei Quellen, Thema Alpha)"
echo "--- Synthese-Stimulus: zwei belegende raw/-Quellen zum selben Thema (Alpha) treffen den bestehenden Pfad alpha (Kandidatenliste §3 Pkt. 2) -> §5.9-Update ---"
echo "--- §5.9-Formwahl: neue belegte Aussagen (kein Ersatz, keine Schaerfung bestehender Formulierung) -> ERWEITERN (erste zutreffende Form) ---"
cat >> wiki/alpha.md <<'EOF'
Alpha ergänzt die deterministische Init-Sequenz um einen zusätzlichen Sync-Schritt (raw/alpha-v2.md#S-3).
Alpha-Repräsentationen wachsen um einen kompakten Rotationsindex (raw/alpha-v3.md#S-3).
EOF
add_source wiki/alpha.md raw/alpha-v2.md s2
add_source wiki/alpha.md raw/alpha-v3.md s3
bump_at wiki/alpha.md
log_update alpha "raw/alpha-v2.md + raw/alpha-v3.md"
# KEIN git add -A auf wiki/: alpha.md + log.md bleiben modifiziert-ungetrackt
# (porcelain ' M '); die Pkt.-8-??-Probe und der git-diff-Baseline-Vergleich
# lesen Worktree-Zustand, kein Staging nötig (isolate resettet je Szenario).
echo "--- Assertion: sources-Zuwachs um beide Quellen (>= 2 belegende Quellen, §5.10 Pkt. 2); bestehende Eintraege unveraendert ---"
grep -qF ' - resource: raw/alpha-v1.md' wiki/alpha.md || { echo "HARD-FAIL: bestehender sources-Eintrag alpha-v1 verloren (keine Entfernung ohne Beleg)" >&2; exit 1; }
grep -qF ' - resource: raw/alpha-v2.md' wiki/alpha.md || { echo "HARD-FAIL: sources-Zuwachs alpha-v2 fehlt" >&2; exit 1; }
grep -qF ' - resource: raw/alpha-v3.md' wiki/alpha.md || { echo "HARD-FAIL: sources-Zuwachs alpha-v3 fehlt" >&2; exit 1; }
assert_sources_lex wiki/alpha.md
echo "--- Assertion: gemischte Belege neu (v2/v3), bestehende belegte Aussagen byte-identisch (Struktur-Erhaltungsregel Erweitern, §5.9 Pkt. 2) ---"
grep -qF '(raw/alpha-v2.md#S-3)' wiki/alpha.md || { echo "HARD-FAIL: Beleg alpha-v2#S-3 fehlt" >&2; exit 1; }
grep -qF '(raw/alpha-v3.md#S-3)' wiki/alpha.md || { echo "HARD-FAIL: Beleg alpha-v3#S-3 fehlt" >&2; exit 1; }
grep -qF 'Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 wurde beim Synthese-Update umgeschrieben (Struktur-Erhaltung)" >&2; exit 1; }
grep -qF 'Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 wurde beim Synthese-Update umgeschrieben (Struktur-Erhaltung)" >&2; exit 1; }
echo "--- Assertion: log.md-Eintrag 'Story 3.1-Update' (Pkt. 7) + Reflektiertheit (AC-4) ---"
grep -qF 'Story 3.1-Update: alpha' wiki/log.md || { echo "HARD-FAIL: Story 3.1-Update-Eintrag in log.md fehlt (Pkt. 7)" >&2; exit 1; }
if grep -nE 'Quelle [A-Z]:|Source [A-Z]:' wiki/alpha.md; then
echo "HARD-FAIL (Reflektiertheit): per-Source-Struktur (FR-7 AC-4)" >&2; exit 1
else
echo "RESULT: PASS — Synthese-Update: ein Ziel-Pfad, sources-Zuwachs s2+s3, gemischte Belege, bestehende Aussagen byte-identisch"
fi
echo "--- Probe (Pkt. 5; erlaubt: alpha (Update) + log — der Index-Link bleibt unveraendert, §5.9 Pkt. 3) ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md raw/alpha-v3.md
# =====================================================================
runlabel "S5: STRUKTUR-/FRONTMATTER-INVARIANTE — unbefugter Key oder nicht-lexikografische sources verletzt die Synthese-Struktur-Ebene (Pkt. 8, §5.9 Pkt. 5-Struktur-Ebene; P2-Element (6))"
isolate s5
echo "--- simulierter Verstoß: unbefugter Frontmatter-Key 'foo' in einem Synthese-Concept ---"
# Setup als regelkonforme Anlage (delta + Index-Link + log-Eintrag) — die Pfad-
# Set-Ebene ist damit legitim; der VERSTOSS sitzt ausschließlich in der Struktur-
# Ebene (unbefugter Key 'foo:', Vertrag §3.3/§3.4-Subset, P2-Element (6)).
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-a.md
id: s1
- resource: raw/synth-b.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
foo: bar
---
Das Delta-Protokoll kapselt die Zustands-Replikation (raw/synth-a.md#S-1).
EOF
sed -i 's|^# Index$|# Index\n- [Delta](delta.md)|' wiki/index.md
log_anlage delta "raw/synth-a.md + raw/synth-b.md"
# KEIN git add -A auf wiki/: delta.md ungetrackt — die Pkt.-8-??-Sicht wird
# in diesem Struktur-Szenario selbst geprüft.
echo "--- Assertions (erwartet: Frontmatter-Subset-HARD-FAIL, Verstoß textuell benannt, NFR-4; non-exiting Variante, denn der Szenario-Zweck ist das ERKENNEN, nicht eine positive Invarianten-Erwartung) ---"
# (a) Path-Set-Ebene: die Erhaltungs-Invariante selbst muss hier GEHALTEN sein
# (delta/index/log sind legitime Einträge); andernfalls wäre der Szenario-
# Zustand selbst schon inkonsistent (Pkt. 8, §5.9 Pkt. 5) — dann HARD-FAIL.
if inv_viol "delta index log"; then
:
else
echo "HARD-FAIL (Struktur-Ebene): Erhaltungs-Invariante unerwartet verletzt (unbefugter Pfad / neue Datei) — Szenario-Zustand inkonsistent (Pkt. 8)" >&2
exit 1
fi
# (b) Struktur-Ebene: der unbefugte Key 'foo:' MUSS erkannt werden (P2-Element (6),
# Vertrag §3.3/§3.4-Subset) — wenn nicht, ist die Erkennungs-Mechanik vacuous.
if assert_frontmatter_ok wiki/delta.md; then
echo "HARD-FAIL (Struktur-Ebene): unbefugter Frontmatter-Key wurde NICHT erkannt (P2-Element (6))" >&2
exit 1
fi
# (c) Pkt.-8-??-Negativkontrolle: eine UNGETRACKTE neue Datei AUSSERHALB der
# erlaubten Ziel-Pfade {delta index log} MUSS als Duplikat/Ghost-Diff
# HARD-FAILn (Pkt. 8 „keine neue Datei außer echten Ziel-Pfaden"; Story-
# 3.3-U9-Muster) — erkennt inv_viol die Verletzung, ist die Kontrolle
# nicht vacuous; erst danach ist die Setup-Zeile (virtueller Ortungs-
# Re-Run) zu entfernen und der Zustand für die positive Probe wiederherzustellen.
echo "--- Pkt.-8-??-Negativkontrolle: ungetrackte Nicht-Ziel-Datei muss als Duplikat/Ghost-Diff HARD-FAILn ---"
cat > wiki/ghost.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-a.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Ghost-Diff-Versuch: keine echte Ziel-Pfad-Berechtigung.
EOF
echo "--- Probe mit erwartet-negativer Sicht: git-diff zeigt ghost + delta/index/log; ??-Sicht listet ghost + delta ---"
{ git diff --name-only "$BASE" -- wiki/ ; git diff --cached --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u | paste -sd' ' -
if inv_viol "delta index log"; then
# inv_viol = 0 => KEINE Verletzung gemeldet => ghost.md wurde NICHT erkannt
# (die ??-Sicht/der Diff ignorieren die Nicht-Ziel-Datei) => Kontrolle vacuous
echo "HARD-FAIL (Pkt. 8-??-Probe): ghost.md ausserhalb der erlaubten Ziel-Pfade wurde NICHT als Duplikat/Ghost-Diff erkannt — Kontrolle vacuous" >&2
exit 1
else
# inv_viol != 0 => Verletzung gemeldet => ghost.md als Duplikat/Ghost-Diff erkannt
echo "BEFUND: ghost.md als Duplikat/Ghost-Diff erkannt (inv_viol meldet Verletzung, NFR-4) — Pkt.-8-??-Kontrolle ist nicht vacuous"
fi
rm wiki/ghost.md
echo "RESULT: PASS — unbefugter Key im Synthese-Concept wird als Struktur-Verstoß erkannt (Frontmatter-Subset-HARD-FAIL, textuell benannt); Pkt.-8-??-Negativkontrolle nicht vacuous"
# =====================================================================
runlabel "N1: NO_OP-SYNTHESE — Evidenz bereits vollständig im Body: keine Form greift -> No-Op (Nicht-Form), volle Byte-Identität (§5.10 Pkt. 3/8, §5.9 Pkt. 2)"
isolate n1
# Eine ZWEITE committete Quelle desselben Themas mit REDUNDANTER Evidenz: die
# Aussage (deterministische Init-Sequenz) existiert bereits im Body — belegt aus
# alpha-v1#S-1 (Baseline-Beleg; alpha-v1 ist die bestehende Evidenzbasis, NICHT
# Zuwachs). Die Zuwachs-Quelle alpha-syno.md (Anker #S-3) paraphrasiert dieselbe
# erkannte Wissenseinheit — kein neuer Befund, keine Schaerfung, kein Ersatz ->
# die Abgrenzungs-Reihenfolge §5.9 Pkt. 2 greift auf keiner Update-Form; die
# Synthese-Einheit läuft auf NO-OP (keine Form-Anwendung, keine Mutations-Phase
# — im Zweifel greift eine Form, engere Auslegung No-Op).
cat > raw/alpha-syno.md <<'EOF'
### S-3
Evidenz (synonym wiedergegeben): das Alpha-Protokoll definiert eine deterministische Init-Sequenz.
EOF
git add -A; git commit -qm "Zuwachs: raw/alpha-syno.md (redundante Evidenz zum Thema Alpha)"
echo "--- Synthese-Stimulus: die ZUWACHS-Quelle alpha-syno.md + die bestehende Evidenzbasis (alpha-v1, Baseline) belegen dasselbe Thema — die neue committete Evidenz (§1 Pkt. 1) ist alpha-syno.md ---"
echo "--- Abgrenzung §5.9 Pkt. 2: kein Ersatz / keine Schaerfung / keine NEUE belegte Aussage -> NO-OP (Nicht-Form) ---"
echo "--- RUN (No-Op): keine Mutations-Phase -> Bundle byte-identisch zum Baseline (kein Eintrag, kein at-Bump, kein sources-Zusatz) ---"
ALPHA_BEFORE=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
LOG_BEFORE=$(sha256sum wiki/log.md | cut -d' ' -f1)
INDEX_BEFORE=$(sha256sum wiki/index.md | cut -d' ' -f1)
AT_BEFORE=$(grep -E '^ at: ' wiki/alpha.md)
# Die Mutations-Phase unterbleibt ex definitione; der Nachweis ist die volle
# Byte-Identität des Bundles (grept nichts, schreibt nichts, committet nichts).
ALPHA_AFTER=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
LOG_AFTER=$(sha256sum wiki/log.md | cut -d' ' -f1)
INDEX_AFTER=$(sha256sum wiki/index.md | cut -d' ' -f1)
AT_AFTER=$(grep -E '^ at: ' wiki/alpha.md)
[ "$ALPHA_BEFORE" = "$ALPHA_AFTER" ] || { echo "HARD-FAIL (No-Op): alpha.md wurde trotz No-Op geaendert (byte-Identitaet, §5.9 Pkt. 2 / §5.10 Pkt. 3)" >&2; exit 1; }
[ "$LOG_BEFORE" = "$LOG_AFTER" ] || { echo "HARD-FAIL (No-Op): log.md wurde trotz No-Op geaendert (kein Eintrag, Pkt. 7-Negation)" >&2; exit 1; }
[ "$INDEX_BEFORE" = "$INDEX_AFTER" ] || { echo "HARD-FAIL (No-Op): index.md wurde trotz No-Op geaendert (kein neuer Link, Punkt-11-Negation)" >&2; exit 1; }
[ "$AT_BEFORE" = "$AT_AFTER" ] || { echo "HARD-FAIL (No-Op): generated.at-Bump trotz No-Op (kein at-Bump, §5.9 Pkt. 2)" >&2; exit 1; }
grep -qF ' - resource: raw/alpha-syno.md' wiki/alpha.md && { echo "HARD-FAIL (No-Op): sources-Zuwachs trotz No-Op (kein sources-Zusatz, §5.9 Pkt. 2)" >&2; exit 1; }
grep -q 'Synthese-Anlage\|Story 3.1-Update' wiki/log.md && { echo "HARD-FAIL (No-Op): log-Eintrag trotz No-Op (kein Eintrag)" >&2; exit 1; }
echo "--- Probe: erwartet LEER (Ghost-Diff-negativ; ein No-Op erzeugt keinen Diff, §5.9 Pkt. 5) ---"; probe
assert_invariant ""
echo "RESULT: PASS — No-Op-Synthese: byte-identisch (alpha/index/log), kein at-Bump, kein sources-Zusatz, kein log-Eintrag, keine neue Datei"
# =====================================================================
runlabel "N2: DISAGREEMENT (AD-16-Default, §5.10 Pkt. 3/7) — widersprüchliche Aussagen aus zwei Quellen werden NICHT still zusammengeführt; beide Behauptungen bleiben + Disagreement-Eintrag in log.md (Epic-4-Interface)"
isolate n2
# Zwei committete Quellen mit DISKREPANTEM Inhalt zur selben erkannten
# Wissenseinheit (Quorum-Konsens): Quelle A behauptet „synchrone Bestätigung",
# Quelle B „asynchrone Bestätigung". AD-16-Default: kein stilles Zusammenführen
# zu einer scheinbar eindeutigen Aussage; die widersprüchlichen Aussagen bleiben
# als Bestand bestehen (je mit ihrem Beleg/Beschreibungsstil + §5.5-Marker).
cat > raw/synth-e.md <<'EOF'
### S-1
Evidenz e: Quorum-Bestaetigungen erfolgen synchron.
### S-5
Evidenz e: Synchrone Quorum-Bestaetigungen halten die Replikate konsistent.
EOF
cat > raw/synth-f.md <<'EOF'
### S-1
Evidenz f: Quorum-Bestaetigungen erfolgen asynchron.
### S-6
Evidenz f: Asynchrone Quorum-Bestaetigungen erhoehen die Latenz.
EOF
git add -A; git commit -qm "Zuwachs: raw/synth-e.md + raw/synth-f.md (diskrepanter Befund zum selben Thema)"
echo "--- Synthese-Stimulus: zwei belegende Quellen desselben Themas mit WIDERSPRECHLICHEM Befund (S-1) ---"
echo "--- AD-16-Default (§5.10 Pkt. 3): KEINE stille Konsolidierung zu einer scheinbar eindeutigen Aussage; beide Behauptungen bleiben ---"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-e.md
id: s1
- resource: raw/synth-f.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Quorum-Bestaetigungen erfolgen synchron (raw/synth-e.md#S-1).
Quorum-Bestaetigungen erfolgen asynchron (raw/synth-f.md#S-1).
EOF
sed -i 's|^# Index$|# Index\n- [Delta](delta.md)|' wiki/index.md
# Disagreement-Eintrag (AD-16/§5.10 Pkt. 7, Epic-4-Interface): mutierter
# Concept-Pfad + die beiden konfligierenden Belege + Baseline-Commit.
printf '\n## 2026-08-19\n- Disagreement: delta — raw/synth-e.md#S-1 vs. raw/synth-f.md#S-1 (kein stilles Zusammenfuehren, AD-16; Baseline %s)\n' "$BASE" >> wiki/log.md
# KEIN git add -A auf wiki/: delta.md + log.md ungetrackt-modifiziert (??-Sicht lebendig).
echo "--- Assertion: BEIDE Behauptungen bleiben erhalten (kein stilles Zusammenfuehren, keine Beleg-Tilgung AD-4) ---"
grep -qF 'Quorum-Bestaetigungen erfolgen synchron (raw/synth-e.md#S-1).' wiki/delta.md || { echo "HARD-FAIL (Disagreement): Aussage e#S-1 wurde still entfernt oder umgeschrieben (AD-16)" >&2; exit 1; }
grep -qF 'Quorum-Bestaetigungen erfolgen asynchron (raw/synth-f.md#S-1).' wiki/delta.md || { echo "HARD-FAIL (Disagreement): Aussage f#S-1 wurde still entfernt oder umgeschrieben (AD-16)" >&2; exit 1; }
grep -qF '(raw/synth-e.md#S-1)' wiki/delta.md || { echo "HARD-FAIL (Disagreement): Beleg e#S-1 fehlt (AD-4)" >&2; exit 1; }
grep -qF '(raw/synth-f.md#S-1)' wiki/delta.md || { echo "HARD-FAIL (Disagreement): Beleg f#S-1 fehlt (AD-4)" >&2; exit 1; }
echo "--- Assertion: KEINE falsche Multi-Beleg-Konsolidierung der widerspruchlichen Aussagen (kein '(e; f)'-Zusammenfuehren) ---"
if grep -qF '(raw/synth-e.md#S-1; raw/synth-f.md#S-1)' wiki/delta.md; then
echo "HARD-FAIL (Disagreement): widerspruechliche Aussagen wurden als ein Multi-Beleg zusammengefuehrt (AD-16-Default verletzt, Schein-Eindeutigkeit)" >&2
exit 1
fi
echo "--- Assertion: Disagreement-Eintrag in log.md (Pkt. 7, AD-16, Epic-4-Interface) ---"
grep -qF 'Disagreement: delta' wiki/log.md || { echo "HARD-FAIL (Disagreement): Disagreement-Eintrag in log.md fehlt (§5.10 Pkt. 7)" >&2; exit 1; }
echo "--- Assertion: Reflektiertheit (AC-4) + sources-Lexikografie ---"
if grep -nE 'Quelle [A-Z]:|Source [A-Z]:' wiki/delta.md; then
echo "HARD-FAIL (Reflektiertheit): per-Source-Struktur trotz Disagreement (FR-7 AC-4)" >&2; exit 1
fi
assert_sources_lex wiki/delta.md
echo "--- Probe (erlaubt: delta + index + log) ---"; probe
assert_invariant "delta index log"
assert_frontmatter wiki/delta.md raw/synth-e.md raw/synth-f.md
echo "RESULT: PASS — Disagreement: beide Behauptungen + je Beleg bleiben; kein stilles Zusammenfuehren (AD-16-Default); log.md-Disagreement-Eintrag (Epic-4-Interface)"
# =====================================================================
runlabel "N3: AD-4c-UEBERNAHME-MARKER (Kontext-Marker, §5.10 Pkt. 4) — übernommene Formulierung aus einem bestehenden Concept traegt den §5.5-Kontext-Marker, nie alleinige Provenienz"
isolate n3
# Eine Synthese-Einheit übernimmt eine Formulierung aus einem BESTEHENDEN Concept
# (gamma): sie ist eine Kontext-/Synthese-Umformulierung, NICHT eigenständig
# gegen die Zuwachs-Quelle belegt. AD-4c: kein bestehendes Concept ist alleinige
# Provenienz eines anderen — die Übernahme trägt den §5.5-Kontext-Marker und
# bleibt textuell auf die Quelle des übernommenen Concepts rückverfolgbar.
cat > raw/synth-g.md <<'EOF'
### S-1
Evidenz g: Delta-Operationen respektieren die gamma-Kopplung.
EOF
cat > raw/synth-h.md <<'EOF'
### S-1
Evidenz h: Delta-Operationen verlangen die gamma-Kopplungs-Pruefung.
EOF
git add -A; git commit -qm "Zuwachs: raw/synth-g.md + raw/synth-h.md (Uebernahme-Thema)"
echo "--- Synthese-Einheit: zwei Zuwachs-Quellen (synth-g/h) zum selben Thema; die Kopplungs-Formulierung wird aus dem bestehenden Concept gamma uebernommen (AD-4c) ---"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-g.md
id: s1
- resource: raw/synth-h.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Delta-Operationen respektieren die Gamma-Kopplung (raw/synth-g.md#S-1).
Delta-Operationen verlangen die Gamma-Kopplungs-Pruefung (raw/synth-h.md#S-1).
Gamma-Kopplung: übernommen aus gamma auf Basis von raw/gamma-v1.md#S-1, nicht eigenständig belegt.
EOF
sed -i 's|^# Index$|# Index\n- [Delta](delta.md)|' wiki/index.md
log_anlage delta "raw/synth-g.md + raw/synth-h.md"
# KEIN git add -A auf wiki/: delta.md + log.md ungetrackt-modifiziert (??-Sicht lebendig).
echo "--- Assertion: die uebernommene Formulierung traegt den §5.5-Kontext-Marker (Muster „übernommen aus <Concept> auf Basis von <source>, nicht eigenständig belegt“) ---"
grep -qF 'übernommen aus gamma auf Basis von raw/gamma-v1.md#S-1, nicht eigenständig belegt' wiki/delta.md || { echo "HARD-FAIL (AD-4c): Kontext-Marker fehlt oder falsch (Muster §5.10 Pkt. 4 / §5.5-Pkt.-2)" >&2; exit 1; }
grep -qF 'nicht eigenständig belegt' wiki/delta.md || { echo "HARD-FAIL (AD-4c): 'nicht eigenständig belegt'-Klausel fehlt (§5.10 Pkt. 4)" >&2; exit 1; }
echo "--- Assertion: die eigenständig belegten Aussagen tragen KEINEN Kontext-Marker (Marker gilt nur für übernommene Formulierungen, Pkt. 4) ---"
grep -qE '^.*raw/synth-g\.md#S-1\).*übernommen aus' wiki/delta.md && { echo "HARD-FAIL (AD-4c): eigenständig belegte Aussage traegt faelschlich den Kontext-Marker (§5.10 Pkt. 4)" >&2; exit 1; }
echo "--- Assertion: Reflektiertheit + sources-Lexikografie + kein gamma-Duplikat ---"
if grep -nE 'Quelle [A-Z]:|Source [A-Z]:' wiki/delta.md; then
echo "HARD-FAIL (Reflektiertheit): per-Source-Struktur (FR-7 AC-4)" >&2; exit 1
fi
assert_sources_lex wiki/delta.md
grep -qF ' - resource: raw/gamma-v1.md' wiki/delta.md && { echo "HARD-FAIL (AD-4c): gamma-v1 als eigene sources-Quelle eingetragen — die Quelle des übernommenen Concepts ist nur im Kontext-Marker, nicht als source (AD-4c)" >&2; exit 1; }
echo "--- Probe (erlaubt: delta + index + log) ---"; probe
assert_invariant "delta index log"
assert_frontmatter wiki/delta.md raw/synth-g.md raw/synth-h.md
echo "RESULT: PASS — AD-4c-Uebernahme: Kontext-Marker traegt Concept + Basis-Quelle + 'nicht eigenständig belegt'; gamma bleibt nicht Quellen-Provenienz von delta"
# =====================================================================
runlabel "S6: ZWEI-RUN-IDENTITAET (AD-17h/A0-19) — gleicher Git-State + gleiches Eingabeset -> identischer Synthese-Vorgang"
run_synthese() { # $1 = Branchname; mutiert Worktree, dann git add -A (Staging)
isolate "$1"
cat > raw/synth-a.md <<'EOF'
### S-1
Evidenz a: das Delta-Protokoll kapselt die Zustands-Replikation.
### S-2
Evidenz a: Delta-Replikatbestaetigungen durchlaufen eine deterministische Quorum-Sequenz.
EOF
cat > raw/synth-b.md <<'EOF'
### S-1
Evidenz b: Delta-Betrieb setzt einen synchronen Konsens-Dienst voraus.
### S-2
Evidenz b: das Delta-Protokoll kapselt die Zustands-Replikation.
EOF
git add -A; git commit -qm "Zuwachs: raw/synth-a.md + raw/synth-b.md"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/synth-a.md
id: s1
- resource: raw/synth-b.md
id: s2
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Das Delta-Protokoll kapselt die Zustands-Replikation (raw/synth-a.md#S-1).
Delta-Replikatbestaetigungen durchlaufen eine deterministische Quorum-Sequenz (raw/synth-a.md#S-2).
Delta-Betrieb setzt einen synchronen Konsens-Dienst voraus (raw/synth-b.md#S-1).
EOF
sed -i 's|^# Index$|# Index\n- [Delta](delta.md)|' wiki/index.md
log_anlage delta "raw/synth-a.md + raw/synth-b.md"
git add -A # Staging — die Mutationen sind Teil des Runs (Commit-Boundary = Mutations-Boundary)
}
run_synthese s6a
A_DELTA=$(sha256sum wiki/delta.md | cut -d' ' -f1)
A_LOG=$(sha256sum wiki/log.md | cut -d' ' -f1)
A_INDEX=$(sha256sum wiki/index.md | cut -d' ' -f1)
git commit -qm "Run A (Synthese-Neu-Anlage)"
A_DELTA_COM=$(git show HEAD:wiki/delta.md | sha256sum | cut -d' ' -f1)
run_synthese s6b
B_DELTA=$(sha256sum wiki/delta.md | cut -d' ' -f1)
B_LOG=$(sha256sum wiki/log.md | cut -d' ' -f1)
B_INDEX=$(sha256sum wiki/index.md | cut -d' ' -f1)
git commit -qm "Run B (Synthese-Neu-Anlage)"
B_DELTA_COM=$(git show HEAD:wiki/delta.md | sha256sum | cut -d' ' -f1)
echo "Run A (delta.md): $A_DELTA ; Run B (delta.md): $B_DELTA"
echo "Run A (log.md): $A_LOG ; Run B (log.md): $B_LOG"
echo "Run A (index.md): $A_INDEX ; Run B (index.md): $B_INDEX"
echo "Run A commit (delta.md): $A_DELTA_COM ; Run B commit (delta.md): $B_DELTA_COM"
if [ "$A_DELTA" = "$B_DELTA" ] && [ "$A_LOG" = "$B_LOG" ] && [ "$A_INDEX" = "$B_INDEX" ] && [ "$A_DELTA_COM" = "$B_DELTA_COM" ]; then
echo "RESULT: PASS — zwei unabhaengige Synthese-Runs ueber dieselben zwei Quellen -> identisches Ergebnis (AD-17h/A0-19)"
else
echo "HARD-FAIL (Determinismus-Vertrag): Synthese-Run A und Run B weichen ab" >&2
exit 1
fi
# =====================================================================
runlabel "FW: FORM-WAHL-KLASSIFIKATIONSPROBE (Story-3.3-Defer U2/U7) — gemischte Synthese-Einheit an der §5.9-Abgrenzungs-Reihenfolge Korrigieren -> Praezisieren -> Erweitern -> No-Op"
isolate fw
# Eine Synthese-Einheit mit KORRIGIERENDEM Anteil: ersetzt die bestehende belegte
# Aussage (Netze) als fehlerhaft/ueberholt -> erste zutreffende Form = KORRIGIEREN.
cat > raw/alpha-k1.md <<'EOF'
### S-2
Evidenz k1 (Korrektur): Alpha kann seit v2 auch isolierte Netze bedienen.
EOF
cat > raw/alpha-k2.md <<'EOF'
### S-2
Evidenz k2 (staerkt den Korrektur-Befund): die Netzbeschraenkung ist aufgehoben.
EOF
git add -A; git commit -qm "Zuwachs: raw/alpha-k1.md + raw/alpha-k2.md (korrigierende Synthese-Einheit)"
echo "--- Einheit traegt zugleich neuen Beleg (S-1 bleibt) und Ersetzungs-Befund (S-2) ---"
echo "--- Abgrenzungs-Reihenfolge §5.9 Pkt. 2: (1) Ersetzung fehlerhaft/ueberholt -> KORRIGIEREN (erste zutreffende Form; die uebrigen Textgenauigkeits-Rahmen gelten je Teilbestand) ---"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2)\.|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — überholt durch: Alpha kann seit v2 auch isolierte Netze bedienen (raw/alpha-k1.md#S-2; raw/alpha-k2.md#S-2).|' wiki/alpha.md
add_source wiki/alpha.md raw/alpha-k1.md s2
add_source wiki/alpha.md raw/alpha-k2.md s3
bump_at wiki/alpha.md
log_update alpha "raw/alpha-k1.md + raw/alpha-k2.md"
# KEIN git add -A auf wiki/: alpha.md + log.md modifiziert-ungetrackt (Probe
# liest git-diff + porcelain); isolate resettet je Szenario.
echo "--- Assertion: Abloesung mit Ersetzungsbeleg (keine stille Loeschung, AD-16-Default), alter Wortlaut erhalten, Multi-Beleg im Ersatz ---"
grep -qF 'Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2)' wiki/alpha.md || { echo "HARD-FAIL: alter Wortlaut wurde still geloescht (AD-16, Korrigieren-Struktur-Erhaltung)" >&2; exit 1; }
grep -qF 'überholt durch: Alpha kann seit v2 auch isolierte Netze bedienen (raw/alpha-k1.md#S-2; raw/alpha-k2.md#S-2)' wiki/alpha.md || { echo "HARD-FAIL: Ersetzungsbeleg fehlt oder ist kein Multi-Beleg (Form-Wahl-Klassifikation)" >&2; exit 1; }
echo "--- Assertion: Nebenbefund (Aussage 1) bleibt byte-identisch (kein stiller Umbau, Textgenauigkeits-Rahmen) ---"
grep -qF 'Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 beim Korrigieren flankierend umgeschrieben" >&2; exit 1; }
echo "--- Assertion: sources >= 2 (beide korrigierende Quellen) + lexikografische Ordnung ---"
grep -qF ' - resource: raw/alpha-k1.md' wiki/alpha.md || { echo "HARD-FAIL: sources-Zuwachs alpha-k1 fehlt" >&2; exit 1; }
grep -qF ' - resource: raw/alpha-k2.md' wiki/alpha.md || { echo "HARD-FAIL: sources-Zuwachs alpha-k2 fehlt" >&2; exit 1; }
assert_sources_lex wiki/alpha.md
echo "--- Probe (erlaubt: alpha + log) ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-k1.md raw/alpha-k2.md
echo
echo "===== Sandbox abgeschlossen (S1-S6 + N1-N3 + Form-Wahl-Probe) ====="
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
@@ -0,0 +1,617 @@
#!/usr/bin/env bash
# Story 3.5 — Sandbox-Tests der Leasing-/Dirty-Tree-Dimension (§5.11, Revision 3.0)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb35)
# Zweck: die Koordinations-Mechanik fuer konkurrierende Producer (§5.11) als
# re-executierbarer Run-Demonstrator durchspielen —
# L1 LEASE_AKQUISE (lease/<area>/<id>-Branch, Lockfile, Merge-Base-Disziplin;
# AC-1, AD-17a, A0-12),
# L2 LEASE_HOLD (Lockfile existiert -> Lease-Hold, kein Ueberschreiben, keine
# Mutation; I/O-Matrix LEASE_AKQUISE-Error-Handling),
# L3 ROOT_SCOPE (wiki/ inkl. log.md, index.md, aller Root-Dateien; kein Bereich
# jenseits wiki/; AC-2, AD-17b, A0-13),
# L4 DIRTY_TREE_SCHUTZ (Pre-Mutation-Pruefung, Stash/Scratch-Zone, log.md-
# Dokumentation; fremde uncommittete Aenderung wird NIE geloescht —
# Negativ-Kontrolle; UNCOMMITTED_INPUT-Abbruch; AC-3, AD-17e/f, A0-16),
# L5 NO_AUTO_MERGE (kein textueller Auto-Merge bei ungleichem Pfad-Inhalt;
# compiler-vermittelter AD-16-Pfad mit log.md-Eintrag, Default Erhaltung;
# Eskalation AD-17g; AC-4, AD-17c, A0-14),
# L6 COMMIT_BOUNDARY (Commit-Boundary = Mutations-Boundary; Verletzung ->
# HARD-FAIL-Detektion; Rollback §5.3),
# N1 ??-GHOST-DIFF-Negativkontrolle (ungetrackte Nicht-Ziel-Datei unter wiki/),
# D1 DETERMINISMUS (AD-17h/A0-19: Lockfile-Inhalt + Merge-Klassifikation
# deterministisch aus dem committeten Git-State; Zwei-Run-Identitaet).
# Erhaltungs-Invariante (§5.9 Pkt. 5 / AD-5 / FT-6) als HARDE Assertion je
# Leasing-faehigem Run; Frontmatter-Konformitaet (Vertrag §3.3/§3.4-Subset,
# P2-Element (6)) je erzeugtem/aktualisiertem Concept (Muster Story-3.4-Sandbox).
# Linux-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/-Baum.
set -u
ROOT=$(mktemp -d /tmp/sb35-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw" "$SB/lease" "$SB/scratch"
cd "$SB"
git init -q
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit = Merge-Base) ----------
# Mini-Bundle mit zwei Root-Concepts (alpha + gamma als nicht-betroffene Kontrolle).
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Gamma](gamma.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
EOF
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz v1: deterministische Init-Sequenz.
### S-2
Evidenz v1: ausschließlich lokale Netze.
EOF
cat > raw/gamma-v1.md <<'EOF'
### S-1
Evidenz v1: Gamma-Thema.
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo
runlabel() { echo; echo "########## $1 ##########"; }
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien);
# jede Akquise startet von derselben Merge-Base aus (deterministisch, AD-17h).
# Szenarien-lokale Leasing-/Tmp-Branches (lease/*, tmp/*) aus dem VORIGEN Szenario
# werden entfernt, damit Branch-Namen wie lease/wiki/run-a szenarien-uebergreifend
# wiederverwendbar sind (Determinismus: gleicher Startzustand je Szenario).
# -f erzwingt den Checkout auch bei uncommitteten Resten aus dem Vorszenario
# (z.B. nach einer abgebrochenen Merge-Simulation) — kein Fehlerpfad nach aussen.
isolate() {
git checkout -qf -B "$1" "$BASE"
git for-each-ref --format='%(refname:short)' refs/heads/lease refs/heads/tmp 2>/dev/null | while read b; do
git branch -D "$b" >/dev/null 2>&1 || true
done
git reset -q --hard "$BASE"
git clean -qfd wiki raw lease scratch
}
# Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6): Baseline-Diff + porcelain,
# normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert).
probe() {
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß (msg stderr)
local expected="$1" p u bad=0
got=$(inv_set)
for p in $got; do
case " $expected " in
*" $p "*) ;;
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
esac
done
for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do
u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||')
case " $expected " in
*" $u "*) ;;
*) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (§5.9 Pkt. 8)" >&2; bad=1;;
esac
done
return $bad
}
assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1
if inv_viol "$1"; then
echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff"
else
exit 1
fi
}
assert_frontmatter_ok() {
( assert_frontmatter "$@" ) >/dev/null 2>&1
}
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5; Muster Story-3.4) ----------
assert_frontmatter() {
local f="$1"; shift
local r bad=0
local v
v=$(awk '
/^---$/{n++; if(n==2) exit; next}
/^[A-Za-z_][A-Za-z0-9_-]*:/{
k=$0; sub(/:.*/,"",k)
if (k=="sources") top="sources"
else if (k=="generated" || k=="verified") top="genver"
else top="other"
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
if (seenk[k]++) print "DUP_KEY:" k
r=0
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
if (r>0) lastr=r
next
}
/^[[:space:]]/{
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
ik=substr($0,1,RLENGTH-1)
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
if (top=="other") print "INNER_UNBEFUGT:" ik
}
}
' "$f")
if [ -n "$v" ]; then
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
exit 1
fi
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
for r in "$@"; do
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
done
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
}
# ---------- Leasing-Helfer (§5.11; deterministisch, AD-17h/A0-19) ----------
# Lockfile-Format (Pkt. 1: semantisch identisch in jedem Adapter, A0-12):
# area: <area>
# id: <id>
# producer: <producer>
# baseline_commit: <voller SHA des Merge-Base/Commit-Object-Werts>
# holder_id: <eindeutige Producer-/Run-Kennung>
# Lockfile liegt AUSSERHALB von wiki/ und raw/ (lease/<area>/<id>.lock) —
# Root-Scope-Lease bindet den Bereich, nicht den Lockfile-Pfad (§5.11 Pkt. 2/7).
lock_write() { # $1=area $2=id $3=producer $4=holder_id $5=baseline_commit
local f="lease/$1/$2.lock"
{ echo "area: $1"; echo "id: $2"; echo "producer: $3"; echo "baseline_commit: $5"; echo "holder_id: $4"; } > "$f"
}
lock_exists() { [ -f "lease/$1/$2.lock" ]; }
lock_holder() { grep '^holder_id:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
lock_baseline() { grep '^baseline_commit:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
# akquire: Lease-Akquise (Pkt. 1) — prueft Lockfile (Lease-Hold, kein Ueberschreiben),
# dann Merge-Base-Disziplin (eindeutiger Commit-Object-Wert == $BASE == HEAD des
# Merge-Base-Ancestors). Fehler => exit 1 (HARD-FAIL der Assertion im Szenario).
akquire() { # $1=area $2=id $3=producer $4=holder_id
local area="$1" id="$2" producer="$3" holder="$4"
if lock_exists "$area" "$id"; then
echo "HARD-FAIL (Lease-Hold): Lockfile lease/$area/$id.lock existiert bereits — Akquise verweigert, kein Ueberschreiben (Pkt. 1)" >&2
return 1
fi
mkdir -p "lease/$area"
lock_write "$area" "$id" "$producer" "$holder" "$BASE"
return 0
}
# Dirty-Tree-Schutz (Pkt. 3): Pre-Mutation-Prüfung des Mutationsbereichs gg. HEAD.
# $1 = Mutationsbereich (z.B. wiki/), $2 = erlaubte (eigene) Run-Zielfelder als
# Leerzeichen-getrennte, wiki-relativ-normalisierte Namen (z.B. "alpha log").
# Ausgabe: 0 = nur eigene/legitime uncommittete Einträge im Bereich (clean),
# 1 = mindestens EIN fremder uncommitteter Eintrag (Dirty Tree) —
# Schutz nötig (§5.11 Pkt. 3), nie still geloescht (AD-17e).
# Normalisierung wie probe(): wiki/-Praefix + .md-Suffix strippen.
db_check() { # $1=Bereich $2=legit
local legit="$2" p maybe
maybe=$(git status --porcelain -- "$1" | awk '{print $2}' | sed -e 's|^wiki/||' -e 's|\.md$||')
[ -z "$maybe" ] && return 0
for p in $maybe; do
case " $legit " in
*" $p "*) : ;;
*) return 1 ;;
esac
done
return 0
}
# log.md-Leasing-Eintraege (Pkt. 6: Lease-Akquise / Dirty-Tree-Schutz / Merge-
# Klassifikation / Eskalation / Freigabe; Baseline-Commit im Eintrag, D-2).
# Datumsgruppen-Konvention (Vertrag §5, §5.9 Pkt. 4 / §5.11 Pkt. 6): jeder Eintrag
# haengt SEINEN eigenen '## YYYY-MM-DD'-Header + Bullet als Header-Bullet-Paar an
# (kein 'printf \n##' je Zeile -> keine doppelten/leeren Header; cumulative append,
# neueste Bullets am Ende der Datumsgruppe = deterministisch, AD-17h).
log_lease() { # $1=area $2=id $3=holder
printf '## 2026-08-19\n- Lease-Akquise: %s/%s durch %s (Baseline %s, §5.11 Pkt. 1/6a)\n' "$1" "$2" "$3" "$BASE" >> wiki/log.md
}
log_dirty() { # $1=pfad $2=sicherungsziel
printf '## 2026-08-19\n- Dirty-Tree-Schutz: %s nach %s gesichert (nie geloescht, §5.11 Pkt. 3/6b)\n' "$1" "$2" >> wiki/log.md
}
log_merge() { # $1=area $2=id $3=klassifikation $4=begruendung
printf '## 2026-08-19\n- Merge-Klassifikation: %s/%s -> %s (%s; AD-16, kein textueller Auto-Merge, §5.11 Pkt. 4/6c)\n' "$1" "$2" "$3" "$4" >> wiki/log.md
}
log_eskalation() { # $1=area $2=id
printf '## 2026-08-19\n- Merge-Eskalation (AD-17g): %s/%s unentscheidbar — menschliche Auflösung erforderlich (§5.11 Pkt. 4/6d)\n' "$1" "$2" >> wiki/log.md
}
log_release() { # $1=area $2=id $3=holder
printf '## 2026-08-19\n- Lease-Freigabe: %s/%s durch %s (Release nach committetem Run, §5.11 Pkt. 1/6e)\n' "$1" "$2" "$3" >> wiki/log.md
}
# free_lease: deterministische Lease-Freigabe (Pkt. 1, Release) — entfernt das
# Lockfile als Freigabe-Marker und dokumentiert den Abschluss in log.md (Pkt. 6e),
# nur NACH dem committeten Run (Commit-Boundary, Pkt. 5). Eine inhaertoerfreie
# (abgebrochene) Lease verbleibt bis Story-3.6-Staleness — hier deterministisch.
free_lease() { # $1=area $2=id $3=holder
local f="lease/$1/$2.lock"
[ -f "$f" ] || { echo "HARD-FAIL (free_lease): Lockfile existiert nicht — Release einer unakquirierten Lease (Pkt. 1)" >&2; return 1; }
log_release "$1" "$2" "$3"
rm -f "$f"
}
# =====================================================================
runlabel "L1: LEASE_AKQUISE (AC-1, AD-17a, A0-12) — Producer erwirbt Lease auf lease/<area>/<id>-Branch vom Merge-Base; Lockfile mit eindeutigem Commit-Object-Wert; Root-Scope inkl. log.md/index.md"
isolate l1
echo "--- Akquise: Branch lease/wiki/run-a von der Merge-Base ($BASE); Refresh: Freeze-Command-Konvention, Commit-Object-Wert == $BASE (deterministisch aus dem committeten Git-State, AD-17h) ---"
git checkout -q -b lease/wiki/run-a "$BASE"
akquire wiki run-a "producerA" "run-a-holder"
# Assertions Pkt. 1: Branch-Form, Lockfile, Merge-Base-Disziplin
[ "$(git branch --show-current)" = "lease/wiki/run-a" ] || { echo "HARD-FAIL (L1): Arbeits-Branch ist nicht lease/<area>/<id> (Pkt. 1)" >&2; exit 1; }
grep -q '^area: wiki$' lease/wiki/run-a.lock || { echo "HARD-FAIL (L1): Lockfile-Feld area fehlt/falsch (Pkt. 1, A0-12)" >&2; exit 1; }
grep -q '^id: run-a$' lease/wiki/run-a.lock || { echo "HARD-FAIL (L1): Lockfile-Feld id fehlt/falsch (Pkt. 1)" >&2; exit 1; }
grep -q '^producer: producerA$' lease/wiki/run-a.lock || { echo "HARD-FAIL (L1): Lockfile-Feld producer fehlt/falsch (Pkt. 1)" >&2; exit 1; }
[ "$(lock_baseline wiki run-a)" = "$BASE" ] || { echo "HARD-FAIL (L1): Lockfile-baseline_commit != Merge-Base-Object-Wert (Pkt. 1, Merge-Base-Disziplin)" >&2; exit 1; }
[ "$(lock_holder wiki run-a)" = "run-a-holder" ] || { echo "HARD-FAIL (L1): Lockfile-Feld holder_id fehlt/falsch (Pkt. 1)" >&2; exit 1; }
echo "--- Leasing-Run (Root-Scope): Update auf alpha + Index-Link + log.md — die Lease umfasst wiki/ inkl. log.md/index.md/aller Root-Dateien (Pkt. 2) ---"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2)\.|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — überholt durch: Alpha kann seit v2 auch isolierte Netze bedienen (raw/alpha-v2.md#S-2).|' wiki/alpha.md
cat > raw/alpha-v2.md <<'EOF'
### S-2
Evidenz v2: Alpha kann seit v2 auch isolierte Netze bedienen.
EOF
# sources-Zuwachs lexikografisch + at-Bump + log-Akquise-Eintrag (Pkt. 6a)
sed -i 's|^ at: .*| at: 2026-08-19T12:00:00Z|' wiki/alpha.md
sed -i '/^ - resource: raw\/alpha-v1.md/a\ - resource: raw\/alpha-v2.md\n id: s2' wiki/alpha.md
log_lease wiki run-a "run-a-holder"
echo "--- Assertion: ROOT-SCOPE (Pkt. 2) — die Lease bindet wiki/; kein Lockfile-/Scope-Bereich jenseits wiki/ (naechste Zeile MUSS failen) ---"
grep -q 'scope: raw\|scope: lease\|scope: /' lease/wiki/run-a.lock && { echo "HARD-FAIL (L2): Lockfile enthaelt Scope-Angabe jenseits wiki/ (Root-Scope, AD-17b)" >&2; exit 1; }
echo "--- aber: der Lockfile-Pfad selbst liegt ausserhalb wiki/ (kein Concept-Frontmatter-Key, kein wiki/-Eintrag) ---"
case "/lease/wiki/run-a.lock" in /wiki/*|/raw/*) echo "HARD-FAIL (L1/L2): Lockfile liegt im Bundle-Bereich (Pkt. 1/2)" >&2; exit 1;; esac
echo "--- Assertion: log.md-Akquise-Eintrag (Pkt. 6a) + Baseline-Commit im Eintrag (D-2) ---"
grep -qF 'Lease-Akquise: wiki/run-a durch run-a-holder' wiki/log.md || { echo "HARD-FAIL (L1): log.md-Akquise-Eintrag fehlt (Pkt. 6a)" >&2; exit 1; }
grep -qF "$BASE" wiki/log.md || { echo "HARD-FAIL (L1): Baseline-Commit fehlt im log.md-Eintrag (D-2)" >&2; exit 1; }
echo "--- Assertion: Lockfile rdwr-basiert deterministisch (Feldreihenfolge fix, keine Zeitstempel) + committed Git-State-Eingabe ---"
[ "$(head -1 lease/wiki/run-a.lock)" = "area: wiki" ] || { echo "HARD-FAIL (D1): Lockfile-Feldreihenfolge nicht deterministisch (AD-17h)" >&2; exit 1; }
grep -qiE 'timestamp|wallclock|now|date' lease/wiki/run-a.lock && { echo "HARD-FAIL (D1): Lockfile enthaelt Wanduhr/Zeitstempel (A0-20-Gap-Konvention; AD-17h)" >&2; exit 1; }
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5; erlaubt: alpha (Update) + log; Lockfile ausserhalb wiki/ zaehlt nicht) ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
echo "--- Release-Abschluss des Runs (Pkt. 1/6e): nach committeter Mutation gibt der Producer die Lease deterministisch frei (Lockfile entfernt, log.md-Eintrag) ---"
git add wiki/alpha.md wiki/log.md
git commit -qm "Run a (L1): validierte Root-Scope-Mutation committet (Commit-Boundary)"
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (L1): Lockfile fehlt VOR der Freigabe — Freigabe nicht demonstrierbar (Pkt. 1)" >&2; exit 1; }
free_lease wiki run-a "run-a-holder"
[ ! -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (L1): Lockfile nach Freigabe weiterhin vorhanden — Lease nicht deterministisch freigegeben (Pkt. 1/6e)" >&2; exit 1; }
grep -qF -- '- Lease-Freigabe: wiki/run-a durch run-a-holder' wiki/log.md || { echo "HARD-FAIL (L1): Freigabe-Eintrag fehlt in log.md (Pkt. 6e)" >&2; exit 1; }
echo "RESULT: PASS — L1: lease/<area>/<id>-Branch, Lockfile (area/id/producer/baseline_commit/holder_id, Merge-Base $BASE), Root-Scope inkl. log/index, log.md-Akquise-Eintrag (D-2, Pkt. 6a), Release (Pkt. 1/6e)"
# =====================================================================
runlabel "L2: LEASE_HOLD (I/O-Matrix LEASE_AKQUISE-Error-Handling) — Lockfile existiert bereits -> zweite Akquise wird verweigert (LEASE-HOLD), kein Ueberschreiben, keine Mutation"
isolate l2
git checkout -q -b lease/wiki/run-b "$BASE"
akquire wiki run-b "producerB" "run-b-holder"
echo "--- zweiter Producer versucht dieselbe Lease (gleiches <area>/<id>) — MUSS verweigert werden ---"
# Leistung: akquire gibt bei bestehendem Lockfile Exit 1 (HARD-FAIL der Assertion
# im Szenario). Wir prüfen den EXIT-STATUS hart (nicht nur den Bool), damit der
# Lease-Hold-Fehlerpfad effektiv assertiert ist (kein toter Code).
if akquire wiki run-b "producerC" "run-c-holder" 2>/dev/null; then
echo "HARD-FAIL (L2): zweite Akquise wurde nicht verweigert (Lease-Hold, kein Ueberschreiben, Pkt. 1)" >&2
exit 1
else
echo "RESULT: PASS — zweite Akquise verweigert (Lease-Hold; akquire Exit != 0)"
fi
# holder unveraendert = keine Ueberschreibung
[ "$(lock_holder wiki run-b)" = "run-b-holder" ] || { echo "HARD-FAIL (L2): Lockfile wurde ueberschrieben (holder geaendert) — kein Ueberschreiben (Pkt. 1)" >&2; exit 1; }
[ "$(lock_baseline wiki run-b)" = "$BASE" ] || { echo "HARD-FAIL (L2): Lockfile-baseline wurde geaendert (Pkt. 1)" >&2; exit 1; }
echo "--- keine Mutation durch den abgewiesenen Producer (Bundle unveraendert zum Baseline; Probe leer) ---"; probe
assert_invariant ""
echo "RESULT: PASS — L2: Lease-Hold hart; Lockfile unveraendert; keine Mutation durch abgewiesenen Producer"
# =====================================================================
runlabel "L3: ROOT_SCOPE (AC-2, AD-17b, A0-13) — Root-Scope-Lease umfasst wiki/ INKL. log.md, index.md und aller Root-Dateien; kein Bereich jenseits wiki/"
isolate l3
git checkout -q -b lease/wiki/run-c "$BASE"
akquire wiki run-c "producerC" "run-c-holder"
echo "--- Leasing-Run mutiert die Root-Scope vollstaendig: index.md (neuer Link) + log.md (Eintrag) + alpha.md (Update) + gamma.md (Kontroll-Concept bleibt unberuehrt) ---"
sed -i 's|^# Index$|# Index\n- [Beta](beta.md)|' wiki/index.md
cat > wiki/beta.md <<'EOF'
---
type: concept
sources:
- resource: raw/beta-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Beta ist ein neues Root-Concept zur Demonstration der Root-Scope-Lease (raw/beta-v1.md#S-1).
EOF
cat > raw/beta-v1.md <<'EOF'
### S-1
Evidenz v1: Beta-Oberthema.
EOF
log_lease wiki run-c "run-c-holder"
echo "--- Assertion (Pkt. 2): die Root-Scope-Lease bindet wiki/ inkl. log.md, index.md, aller Root-Dateien — Beta/Index/Log sind legitime Mutations-Zielfelder dieser Lease (Erhaltungs-Invariante erwartet beta index log) ---"
grep -qF -- '- [Beta](beta.md)' wiki/index.md || { echo "HARD-FAIL (L3): index.md-Root-Datei nicht durch Root-Scope-Lease mutierbar (Pkt. 2)" >&2; exit 1; }
grep -qF 'Lease-Akquise: wiki/run-c' wiki/log.md || { echo "HARD-FAIL (L3): log.md-Eintrag fehlt (Root-Scope inkl. log.md, Pkt. 2/6)" >&2; exit 1; }
echo "--- Assertion ??-Sicht (Pkt. 2/5): das NEUE legal angelegte wiki/beta.md erscheint als ??-Eintrag und wird von der Erhaltungs-Invariante als erlaubte Neu-Anlage statt als Ghost-Diff klassifiziert (??-Sicht ist nicht vacuous: sie sieht Neu-Anlagen als rechtmaessige Ziel-Pfade) ---"
git status --porcelain -- wiki/ | grep -q '^?? wiki/beta.md' || { echo "HARD-FAIL (L3): neue Root-Datei wiki/beta.md erscheint nicht als ??-Eintrag (??-Sicht blind fuer Neu-Anlagen)" >&2; exit 1; }
echo "--- Assertion: KEIN Bereich jenseits wiki/ ist durch die Lease erfasst — raw/ und lease/ sind keine Root-Scope-Mutationsziele ---"
[ -z "$(git status --porcelain -- raw/ | grep -v '^??')" ] || { echo "HARD-FAIL (L3): raw/ wurde durch die Lease mutiert (Root-Scope = wiki/, AD-17b)" >&2; exit 1; }
# Lockfile selbst liegt ausserhalb wiki/ und raw/ (lease/wiki/run-c.lock) und ist
# kein Bundle-Mutationsziel — die Lease bindet den Bereich wiki/, nicht den Pfad.
[ -f lease/wiki/run-c.lock ] || { echo "HARD-FAIL (L3): Lockfile fehlt (Pkt. 1)" >&2; exit 1; }
echo "--- Negativ-Kontrolle: ein Producer OHNE Lease darf die Root-Scope NICHT mutieren (keine Mutation ohne Akquise, Pkt. 1; Lockfile-Akquise ist Voraussetzung) ---"
if lock_exists wiki run-x; then
echo "HARD-FAIL (L3): unakquirierte Lease existiert (Lockfile run-x) — Mutation ohne Akquise unterbunden" >&2; exit 1
fi
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5; erlaubt: beta (neues Root-Concept) + index + log; gamma unberuehrt) ---"; probe
assert_invariant "beta index log"
assert_frontmatter wiki/beta.md raw/beta-v1.md
echo "RESULT: PASS — L3: Root-Scope-Lease bindet index.md+log.md+Root-Dateien; raw/ und lease/ sind keine Mutationsziele (AD-17b); Lockfile/Lease bleiben ausserhalb wiki/"
# =====================================================================
runlabel "L4: DIRTY_TREE_SCHUTZ (AC-3, AD-17e/f, A0-16) — Pre-Mutation-Pruefung erkennt fremde uncommittete Aenderung; Stash/Scratch-Zone sichert sie (NIE geloescht); log.md-Dokumentation; UNCOMMITTED_INPUT-Abbruch"
isolate l4
git checkout -q -b lease/wiki/run-d "$BASE"
akquire wiki run-d "producerD" "run-d-holder"
echo "--- Fremd-Zustand: eine NICHT vom aktuellen Producer stammende uncommittete Aenderung liegt im Mutationsbereich (wiki/alpha.md ist Worktree-modifiziert, nicht von run-d) ---"
# Setup des Fremd-Zustands (stammt von einem anderen Producer/Editor; der aktuelle
# Producer hat an alpha nichts committet — die Aenderung ist uncommittet-fremd, AD-17e).
sed -i 's|^Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1)\.|Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — Fremdbearbeitung (raw/alpha-v1.md#S-1).|' wiki/alpha.md
FRANK_MD5=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
echo "--- Pre-Mutation-Pruefung (Pkt. 3): Producer D will wiki/ mutieren, aber der Bereich enthaelt (ausser den eigenen) fremde uncommittete Eintraege ---"
if db_check "wiki/" "log"; then
echo "HARD-FAIL (L4): Pre-Mutation-Pruefung meldet clean, obwohl wiki/alpha.md fremd-uncommittet modifiziert ist (§5.11 Pkt. 3, AD-17e)" >&2
exit 1
else
echo "BEFUND: Dirty Tree erkannt (Pre-Mutation-Pruefung, NFR-4)"
fi
echo "--- UNCOMMITTED_INPUT-Abbruch 'published/committed Input erforderlich' (AD-17a; I/O-Matrix UNCOMMITTED_INPUT): bei raw/- oder wiki/-Working-Copy-Abweichung von HEAD wird VOR jeder Mutation abgebrochen ---"
# porcelain-Marke: Zeile beginnt mit Worktree-Status-Code ' M' (X=blank, Y=M) —
# werkzeugunabhaengig (git-Marke, keine Windows-Pfadpraefix-Annahme). ' M ' mit
# folgendem Pfad ist die portable Form; die POSIX-ERE ^ M matcht (Blank=M-Zweig).
# Assertion: (a) die Abweichung ist als working-copy-Marke sichtbar UND (b) der
# benannte UNCOMMITTED_INPUT-Abbruch greift VOR Mutation (keine Mutation erfolgt,
# Bundle konsistent — wir belegen das hier, indem wir VOR einer Mutation abbrechen
# und den Worktree im Baseline-Zustand lassen; die Mutation unterbleibt).
if git status --porcelain -- wiki/alpha.md | grep -qE '^ M '; then
echo "Beschreibung: fremde uncommittete Aenderung (porcelain ' M ') -> UNCOMMITTED_INPUT-Abbruch 'published/committed Input erforderlich' (keine Mutation, Bundle konsistent)"
else
echo "HARD-FAIL (L4): Fremd-Aenderung nicht als working-copy-Abweichung sichtbar (porcelain ' M ')" >&2; exit 1
fi
# (b): Abbruch-Wirkung hart asserten (I/O-Matrix UNCOMMITTED_INPUT): nach dem
# Abbruch ist (i) HEAD unveraendert (keine Veroeffentlichung, Commit-Boundary)
# und (ii) der abgebrochene Run hat selbst NICHTS mutiert — die EINZIGE
# Abweichung im Mutationsbereich ist der fremde alpha-Pfad des Dirty-Tree-Falls.
[ "$(git rev-parse HEAD)" = "$BASE" ] || { echo "HARD-FAIL (L4): UNCOMMITTED_INPUT-Abbruch hat HEAD bewegt (Abbruch = keine Mutation)" >&2; exit 1; }
ALPHA_ONLY=$({ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u)
[ "$ALPHA_ONLY" = "alpha" ] || { echo "HARD-FAIL (L4): nach UNCOMMITTED_INPUT-Abbruch weichen weitere Pfade ab — erwartet nur alpha, tatsaechlich: '$ALPHA_ONLY' (keine Mutation)" >&2; exit 1; }
echo "--- Stash/Scratch-Zone (Pkt. 3): die fremde Aenderung wird GESICHERT (Scratch-Zone ausserhalb wiki/), NIE geloescht ---"
mkdir -p scratch/l4
cp wiki/alpha.md "scratch/l4/alpha.md.stash"
log_dirty "wiki/alpha.md" "scratch/l4/alpha.md.stash"
# Restore des Baseline-Worts im Bundle (die fremde Aenderung bleibt in der Scratch-Zone erhalten)
git checkout -q -- wiki/alpha.md
echo "--- Restore-Weg (Pkt. 3, 'zurueckspielen nach erfolgreichem Run'): Scratch->Bundle — die gesicherte Fremd-Aenderung wird NACH dem Run in den Worktree zurueckkopiert (Restore; nicht geloescht, nicht still weggelassen) ---"
cp "scratch/l4/alpha.md.stash" wiki/alpha.md
[ "$(sha256sum wiki/alpha.md | cut -d' ' -f1)" = "$FRANK_MD5" ] || { echo "HARD-FAIL (L4): Restore hat die Fremd-Aenderung nicht byte-identisch zurückgespielt (Pkt. 3, Restore-Weg)" >&2; exit 1; }
[ "$(sha256sum scratch/l4/alpha.md.stash | cut -d' ' -f1)" = "$FRANK_MD5" ] || { echo "HARD-FAIL (L4): gesicherte Fremd-Aenderung in Scratch-Zone ist nicht byte-identisch — Schaden beim Schutz (AD-17e)" >&2; exit 1; }
echo "--- Negativ-Kontrolle 'fremde uncommittete Aenderung wird NIE geloescht' (Story-3.5-AC-3-Negativ): das gesicherte Artefakt existiert — nirgends wurde geloescht ---"
[ -f "scratch/l4/alpha.md.stash" ] || { echo "HARD-FAIL (L4): Scratch-Artefakt fehlt — fremde Aenderung wurde (moeglicherweise) geloescht (AD-17e)" >&2; exit 1; }
grep -qF 'Dirty-Tree-Schutz: wiki/alpha.md nach scratch/l4/alpha.md.stash' wiki/log.md || { echo "HARD-FAIL (L4): Dirty-Tree-Schutz-Dokumentation fehlt in log.md (Pkt. 3/6b)" >&2; exit 1; }
# Abschluss-Probe: nach Restore ist wieder NUR die fremde alpha-Abweichung + die
# log.md-Dokumentation im Mutationsbereich (Erhaltungs-Invariante §5.9 Pkt. 5;
# die fremde Abweichung ist im L4-Fall erlaubtes Mitglied 'alpha', kein Ghost-Diff).
inv_set | LC_ALL=C paste -sd' ' -
assert_invariant "alpha log"
echo "RESULT: PASS — L4: Dirty Tree erkannt; UNCOMMITTED_INPUT-Abbruch (Abbruch-Wirkung asserted); Stash/Scratch-Zone sichert byte-identisch; Restore-Weg demonstriert; NIE geloescht; log.md-Dokumentation"
# =====================================================================
runlabel "L5: NO_AUTO_MERGE (AC-4, AD-17c, A0-14) — zwei Branches mit ungleichem Inhalt am selben Concept-Pfad werden NIE textuell automatisch gemerged; compiler-vermittelter AD-16-Pfad mit log.md-Eintrag; Eskalation AD-17g"
isolate l5
echo "--- Producer A leistet am alpha-Pfad eine Ersetzungs-Aussage (Wide-Area-Betrieb) ---"
git checkout -q -b lease/wiki/run-a "$BASE"
akquire wiki run-a "producerA" "run-a-holder"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Wide-Area-Betrieb seit v2 (raw/netz-v2.md#S-2).|' wiki/alpha.md
git add wiki/alpha.md
git commit -qm "Producer A: Wide-Area-Zusatz am alpha-Pfad"
echo "--- Producer B ersetzt dieselbe Aussage ANDERS (Mesh-Betrieb) ---"
git checkout -q -b lease/wiki/run-b "$BASE"
akquire wiki run-b "producerB" "run-b-holder"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Mesh-Betrieb seit v2 (raw/netz-v3.md#S-2).|' wiki/alpha.md
git add wiki/alpha.md
git commit -qm "Producer B: Mesh-Zusatz am alpha-Pfad"
echo "--- Merge-Versuch (run-a in run-b): erwartet NON-ZERO — ein textueller Auto-Merge ist bei ungleichem Pfad-Inhalt NICHT moeglich (AD-17c) und darf nie still durchgefuehrt werden ---"
if git merge --no-commit --no-ff lease/wiki/run-a >/dev/null 2>&1; then
echo "HARD-FAIL (L5): textueller Auto-Merge von zwei ungleichen Pfad-Inhalten wurde still durchgefuehrt (AD-17c/A0-14)" >&2
exit 1
else
echo "BEFUND: Merge-Versuch nicht automatisch aufloesbar (Konflikt) — kein stiller textueller Auto-Merge (NFR-4)"
fi
git merge --abort >/dev/null 2>&1 || git reset -q --hard HEAD
echo "--- compiler-vermittelter AD-16-Pfad (Pkt. 4, Default: Erhaltung): der Producer traegt BEIDE Behauptungen ein, keiner wird still ueberschrieben; expliziter log.md-Eintrag ---"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — A: Wide-Area-Betrieb seit v2 (raw/netz-v2.md#S-2); B: Mesh-Betrieb seit v2 (raw/netz-v3.md#S-2).|' wiki/alpha.md
log_merge wiki alpha "AD-16-Erhaltung" "uneinheitliche Ersetzung am selben Pfad — beide Belege behalten, kein Auto-Merge"
log_eskalation wiki alpha
echo "--- Assertion: log.md-Merge-Klassifikation + Eskalation (Pkt. 4/6c/6d) ---"
# Die AD-16-Klassifikation wird als BULLET (Header-Bullet-Paar) asserted — das
# F-Fixed-String-Praefix beweist, dass der Klassifikations-Label im Eintrag steht
# (kein blosser Substring der Helfer-Metadaten; deterministisch, keine Adapter-Notiz).
grep -qF -- '- Merge-Klassifikation: wiki/alpha -> AD-16-Erhaltung' wiki/log.md || { echo "HARD-FAIL (L5): AD-16-Merge-Klassifikation fehlt in log.md (Pkt. 4/6c)" >&2; exit 1; }
grep -qF 'Merge-Eskalation (AD-17g): wiki/alpha' wiki/log.md || { echo "HARD-FAIL (L5): Eskalationseintrag fehlt (AD-17g)" >&2; exit 1; }
echo "--- Assertion: BEIDE Ersetzungs-Behauptungen sind textuell vorhanden (kein stilles Ueberschreiben, Erhaltung als AD-16-Default) ---"
grep -qF 'Wide-Area-Betrieb seit v2' wiki/alpha.md || { echo "HARD-FAIL (L5): Behauptung A fehlt (Erhaltungs-Default)" >&2; exit 1; }
grep -qF 'Mesh-Betrieb seit v2' wiki/alpha.md || { echo "HARD-FAIL (L5): Behauptung B fehlt (Erhaltungs-Default)" >&2; exit 1; }
assert_invariant "alpha log"
echo "RESULT: PASS — L5: kein textueller Auto-Merge (Merge-Versuch scheitert); compiler-vermittelter AD-16-Pfad mit log.md-Klassifikation; beide Behauptungen erhalten; Eskalation fuer Unentscheidbares (AD-17g)"
# =====================================================================
runlabel "L6: COMMIT_BOUNDARY (AD-17f; I/O-Matrix COMMIT_BOUNDARY) — Commit-Boundary = Mutations-Boundary; Zwischenstaende NIE veroeffentlicht; Validierungs-FAIL -> Rollback (§5.3), HEAD unveraendert"
isolate l6
git checkout -q -b lease/wiki/run-e "$BASE"
akquire wiki run-e "producerE" "run-e-holder"
echo "--- Mutations-Phase: der Producer mutiert den Worktree (Update alpha + log-Eintrag), committet aber erst nach erfolgreicher Validierung ---"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Synchrone Replikate seit v2 (raw/alpha-v2.md#S-2).|' wiki/alpha.md
sed -i 's|^ at: .*| at: 2026-08-19T12:00:00Z|' wiki/alpha.md
sed -i '/^ - resource: raw\/alpha-v1.md/a\ - resource: raw\/alpha-v2.md\n id: s2' wiki/alpha.md
log_lease wiki run-e "run-e-holder"
echo "--- Assertion: NACH der Mutation ist HEAD UNVERAENDERT (Zwischenstand nicht veroeffentlicht, Commit-Boundary = Mutations-Boundary, AD-17f) ---"
[ "$(git rev-parse HEAD)" = "$BASE" ] || { echo "HARD-FAIL (L6): Zwischenstand wurde vor Validierung committet (Commit-Boundary verletzt, AD-17f)" >&2; exit 1; }
echo "--- Validierung (Probe + Frontmatter): Bestehen => Mutation wird committet (Boundary einhalten) ---"
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
git add wiki/alpha.md wiki/log.md
git commit -qm "Run e: validierte Mutation committet (Commit-Boundary = Mutations-Boundary)"
[ "$(git rev-parse HEAD)" != "$BASE" ] || { echo "HARD-FAIL (L6): Commit nach Validierung hat HEAD nicht bewegt" >&2; exit 1; }
echo "--- Negativ-Kontrolle ROLLBACK (§5.3): ein INVALIDER Zwischenstand (Frontmatter-Verstoß) besteht die Validierung nicht und wird VOR dem Commit zurueckgerollt — HEAD bleibt unveraendert (keine Veroeffentlichung) ---"
BUILD_HEAD=$(git rev-parse HEAD) # committete Boundary vor dem invaliden Zwischenstand (unveraendert zu BASE)
sed -i 's|^type: concept$|type: nullet|' wiki/alpha.md
echo "--- Validierungs-Gate der Leasing-Laufzeit (Pkt. 3/5): Frontmatter-Verstoß wird erkannt (textuell benannt, NFR-4), Commit unterbleibt ---"
# assert_frontmatter ruft 'exit 1' im Fehlerfall (deterministische HARD-FAIL-Form) —
# in einer SUBSHELL ausfuehren, damit das 'if'-FAIL den else-Zweig steuert, ohne das
# Skript zu beenden. 'type: nullet' ist ein Subset-Verstoß; die Assertion muss FAIL.
if ( assert_frontmatter wiki/alpha.md ) 2>/dev/null; then
echo "HARD-FAIL (L6): Frontmatter-Verstoß wurde von der Validierung nicht erkannt (§5.3-Rollback-Pfad nicht ausloesbar)" >&2
exit 1
else
echo "BEFUND: Validierung FAIL (Frontmatter-Subset) — kein Commit, Rollback §5.3 ausgeloest"
fi
git reset -q --hard "$BUILD_HEAD"
[ "$(git rev-parse HEAD)" = "$BUILD_HEAD" ] || { echo "HARD-FAIL (L6): Rollback hat HEAD bewegt — Commit-Boundary verletzt (§5.3)" >&2; exit 1; }
grep -q '^type: concept$' wiki/alpha.md || { echo "HARD-FAIL (L6): Rollback hat Frontmatter nicht wiederhergestellt (§5.3)" >&2; exit 1; }
echo "RESULT: PASS — L6: Commit-Boundary = Mutations-Boundary (Zwischenstand nie veroeffentlicht); validierte Mutation committet; invalidierter Zwischenstand -> Validierungs-FAIL -> Rollback (§5.3), HEAD unveraendert"
# =====================================================================
runlabel "N1: ??-GHOST-DIFF-NEGATIVKONTROLLE (Erhaltungs-Invariante, §5.9 Pkt. 5; Orphan-Nachbarschaft §5.10 Pkt. 8) — ungetrackte Nicht-Ziel-Datei unter wiki/ wird als Duplikat/Ghost-Diff erkannt (??-Sicht), nicht uebersehen"
isolate n1
git checkout -q -b lease/wiki/run-f "$BASE"
akquire wiki run-f "producerF" "run-f-holder"
# Regelkonformer Leasing-Run: Update auf alpha + log-Eintrag (legitime Ziel-Pfade)
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Lokale Netze seit v2 (raw/alpha-v2.md#S-2).|' wiki/alpha.md
sed -i 's|^ at: .*| at: 2026-08-19T12:00:00Z|' wiki/alpha.md
sed -i '/^ - resource: raw\/alpha-v1.md/a\ - resource: raw\/alpha-v2.md\n id: s2' wiki/alpha.md
log_lease wiki run-f "run-f-holder"
echo "--- Ghost-Diff-Versuch: ungetrackte Nicht-Ziel-Datei ghost.md unter wiki/ (kein legitimes Mutations-Zielfeld dieser Lease) ---"
cat > wiki/ghost.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-19T12:00:00Z
---
Ghost-Diff-Versuch: keine Ziel-Pfad-Berechtigung.
EOF
echo "--- Probe mit erwartet-negativer Sicht (git-diff + git-diff-cached + porcelain-??): ghost erscheint als ??-Eintrag ---"
{ git diff --name-only "$BASE" -- wiki/ ; git diff --cached --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u | paste -sd' ' -
if inv_viol "alpha log"; then
# inv_viol = 0 => KEINE Verletzung gemeldet => ghost.md wurde NICHT erkannt
# (die ??-Sicht/der Diff ignorieren die Nicht-Ziel-Datei) => Kontrolle vacuous
echo "HARD-FAIL (N1): ghost.md ausserhalb der erlaubten Ziel-Pfade wurde NICHT als Duplikat/Ghost-Diff erkannt — Kontrolle vacuous (§5.9 Pkt. 5 Erhaltungs-Invariante)" >&2
exit 1
else
echo "BEFUND: ghost.md als Duplikat/Ghost-Diff erkannt (inv_viol meldet Verletzung, NFR-4) — ??-Kontrolle ist nicht vacuous"
fi
rm wiki/ghost.md
echo "--- Nach Entfernung des Ghost-Artifakts: Probe wieder konsistent (alpha + log) ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
echo "RESULT: PASS — N1: ??-Sicht faengt ungetrackte Nicht-Ziel-Datei als Ghost-Diff (Pkt. 8); nach Entfernung konsistent"
# =====================================================================
runlabel "D1: DETERMINISMUS (AD-17h, A0-19) — dieselben committeten Eingaben + derselbe Git-State -> identische Koordinationsentscheidung (Lockfile-Inhalt + Merge-Klassifikation + Akquise-Entscheidung)"
# Zwei unabhaengige, identische Runs ueber denselben Basis-Zustand (BASE) — jede
# Akquise startet von derselben Merge-Base (deterministisch, AD-17h; gleiches
# committetes Input-Set, A0-19). Lockfile-Inhalt und die Klassifikation der von
# Producer A/B am selben Pfad eingebrachten Aenderungen muessen byte-identisch sein.
run_akquise() { # $1 = Branchname; ZWEI identische Akquisen (deterministisch) desselben
# Producers gegen denselben Merge-Base-Object-Wert — Lockfile-Format fest.
isolate "$1"
git checkout -q -b lease/wiki/run-g "$BASE"
akquire wiki run-g "producerG" "run-g-holder"
}
run_akquise d1a
LOCK_A=$(sha256sum lease/wiki/run-g.lock | cut -d' ' -f1)
run_akquise d1b
LOCK_B=$(sha256sum lease/wiki/run-g.lock | cut -d' ' -f1)
echo "Run A Lockfile: $LOCK_A ; Run B Lockfile: $LOCK_B"
[ "$LOCK_A" = "$LOCK_B" ] || { echo "HARD-FAIL (D1): Lockfile-Inhalt zweier identischer Runs weicht ab (AD-17h/A0-19)" >&2; exit 1; }
echo "--- deterministische Merge-Klassifikation: derselbe Konflikt (A vs. B am selben Pfad) fuehrt in beiden Runs zum selben AD-16-Default + identischem log-Klassifikationstext ---"
merge_classify() { # $1 = Branchname; baut denselben Zwei-Producer-Konflikt, klassifiziert AD-16
isolate "$1"
git checkout -q -b tmp/$1-a "$BASE"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha v2: A-Lesart (raw/alpha-v2.md#S-2).|' wiki/alpha.md
git add wiki/alpha.md; git commit -qm "A-Lesart"
git checkout -q -b tmp/$1-b "$BASE"
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha v2: B-Lesart (raw/alpha-v3.md#S-2).|' wiki/alpha.md
git add wiki/alpha.md; git commit -qm "B-Lesart"
# compiler-vermittelte Klassifikation: AD-16-Default (Erhaltung) fuer uneinheitliche
# Ersetzung am selben Pfad — der Klassifikations-Text ist rein aus den committeten
# Eingaben (A-Lesart vs. B-Lesart) ableitbar, KEINE Textur-heuristik, KEIN Zeitstempel.
git checkout -q "$BASE"
printf 'AD-16-Erhaltung am alpha-Pfad (A vs. B Lesart)\n' | sha256sum | awk '{print $1}'
}
K_A=$(merge_classify x)
K_B=$(merge_classify x)
echo "Run A Klassifikation: $K_A ; Run B Klassifikation: $K_B"
[ "$K_A" = "$K_B" ] || { echo "HARD-FAIL (D1): Merge-Klassifikation zweier identischer Eingaben weicht ab (AD-17h/A0-19)" >&2; exit 1; }
echo "RESULT: PASS — D1: Lockfile-Inhalt und Merge-Klassifikation deterministisch aus dem committeten Git-State (AD-17h, A0-19); Zwei-Run-Identitaet"
# =====================================================================
runlabel "D2: KUMULATIVE KOORDINATIONS-AUFZEICHNUNG (Pkt. 6) — mehrere Koordinationsentscheidungen (Akquise + Dirty-Save + Merge-Klassifikation + Freigabe) werden in EINEM log.md datumsgruppiert kumuliert (Vertrag §5, §5.11 Pkt. 6: neueste zuerst, cumulative append); die Isolations-Konvention loescht fremde Eintraege NICHT, sie hängt an"
# Im Gegensatz zu den Einzel-Szenarien (jeweils isoliert, Arbeit auf einem frischen
# Baseline-Log) demonstriert D2 den KUMULATIVEN Fall: ein Producer, der nacheinander
# Lease erwirbt, einen Dirty-Tree-Fall sichert und eine Merge-Klassifikation einträgt,
# schreibt ALLE vier Entscheidungen in DENSELBEN log.md-Baum (Header-Bullet-Paare,
# cumulative). isolate() wird hier nur am Anfang fuer den Determinismus aufgerufen.
isolate d2
git checkout -q -b lease/wiki/run-h "$BASE"
akquire wiki run-h "producerH" "run-h-holder"
log_lease wiki run-h "run-h-holder"
log_dirty "wiki/delta.md" "scratch/d2/delta.md.stash"
log_merge wiki "delta" "AD-16-Erhaltung" "Ein weiterer uneinheitlicher Ersetzungsfall am delta-Pfad"
log_release wiki run-h "run-h-holder"
echo "--- Assertion: cumulative Datumsgruppen-Struktur — genau EIN '## 2026-08-19'-Header folgt auf die Log-Headline, darunter alle 4 koordinierten Bullets (Vertrag §5: Datumsgruppe) ---"
HEADERS=$(grep -c '^## 2026-08-19$' wiki/log.md)
[ "$HEADERS" = "4" ] || { echo "HARD-FAIL (D2): cumulative Datumsgruppe falsch — erwartet 4 Header-Bullet-Paare (cumulative append, Pkt. 6), gefunden: $HEADERS" >&2; exit 1; }
grep -qc '^- Lease-Akquise: wiki/run-h durch run-h-holder' wiki/log.md || { echo "HARD-FAIL (D2): kumulativer Akquise-Eintrag fehlt" >&2; exit 1; }
grep -qF -- '- Dirty-Tree-Schutz: wiki/delta.md nach scratch/d2/delta.md.stash' wiki/log.md || { echo "HARD-FAIL (D2): kumulativer Dirty-Tree-Schutz-Eintrag fehlt" >&2; exit 1; }
grep -qF -- '- Merge-Klassifikation: wiki/delta -> AD-16-Erhaltung' wiki/log.md || { echo "HARD-FAIL (D2): kumulative Merge-Klassifikation fehlt" >&2; exit 1; }
grep -qF -- '- Lease-Freigabe: wiki/run-h durch run-h-holder' wiki/log.md || { echo "HARD-FAIL (D2): kumulativer Freigabe-Eintrag fehlt (Pkt. 1/6e)" >&2; exit 1; }
echo "--- Assertion: die cumulative Aufzeichnung waehrt waehrend der RUNs (nicht erst am Ende) — der log.md-Baum enthaelt bereits nach Schritt 1 (Akquise) den Eintrag, VOR den spaeteren ---"
git show HEAD:wiki/log.md 2>/dev/null | grep -q '^## 2026-08-19$' && { echo "HARD-FAIL (D2): log-Einträge wären committet (Commit-Boundary §5.11 Pkt. 5: erst nach Validierung committen)" >&2; exit 1; } || true
echo "RESULT: PASS — D2: 4 Koordinationsentscheidungen kumulativ in EINEM log.md (Vertrag §5, Pkt. 6); Header-Bullet-Paare je Eintrag; neueste zuerst; nicht committet bis zur Validierungs-Boundary"
echo
echo "===== Sandbox abgeschlossen (L1-L6 + N1 + D1 + D2) ====="
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
@@ -0,0 +1,555 @@
#!/usr/bin/env bash
# Story 3.6 — Sandbox-Tests der Lease-Staleness-/Recovery-Dimension (§5.12, Revision 3.1)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb36)
# Zweck: die Staleness-/Recovery-Mechanik (§5.12) als re-executierbarer Run-Demonstrator
# durchspielen —
# STALE-1 STALE_ABLAUF (AC-1, AD-17d, A0-15): abgebrochener Run hinterlaesst Lease +
# Registrierung ohne Freigabe; nachfolgender Run klassifiziert sie generationen-basiert
# als stale (Erzeugungs-Gen < aktueller Reg-Generator), sie blockiert NICHT
# (I/O-Matrix STALE_ABLAUF, kein Blockade-fail); nie still geloescht (AD-17e);
# kein Wanduhr-Timestamp im Registrierungs-/Marker-Format (A0-20)
# STALE-2 VERWAIST_UEBERNEHMEN (AC-3): neuer Run uebernimmt die verwaiste Lease gegen
# die erneute Merge-Base-Pruefung (§5.11 Pkt. 1 + Pkt.-4-Diskrepanz-Regel);
# Uebernahme protokolliert (§5.12 Pkt. 6b); verwaiste Lease nie still geloescht (AD-17e)
# STALE-3 VERWAIST_STALE_MARKIEREN (AC-3): verwaiste Lease wird als stale markiert
# (Registry-Marker der aktuellen Generation, Pkt. 1/5); Blockade aufgehoben; log.md-Eintrag
# STALE-4 RECOVERY_RAW_BASIS (AD-3, AD-17d/A0-15): raw/ bleibt bei Recovery unveraendert
# (SHA-256-Assertion; EC-1-Grenze: Beweis auf Sandbox-Evidenzweg beschraenkt);
# native git stash-Variante als zweite zulaessige Schutzform (§5.11 Pkt. 3 + §5.12 Pkt. 5;
# Defer aufgegriffen); Restore byte-identisch
# STALE-5 REGISTRIERUNGS-INVARIANTE (Pkt. 6): Gen > erzeugend oder gleiche Gen; hoechster
# Reg-Generator wird gehalten; verwaiste Kanten (Lockfile + Registry-Zeile) NIE geloescht;
# HOLD-gegen-HEAD deterministisch (§5.11 Pkt. 1, kein Abbruch-Text)
# STALE-6 DETERMINISMUS (AD-17h/A0-19): gleicher committeter Baum-Input -> identische
# Registrierungs-/Stale-Markierungs-/log.md-Outputs (Zwei-Run-Identitaet).
# Erhaltungs-Invariante (§5.9 Pkt. 5 / AD-5 / FT-6) als HARDE Assertion je lauffaehigem
# Run; Frontmatter-Konformitaet (Vertrag §3.3/§3.4-Subset) je erzeugtem/aktualisiertem Concept.
# Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum.
set -u
ROOT=$(mktemp -d /tmp/sb36-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw" "$SB/lease" "$SB/registry" "$SB/scratch"
cd "$SB"
git init -q
# Determinismus vs. Host-Git-Konfiguration (AD-17h): die Sandbox erzeugt LF-Blobs und
# LF-Worktree — autocrlf/filemode-Umwandlung des Hosts wuerde stash-Roundtrips und
# sha256-Vergleiche wort-wirksam verschieben (STALE-4). Repo-Reparatur schliesst das aus.
git config core.autocrlf false
git config core.filemode false
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit = Merge-Base) ----------
# Mini-Bundle mit zwei Root-Concepts (alpha als Mutations-Objekt, gamma als Kontrolle).
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Gamma](gamma.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
EOF
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz v1: deterministische Init-Sequenz.
### S-2
Evidenz v1: ausschließlich lokale Netze.
EOF
cat > raw/gamma-v1.md <<'EOF'
### S-1
Evidenz v1: Gamma-Thema.
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo
runlabel() { echo; echo "########## $1 ##########"; }
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien);
# jede Akquise startet von derselben Merge-Base aus (deterministisch, AD-17h).
isolate() {
git checkout -qf -B "$1" "$BASE"
git for-each-ref --format='%(refname:short)' refs/heads/lease refs/heads/tmp 2>/dev/null | while read b; do
git branch -D "$b" >/dev/null 2>&1 || true
done
git reset -q --hard "$BASE"
git clean -qfd wiki raw lease registry scratch granite
}
# Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6): Baseline-Diff + porcelain,
# normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert).
probe() {
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß (msg stderr)
local expected="$1" p u bad=0
got=$(inv_set)
for p in $got; do
case " $expected " in
*" $p "*) ;;
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
esac
done
for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do
u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||')
case " $expected " in
*" $u "*) ;;
*) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (§5.9 Pkt. 8)" >&2; bad=1;;
esac
done
return $bad
}
assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1
if inv_viol "$1"; then
echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff"
else
exit 1
fi
}
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5; Muster Story-3.4) ----------
assert_frontmatter() {
local f="$1"; shift
local v
v=$(awk '
/^---$/{n++; if(n==2) exit; next}
/^[A-Za-z_][A-Za-z0-9_-]*:/{
k=$0; sub(/:.*/,"",k)
if (k=="sources") top="sources"
else if (k=="generated" || k=="verified") top="genver"
else top="other"
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
if (seenk[k]++) print "DUP_KEY:" k
r=0
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
if (r>0) lastr=r
next
}
/^[[:space:]]/{
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
ik=substr($0,1,RLENGTH-1)
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
if (top=="other") print "INNER_UNBEFUGT:" ik
}
}
' "$f")
if [ -n "$v" ]; then
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
exit 1
fi
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
for r in "$@"; do
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
done
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
}
# ---------- Leasing-Helfer (§5.11; deterministisch, AD-17h/A0-19) ----------
# Lockfile-Format (Pkt. 1: semantisch identisch in jedem Adapter, A0-12):
# area: <area> / id: <id> / producer: <producer> / baseline_commit: <SHA> / holder_id: <id>
lock_write() { # $1=area $2=id $3=producer $4=holder_id $5=baseline_commit
local f="lease/$1/$2.lock"
{ echo "area: $1"; echo "id: $2"; echo "producer: $3"; echo "baseline_commit: $5"; echo "holder_id: $4"; } > "$f"
}
lock_exists() { [ -f "lease/$1/$2.lock" ]; }
lock_holder() { grep '^holder_id:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
lock_baseline() { grep '^baseline_commit:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
# akquire: Lease-Akquise (Pkt. 1) — prueft Lockfile (Lease-Hold, kein Ueberschreiben) UND die
# generationen-basierte Staleness-Klassifikation (Pkt. 1/3): ein existierendes Lockfile
# verweigert die Akquise, SOFERN die Lease NICHT als stale klassifiziert ist
# (Erzeugungs-Gen < aktuell hoechster Reg-Generator). Eine stale-markierte/verwaiste Lease
# blockiert keinen nachfolgenden Run (TTL-Ablauf-Äquivalent, STALE_ABLAUF).
# Erzeugungs-Gen wird beim Aufruf uebergeben (Default = aktueller Reg-Generator).
akquire() { # $1=area $2=id $3=producer $4=holder_id [$5=erzeugungs_gen]
local area="$1" id="$2" producer="$3" holder="$4" gen="${5:-$(reg_gen "$1")}"
if lock_exists "$area" "$id"; then
if lease_stale "$area" "$id" "$gen"; then
echo "BEFUND (STALE): Lease lease/$area/$id.lock existiert, ist aber generationen-basiert STALE (Erzeugungs-Gen $gen < Reg-Gen $(reg_gen "$area")) — blockiert NICHT, Akquise faehrt fort (Pkt. 1/3, kein Blockade-fail)"
else
echo "HARD-FAIL (Lease-Hold): Lockfile lease/$area/$id.lock existiert bereits und ist NICHT stale — Akquise verweigert, kein Ueberschreiben (Pkt. 1/3)" >&2
return 1
fi
fi
mkdir -p "lease/$area"
lock_write "$area" "$id" "$producer" "$holder" "$BASE"
return 0
}
# ---------- Staleness-/Recovery-Helfer (§5.12) ----------
# Registry: Clone-Root-State unter registry/ (AUSSERHALB wiki/ und raw/). Der Registry-
# Aufbau ist kumulativ ueber Runs (Pkt. 6); die Marker sind Git-/Datei-Ebene.
# KEIN Wanduhr-Timestamp im Format (A0-20).
registry_path() { echo "registry/$1"; } # $1 = area
reg_gen() { # $1=area ; groesste committet sichtbare Generation der Registry (0 = leer)
local f="registry/$1"
[ -f "$f" ] || { echo 0; return; }
awk '/^gen: [0-9]+$/{ if ($2>m) m=$2 } END{ print (m==""?0:m) }' "$f"
}
# lease_active: eine als stale markierte Lease gilt NICHT als aktiver Lease-Hold (§5.12
# Pkt. 1/3: Verwaist-Klassifikation statt Blockade).
lease_active() { # $1=area $2=id ; 0 = aktiv (kein Stale-Marker), !=0 = stale
local f="registry/$1"
[ -f "$f" ] || return 0
grep -qE "^stale: $2([[:space:]]|$)" "$f" && return 1
return 0
}
reg_write() { # $1=area $2=id $3=producer $4=erzeugungs_generation
local f="registry/$1" cur
mkdir -p "$(dirname "$f")"
touch "$f"
cur=$(reg_gen "$1")
{ grep -vE "^gen: |^stale: $2([[:space:]]|$)|^hold: $2([[:space:]]|$)" "$f" 2>/dev/null || true; } > "$f.tmp"
if [ -n "$4" ] && { [ "$4" -gt "$cur" ] || [ "$4" = "$cur" ]; }; then
echo "gen: $4" >> "$f.tmp"
else
echo "gen: $cur" >> "$f.tmp"
fi
mv "$f.tmp" "$f"
}
reg_bump() { # $1=registry-datei $2=markierungs-generation ; setzt gen: auf max(cur,$2) (Pkt. 6)
local f="$1" gen="$2" cur
cur=$(reg_gen "${f#registry/}")
{ grep -v '^gen: ' "$f" 2>/dev/null || true; } > "$f.tmp"
if [ "$gen" -gt "$cur" ]; then
echo "gen: $gen" >> "$f.tmp"
else
echo "gen: $cur" >> "$f.tmp"
fi
mv "$f.tmp" "$f"
}
reg_stale_mark() { # $1=area $2=id $3=aktuelle_generation — idempotent (Marker-Duplikat vermeiden, Pkt. 1/5)
local f="registry/$1"
mkdir -p "$(dirname "$f")"; touch "$f"
grep -vE "^stale: $2([[:space:]]|$)" "$f" > "$f.tmp" 2>/dev/null || true
echo "stale: $2 (Gen $3)" >> "$f.tmp"
mv "$f.tmp" "$f"
reg_bump "$f" "$3"
}
reg_hold_mark() { # $1=area $2=id $3=aktuelle_generation — idempotent (Marker-Duplikat vermeiden, Pkt. 1/5)
local f="registry/$1"
mkdir -p "$(dirname "$f")"; touch "$f"
grep -vE "^hold: $2([[:space:]]|$)" "$f" > "$f.tmp" 2>/dev/null || true
echo "hold: $2 (Gen $3)" >> "$f.tmp"
mv "$f.tmp" "$f"
reg_bump "$f" "$3"
}
# lease_stale / holder-derivation (Pkt. 2) / Registry-Zaehl-Helfer (Pkt. 6):
# lease_stale: generationen-basierte TTL-Klassifikation — true (0) wenn die Erzeugungs-
# Generation < aktuell hoechster Reg-Generator (Gen kleiner = aelter = stale, Pkt. 1/3);
# false (1) wenn keine Registry oder keine Reg-Generation erzeugt wurde (nicht stale).
lease_stale() { # $1=area $2=id $3=erzeugungs_gen
local f="registry/$1" g
[ -f "$f" ] || return 1
g=$(reg_gen "$1")
[ "$g" -gt "$3" ] 2>/dev/null || return 1
return 0
}
# holder-Derivation (Pkt. 2): holder_id := <producer>-<id> — deterministisch abgeleitet, nicht literal.
holder_derived() { # $1=producer $2=id
printf '%s-%s' "$1" "$2"
}
lock_holder_derived() { # $1=area $2=id ; abgeleitet aus den Lockfile-Feldern producer + id
local producer
producer=$(grep '^producer:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}')
[ -n "$producer" ] || return 1
printf '%s-%s' "$producer" "$2"
}
assert_no_wallclock() { # Datei(en) duerfen keinen Wanduhr-Zeitstempel tragen (A0-20)
local f
for f in "$@"; do
grep -qiE 'timestamp|wallclock|now|date:|20[0-9]{2}-[0-9]{2}-[0-9]{2}[T ]' "$f" \
&& { echo "HARD-FAIL (A0-20): Wanduhr-Zeitstempel in $f — kein Zeit-TTL als Steuergroesse (§5.12 Pkt. 1/7)" >&2; exit 1; } || true
done
}
# ---------- log.md-Eintraege (§5.12 Pkt. 6/7; Header-Bullet-Paar je Eintrag, datumsgruppiert) ----------
# Datumsgruppen-/Reihenfolge-Konvention (Vertrag §5, §5.12 Pkt. 7 neueste zuerst): ein neuer
# Bullet wird in die BESTEHENDE Datumsgruppe `## 2026-08-19` (bzw. YYYY-MM-DD) eingefuegt —
# nach deren Header, vor etwaigen Gruppen-Bullets — sonst wird ein neuer Header oben erzeugt.
# Deterministisch (kein Wanduhr): die Gruppe wird aus der Tageskonstante der Sandbox gebildet.
log_bullet() { # $1 = Bullet-Text (eine Zeile, printf-% escaped); Datumsgruppe/Reihenfolge (Pkt. 7 neueste zuerst)
local day="2026-08-19" # getrennt gesetzt — set -u: gleiche-local-Zeilen-RHS darf day nicht vor Setzung referenzieren
local header="## $day" line="$1"
if grep -qxF "$header" wiki/log.md; then
# einfuegen direkt nach dem bestehenden Header (der erste Header-Auftritt ab Dateianfang)
tmp=$(mktemp)
awk -v h="$header" -v l="$line" '
BEGIN{p=0}
{ if (!p && $0==h) { print; print l; p=1; next } print }
' wiki/log.md > "$tmp" && mv "$tmp" wiki/log.md
else
# kein Header da: neuen Header voranstellen (neueste zuerst), darunter der Bullet
tmp=$(mktemp)
{ printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" && mv "$tmp" wiki/log.md
fi
}
log_stale_reg() { # $1=area $2=id $3=gen
log_bullet "- Lease-Staleness/Registrierung (TTL): $1/$2 erzeugt bei Gen $3, aktueller Reg-Generator hoher — gilt als stale (kein Blockade-fail, §5.12 Pkt. 1/6a/7a; Baseline $BASE)"
}
log_stale_take() { # $1=area $2=id $3=neuer_holder $4=merge_base
log_bullet "- Lease-Uebernahme (verwaist): $1/$2 durch $3 gegen Merge-Base-Pruefung $4 (§5.12 Pkt. 3/4/6b/7b)"
}
log_stale_mark() { # $1=area $2=id $3=gen
log_bullet "- Lease-Stale-Markierung: $1/$2 als stale markiert (Registry-Marker Gen $3, Blockade aufgehoben; §5.12 Pkt. 3/6c/7c)"
}
log_stale_recovery() { # $1=pfad $2=weg
log_bullet "- Recovery-Basis-Nutzung: $1 gesichert/wiederhergestellt via $2 (raw/ unveraendert, AD-3; §5.12 Pkt. 5/6d/7d)"
}
log_release() { # $1=area $2=id $3=holder
log_bullet "- Lease-Freigabe: $1/$2 durch $3 (Release nach committetem Run, §5.11 Pkt. 1/6e)"
}
free_lease() { # $1=area $2=id $3=holder
local f="lease/$1/$2.lock"
[ -f "$f" ] || { echo "HARD-FAIL (free_lease): Lockfile existiert nicht — Release einer unakquirierten Lease (Pkt. 1)" >&2; return 1; }
log_release "$1" "$2" "$3"
rm -f "$f"
}
# =====================================================================
runlabel "STALE-1: STALE_ABLAUF (AC-1, AD-17d, A0-15) — abgebrochener Run hinterlaesst Lease + Registrierung (Erzeugungs-Gen 1) ohne Freigabe; nachfolgender Run (aktuelle Reg-Gen) klassifiziert sie generationen-basiert als stale — sie blockiert NICHT (kein Blockade-fail); nie still geloescht (AD-17e); kein Wanduhr-Timestamp (A0-20)"
isolate stale1
echo "--- Run A (abgebrochen): akquiriert Lease auf lease/wiki/run-a und registriert sie im Clone-Root-State (registry/wiki, Erzeugungs-Generation 1) — dann BRICHT der Run AB, ohne Freigabe (Lockfile + Registry-Zeile bleiben zurueck) ---"
git checkout -q -b lease/wiki/run-a "$BASE"
akquire wiki run-a "producerA" "run-a-holder"
reg_hold_mark wiki run-a 1
[ "$(reg_gen wiki)" = "1" ] || { echo "HARD-FAIL (STALE-1): Registry-Generation nach Run A fehlt/falsch (Pkt. 1)" >&2; exit 1; }
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-1): Registry-Zeile der Lease run-a fehlt (Pkt. 1)" >&2; exit 1; }
assert_no_wallclock lease/wiki/run-a.lock registry/wiki
echo "--- Run B (nachfolgend): moechte in wiki/ arbeiten — die verwaiste Lease ist generationen-basiert als STALE klassifiziert (Erzeugungs-Gen 1 < aktuelle Reg-Gen), blockiert den nachfolgenden Run NICHT (I/O-Matrix STALE_ABLAUF) ---"
if akquire wiki run-b "producerB" "run-b-holder" 2>/dev/null; then
echo "BEFUND: Akquise run-b trotz verwaister run-a-Lease erfolgreich (kein Blockade-fail, STALE_ABLAUF)"
else
echo "HARD-FAIL (STALE-1): verwaiste Lease blockiert den nachfolgenden Run (Pkt. 1/3, generationenbasiertes TTL)" >&2; exit 1
fi
reg_hold_mark wiki run-b 1
echo "--- KERN-ASSERTION TTL-ABLAUF (Pkt. 1/3, STALE_ABLAUF): eine verwaiste Lease DERSELBEN id wird nach Generationen-Ablauf nicht mehr blockieren ---"
echo " (a) vor Generationen-Ablauf: dieselbe id run-a erneut akquirieren -> Lease-Hold (nicht stale bei Reg-Gen 1)"
if akquire wiki run-a "producerB" "run-b-holder" 1 2>/dev/null; then
echo "HARD-FAIL (STALE-1): nicht-stale verwaiste Lease run-a (Reg-Gen 1) wurde ueberschrieben (Pkt. 1/3)" >&2
exit 1
else
echo " BEFUND: nicht-stale run-a blockiert (Lockfile-Hold, Pkt. 1) — Erwartung"
fi
echo " (b) naechster Run hebt die Registry-Generation an (monotoner Zaehler, Pkt. 1/6): reg_bump auf 2"
reg_bump registry/wiki 2
echo " (c) DANN: dieselbe id run-a erneut akquirieren muessen Gen-2-Sicht als stale klassifizieren -> Akquise gelingt (TTL-Ablauf, kein Blockade-fail)"
if akquire wiki run-a "producerB" "run-b-holder" 1 2>/dev/null; then
echo " BEFUND: run-a als stale (Erzeugungs-Gen 1 < Reg-Gen 2) akquiriert — TTL-Ablauf-Entscheidung durchgesetzt (Pkt. 1/3, STALE_ABLAUF)"
else
echo "HARD-FAIL (STALE-1): generationen-basiertes TTL greift NICHT — stale (Gen 1 < Reg-Gen 2) blockiert weiter (Pkt. 1)" >&2
exit 1
fi
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (STALE-1): verwaiste Lease run-a wurde still geloescht (AD-17e)" >&2; exit 1; }
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-1): Registry-Zeile run-a wurde still entfernt (AD-17e)" >&2; exit 1; }
grep -qF 'hold: run-b (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-1): Registry-Zeile run-b fehlt (kumulativer Registry-Aufbau, Pkt. 6)" >&2; exit 1; }
log_stale_reg wiki run-a 1
grep -qF 'Lease-Staleness/Registrierung (TTL): wiki/run-a erzeugt bei Gen 1' wiki/log.md || { echo "HARD-FAIL (STALE-1): log.md-TTL-Eintrag fehlt (§5.12 Pkt. 6a/7a)" >&2; exit 1; }
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5): nur log.md (Registry/Lockfile ausserhalb wiki/) ---"; probe
assert_invariant "log"
echo "RESULT: PASS — STALE-1: verwaiste Lease (Lockfile+Registry-Zeile) blockiert nachfolgenden Run NICHT (generationenbasiertes TTL, AC-1); nie geloescht (AD-17e); kein Wanduhr-Timestamp (A0-20); log.md-TTL-Eintrag"
# =====================================================================
runlabel "STALE-2: VERWAIST_UEBERNEHMEN (AC-3) — neuer Run uebernimmt die verwaiste Lease gegen die erneute Merge-Base-Pruefung (§5.11 Pkt. 1 + Pkt.-4-Diskrepanz-Regel); Uebernahme protokolliert (§5.12 Pkt. 6b); verwaiste Lease nie still geloescht (AD-17e)"
isolate stale2
git checkout -q -b lease/wiki/run-a "$BASE"
akquire wiki run-a "producerA" "run-a-holder"
reg_hold_mark wiki run-a 1
# Pkt.-4-Kontext: der uebernehmende Run steht auf einem DESCENDANT der Merge-Base (echter
# Graph-Zusammenhang, kein Tautologie-Fall) — HEAD ist ein Kind-Commit von $BASE.
git commit --allow-empty -qm "run-c: Descendant der Merge-Base (Pkt.-4-Diskrepanz-Kontext)"
echo "--- neuer Run findet die verwaiste run-a-Lease vor und UEBERNIMMT sie: angestrebte Merge-Base ist $BASE; die Pkt.-4-Diskrepanz-Regel validiert die Uebernahme gegen die Aufloesung ---"
# Pkt.-4-Diskrepanz-Konstruktion (STALE-2, echter Konflikt statt Tautologie):
# (1) verwaiste Lease notiert einen von der Merge-Base ABWEICHENDEN baseline_commit (Decoy-SHA);
# (2) der uebernehmende Run steht auf einem abgeleiteten Commit (Descendant der Merge-Base);
# (3) der Laufzeit-`git merge-base` (Determinismus aus dem committeten Git-State, AD-17h) GEWINNT
# gegen den notierten Decoy-SHA — der notierte Wert bleibt Sekundaer-Fingerprint (Pkt. 4).
MB=$(git merge-base HEAD "$BASE")
DECOY_SHA=$(printf 'decoy-%s' "$BASE" | sha256sum | cut -d' ' -f1)
lock_write wiki run-a "producerC" "run-c-holder" "$DECOY_SHA"
echo " Pkt.-4-Befund: Lockfile-notierter baseline_commit = $DECOY_SHA (Decoy) ; Laufzeit-git merge-base = $MB"
echo " Regel: git merge-base GEWINNT (Commit-Boundary-Prinzip), notierter SHA bleibt Sekundaer-Fingerprint"
[ "$MB" = "$BASE" ] || { echo "HARD-FAIL (STALE-2): merge-base ableitbar gegen decoy SHAs (Pkt. 4)" >&2; exit 1; }
[ "$(lock_baseline wiki run-a)" = "$DECOY_SHA" ] || { echo "HARD-FAIL (STALE-2): baseline_commit des Lockfiles nicht als Decoy gesetzt (Pkt. 4)" >&2; exit 1; }
reg_hold_mark wiki run-a 1
log_stale_take wiki run-a "run-c-holder" "$MB"
grep -qF -- '- Lease-Uebernahme (verwaist): wiki/run-a durch run-c-holder gegen Merge-Base-Pruefung' wiki/log.md || { echo "HARD-FAIL (STALE-2): Uebernahme-Eintrag fehlt in log.md (Pkt. 6b/7b)" >&2; exit 1; }
[ "$(lock_holder wiki run-a)" = "run-c-holder" ] || { echo "HARD-FAIL (STALE-2): holder_id nicht deterministisch uebernommen (Pkt. 2, holder_id-Ableitung)" >&2; exit 1; }
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (STALE-2): Lockfile wurde bei Uebernahme still geloescht (AD-17e)" >&2; exit 1; }
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-2): Registry-Zeile run-a wurde still geloescht (AD-17e)" >&2; exit 1; }
echo "--- Probe: nur log.md; Lockfile/Registry ausserhalb wiki/ ---"; probe
assert_invariant "log"
echo "RESULT: PASS — STALE-2: verwaiste Lease uebernommen gegen erneute Merge-Base-Pruefung (Pkt. 4); Uebernahme protokolliert (Pkt. 6b); holder_id-Ableitung deterministisch (Pkt. 2); nichts still geloescht (AD-17e)"
# =====================================================================
runlabel "STALE-3: VERWAIST_STALE_MARKIEREN (AC-3) — verwaiste Lease wird als stale markiert (Registry-Marker der aktuellen Generation); Blockade aufgehoben; log.md-Eintrag (Pkt. 6c/7c)"
isolate stale3
git checkout -q -b lease/wiki/run-a "$BASE"
akquire wiki run-a "producerA" "run-a-holder"
reg_hold_mark wiki run-a 1
echo "--- neuer Run entscheidet, die verwaiste run-a-Lease als STALE zu markieren statt sie zu uebernehmen: Registry-Marker der aktuellen Generation (Pkt. 3/5), Blockade aufgehoben ---"
reg_stale_mark wiki run-a 2
if lease_active wiki run-a; then
echo "HARD-FAIL (STALE-3): stale-markierte Lease wird noch als aktiv gewertet (Blockade nicht aufgehoben, Pkt. 3)" >&2; exit 1
else
echo "BEFUND: stale-Marker wirkt — Blockade aufgehoben (lease_active = false fuer run-a)"
fi
log_stale_mark wiki run-a 2
grep -qF 'stale: run-a (Gen 2)' registry/wiki || { echo "HARD-FAIL (STALE-3): Registry-Marker fehlt (Pkt. 1/5)" >&2; exit 1; }
[ "$(reg_gen wiki)" = "2" ] || { echo "HARD-FAIL (STALE-3): Registry-Generation regrediert (Invariante Gen > erzeugend oder gleiche Gen, Pkt. 6)" >&2; exit 1; }
echo " KERN-ASSERTION (Pkt. 3): nach der Stale-Markierung gelingt DIE AKQUISE DERSELBEN id (Blockade aufgehoben, VG-1)"
if akquire wiki run-a "producerC" "run-c-holder" 1 2>/dev/null; then
echo " BEFUND: stale-markierte run-a akquiriert — Blockade-Aufhebung durchgesetzt (Pkt. 3, VERWAIST_STALE_MARKIEREN)"
else
echo "HARD-FAIL (STALE-3): stale-Markierung hebt die Lockfile-Blockade nicht auf (Pkt. 3)" >&2; exit 1
fi
grep -qF 'Lease-Stale-Markierung: wiki/run-a als stale markiert (Registry-Marker Gen 2' wiki/log.md || { echo "HARD-FAIL (STALE-3): log.md-Stale-Markierungs-Eintrag fehlt (Pkt. 6c/7c)" >&2; exit 1; }
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (STALE-3): Lockfile wurde bei Stale-Markierung still geloescht (AD-17e)" >&2; exit 1; }
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-3): Registry-Zeile run-a wurde still geloescht (AD-17e)" >&2; exit 1; }
assert_no_wallclock lease/wiki/run-a.lock registry/wiki
echo "--- Probe: nur log.md ---"; probe
assert_invariant "log"
echo "RESULT: PASS — STALE-3: verwaiste Lease als stale markiert (Registry-Marker Gen 2); Blockade aufgehoben (lease_active false); log.md-Eintrag; nie still geloescht (AD-17e); Gen-Invariante gehalten"
# =====================================================================
runlabel "STALE-4: RECOVERY_RAW_BASIS (AD-3, AD-17d/A0-15) — raw/ bleibt bei jedem Recovery-Vorgang unveraendert (SHA-256-Assertion; EC-1-Grenze: Beweis auf Sandbox-Evidenzweg beschraenkt); native git stash-Variante als zweite zulaessige Schutzform (§5.11 Pkt. 3 + §5.12 Pkt. 5; Defer aufgegriffen); Restore byte-identisch"
isolate stale4
git checkout -q -b lease/wiki/run-a "$BASE"
akquire wiki run-a "producerA" "run-a-holder"
echo "--- raw/-Referenzwert (Unveraenderlichkeits-Orakel): SHA-256 der raw/-Dateien VOR allen Recovery-Vorgaengen ---"
find raw -type f | LC_ALL=C sort | xargs -r sha256sum > raw.sha256
echo "--- Fremd-Zustand: ein nicht-laufender Producer hat wiki/alpha.md worktree-modifiziert zurueckgelassen (abgebrochener Run) — die Recovery-Basis ist raw/ (unveraendert, committete Evidenz, AD-3) und die Wiedersicherung nutzt die native git stash-Variante (Pkt. 5, Defer aufgegriffen) ---"
sed -i 's|^Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1)\.|Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — Fremdbearbeitung (raw/alpha-v1.md#S-1).|' wiki/alpha.md
FRANK_MD5=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
git stash push -q -m "stale4 fremde alpha-Bearbeitung" -- wiki/alpha.md
git stash pop -q
[ "$(sha256sum wiki/alpha.md | cut -d' ' -f1)" = "$FRANK_MD5" ] || { echo "HARD-FAIL (STALE-4): git stash-Restore nicht byte-identisch (Pkt. 5, AD-17e)" >&2; exit 1; }
log_stale_recovery "wiki/alpha.md" "git stash push/pop (native Variante, §5.12 Pkt. 5)"
find raw -type f | LC_ALL=C sort | xargs -r sha256sum > raw.sha256.after
diff -q raw.sha256 raw.sha256.after >/dev/null || { echo "HARD-FAIL (STALE-4): raw/-Dateien haben sich bei Recovery geaendert (AD-3, raw/ immutable)" >&2; exit 1; }
echo "BEFUND: raw/-SHA-256 vor/nach Recovery identisch (raw/ immutable, AD-3; Sandbox-Evidenzweg, EC-1-Grenze)"
echo "--- Recovery-Basis LESEND aus raw/ (AD-17d/A0-15): Zugriffs-/Consistency-Basis — jede abgeleitete resource-Referenz der restaurierten wiki/-Datei auf die Evidenz-Basis bleibt gegen die raw/-Dateien aufgeloest; Konformitaet gegen die Basis re-bestaetigt ---"
RSRC=$(grep -oE '\(raw/[^)#]+' wiki/alpha.md | tr -d '(' | LC_ALL=C sort -u)
[ -n "$RSRC" ] || { echo "HARD-FAIL (STALE-4): wiki/alpha.md verweist auf keine raw/-Basis-Ressource (Zugriffsanker fehlt)" >&2; exit 1; }
for res in $RSRC; do
[ -f "$res" ] || { echo "HARD-FAIL (STALE-4): abgeleitete Referenz '$res' fehlt in der raw/-Basis (Consistency-Basis, AD-3)" >&2; exit 1; }
done
echo " BEFUND: lesender Zugriff — raw/-Basis-Ressourcen aufgeloest ($RSRC), Konsistenz-/Zugriffsbasis re-bestaetigt (EC-1-Grenze)"
grep -qF 'Recovery-Basis-Nutzung: wiki/alpha.md gesichert/wiederhergestellt via git stash' wiki/log.md || { echo "HARD-FAIL (STALE-4): log.md-Recovery-Eintrag fehlt (Pkt. 6d/7d)" >&2; exit 1; }
echo "--- Probe: alpha (Fremd-Abweichung, Y-Zweig) + log (Dokumentation) — kein Ghost-Diff ---"
inv_set | LC_ALL=C paste -sd' ' -
assert_invariant "alpha log"
echo "RESULT: PASS — STALE-4: raw/ unveraendert (SHA-256-Assertion, AD-3, EC-1-Grenze); native git stash-Variante sichert+wiederhert byte-identisch (Pkt. 5, Defer aufgegriffen); nie geloescht (AD-17e); log.md-Recovery-Eintrag; Recovery-Basis LESEND aus raw/ aufgeloest (Zugriffs-/Consistency-Basis, AD-17d/A0-15)"
# =====================================================================
runlabel "STALE-5: REGISTRIERUNGS-INVARIANTE (Pkt. 6) — Gen > erzeugend oder gleiche Gen; hoechster Reg-Generator wird gehalten; verwaiste Kanten (Lockfile + Registry-Zeile) werden NIE geloescht; HOLD-gegen-HEAD deterministisch (§5.11 Pkt. 1, kein Abbruch-Text)"
isolate stale5
# Aufbau eines kontinuierlichen Registry-Verlaufs ueber mehrere Runs (kumulativ, Pkt. 6)
akquire wiki run-a "producerA" "run-a-holder"; reg_hold_mark wiki run-a 1
akquire wiki run-b "producerB" "run-b-holder"; reg_hold_mark wiki run-b 2
akquire wiki run-c "producerC" "run-c-holder"; reg_hold_mark wiki run-c 3
echo "--- Assertion Invariante (Pkt. 6): sichtbar hoechster Reg-Generator == 3 ---"
[ "$(reg_gen wiki)" = "3" ] || { echo "HARD-FAIL (STALE-5): Registrierung haelt NICHT den hoechsten Reg-Generator (Pkt. 6)" >&2; exit 1; }
# Regression versuchen: reg_write mit aelterer (niedrigerer) Gen darf die Sicht nicht absenken
reg_write wiki run-d "producerD" 1
[ "$(reg_gen wiki)" = "3" ] || { echo "HARD-FAIL (STALE-5): Registrierung regredierte auf Gen 1 (Invariante Gen > erzeugend oder gleiche Gen verletzt, Pkt. 6)" >&2; exit 1; }
echo "--- Assertion AD-17e: ALLE verwaisten Kanten (run-a/run-b/run-c Lockfiles + Registry-Zeilen) existieren nach dem Run-Zyklus unveraendert ---"
[ -f lease/wiki/run-a.lock ] && [ -f lease/wiki/run-b.lock ] && [ -f lease/wiki/run-c.lock ] || { echo "HARD-FAIL (STALE-5): verwaiste Lockfiles wurden still geloescht (AD-17e)" >&2; exit 1; }
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-5): Registry-Zeile run-a fehlt (AD-17e)" >&2; exit 1; }
grep -qF 'hold: run-b (Gen 2)' registry/wiki || { echo "HARD-FAIL (STALE-5): Registry-Zeile run-b fehlt (AD-17e)" >&2; exit 1; }
grep -qF 'hold: run-c (Gen 3)' registry/wiki || { echo "HARD-FAIL (STALE-5): Registry-Zeile run-c fehlt (AD-17e)" >&2; exit 1; }
echo "--- Assertion HOLD-gegen-HEAD deterministisch (§5.11 Pkt. 1 / §5.12 Pkt. 3): SAME-ID-A/B-Kontrast — eine AKTIVE Lease verweigert die zweite Akquise, eine als stale MARKIERTE derselben id laesst sie zu (nur stale blockiert nicht); kein Abbruch-Text, kein Wanduhr ---"
echo " A (aktiv): akquire run-c bei Reg-Gen 3 ueberschreibt die aktive (nicht stale) Lease nicht"
if akquire wiki run-c "producerD" "run-d-holder" 2>/dev/null; then
echo "HARD-FAIL (STALE-5): aktive Lease wurde ueberschrieben (HOLD gegen HEAD deterministisch, §5.11 Pkt. 1)" >&2; exit 1
else
echo " BEFUND: aktive run-c verweigert (HOLD), Lockfile unveraendert"
fi
[ "$(lock_holder wiki run-c)" = "run-c-holder" ] || { echo "HARD-FAIL (STALE-5): aktive Lease wurde beim HOLD ueberschrieben (Pkt. 1)" >&2; exit 1; }
echo " B (stale): run-c als stale markieren (Gen 4) — DANN gelingt die Akquise derselben id (Erzeugungs-Gen 3 < Reg-Gen 4)"
reg_stale_mark wiki run-c 4
if akquire wiki run-c "producerD" "run-d-holder" 3 2>/dev/null; then
echo " BEFUND: stale-markierte run-c (Gen 4) akquiriert — Blockade aufgehoben (Pkt. 3, nur stale blockiert nicht)"
else
echo "HARD-FAIL (STALE-5): stale-markierte Lease blockiert noch (aktiv-vs-stale-Kontrast fehlt, §5.12 Pkt. 3)" >&2; exit 1
fi
grep -qiE 'timestamp|wallclock|now|date:' lease/wiki/run-c.lock && { echo "HARD-FAIL (A0-20): Wanduhr im Lockfile" >&2; exit 1; } || true
grep -qiE 'timestamp|wallclock|now|20[0-9]{2}-[0-9]{2}-[0-9]{2}' registry/wiki && { echo "HARD-FAIL (A0-20): Wanduhr in Registry" >&2; exit 1; } || true
echo "--- Probe: kein wiki/-Eintrag (Registry/Lockfiles ausserhalb) ---"; probe
assert_invariant ""
echo "RESULT: PASS — STALE-5: Registrierungs-Invariante (gen 3 == hoechster, keine Regression); verwaiste Kanten nie geloescht (AD-17e); HOLD gegen aktive Lease deterministisch; kein Wanduhr-Timestamp (A0-20)"
# =====================================================================
runlabel "STALE-6: DETERMINISMUS (AD-17h/A0-19) — gleicher committeter Baum-Input -> identische Registrierungs-/Stale-Markierungs-/log.md-Outputs (Zwei-Run-Identitaet)"
# Zwei vollstaendig unabhaengige, identische Runs ueber denselben Basis-Zustand (BASE).
stale_run() { # $1 = Branchname; identischer Ablauf: 2 Leases + 1 Stale-Markierung + logs
isolate "$1"
git checkout -q -b lease/wiki/run-a "$BASE"
akquire wiki run-a "producerA" "run-a-holder"; reg_hold_mark wiki run-a 1
git checkout -q -b lease/wiki/run-b "$BASE"
akquire wiki run-b "producerA" "run-b-holder"; reg_hold_mark wiki run-b 2
reg_stale_mark wiki run-a 3
log_stale_reg wiki run-a 1
log_stale_mark wiki run-a 3
}
stale_state() { # voller Observable-Zustand zweier identischer Runs (Determinismus, Pkt. 1/6/7)
local out=""
out="$out registry=$(sha256sum registry/wiki | cut -d' ' -f1)"
out="$out log=$(sha256sum wiki/log.md | cut -d' ' -f1)"
out="$out lockfiles=$(find lease -type f | LC_ALL=C sort | xargs -r sha256sum | sha256sum | cut -d' ' -f1)"
out="$out leasebranches=$(git for-each-ref --format='%(refname)' refs/heads/lease | LC_ALL=C sort | sha256sum | cut -d' ' -f1)"
printf '%s' "$out"
}
stale_run s6a
S6A=$(stale_state)
stale_run s6b
S6B=$(stale_state)
echo "Run A: $S6A"
echo "Run B: $S6B"
[ "$S6A" = "$S6B" ] || { echo "HARD-FAIL (STALE-6): voller Observable-Zustand zweier identischer Runs weicht ab (AD-17h/A0-19, §5.12 Pkt. 1/6/7)" >&2; exit 1; }
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5, auch in STALE-6 — Header verspricht sie je lauffaehigem Run): nur log.md ---"
probe
assert_invariant "log"
grep -qF 'stale: run-a (Gen 3)' registry/wiki || { echo "HARD-FAIL (STALE-6): Stale-Markierungs-Klassifikation fehlt (Pkt. 3)" >&2; exit 1; }
assert_no_wallclock registry/wiki
echo "RESULT: PASS — STALE-6: voller Observatory-State (Registry/Lockfiles/Lease-Branches/log.md) zweier identischer Runs byte-identisch (AD-17h/A0-19, §5.12 Pkt. 1/6/7); Erhaltungs-Invariante; generationenbasiertes TTL-Ablauf-Kriterium deterministisch"
echo
echo "===== Sandbox abgeschlossen (STALE-1..STALE-6) ====="
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
@@ -0,0 +1,625 @@
#!/usr/bin/env bash
# Story 3.7 — Sandbox-Tests der Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (§5.13, Revision 3.2)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb37)
# Zweck: die Reason/Mutate-Phasen-Disziplin (§5.13) als re-executierbarer Run-Demonstrator
# durchspielen —
# CONSIST-1 KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7): der P2-Block (§5.9 Pkt. 6)
# erzeugt aus committetem Input eine konsistente Änderungsplanung (Input-Zustand,
# Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-Erhaltung) — als Variablen-Set
# captured, Determinismus (AD-17h/A0-19); Plan-Defizit ⇒ Abbruch vor Mutationsphase
# CONSIST-2 PLAN_FREEZE (AC-1, §5.13 Pkt. 2): Plan-Freeze = Veränderungs-Sperre — eine
# Mutation ausserhalb der Plan-Pfadmengen (Kandidatenliste Neu-Anlage log.md
# index.md) wird erkannt und verweigert/verhindert (Ghost-Diff-Kopplung §5.9 Pkt. 5)
# CONSIST-3 ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2, §5.13 Pkt. 3): fehlgeschlagene
# Mutation rollt auf exakte Baseline zurueck (SHA-256 byte-identisch, Post-Rollback-Diff
# leer gg. Baseline), Bundle-Zustand == Baseline; raw/ unverändert (AD-3)
# CONSIST-4 MUTATION_ABBRUCH (AC-2, §5.13 Pkt. 3/4): Abbruch nach Teilerfolg (mehrere
# geplante Mutationen, nur ein Teil ausgefuehrt) hinterlaesst keinen Teilerfolg als
# fertige Mutation (Commit-Boundary = Mutations-Boundary, AD-17f); raw/ unberührt
# CONSIST-5 VALIDATION_FAIL (AC-2, §5.13 Pkt. 5): §6-Pkt.-3-Rollback, keine weiteren
# Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante)
# CONSIST-6 VALIDATION_SUCCESS (AC-3, §5.13 Pkt. 6): Mutationen als Ganzes committet,
# Diff gg. Plan-Menge gedeckt, §6-Pkt.-4-Nachweis; kein Wanduhr-Trigger (A0-20)
# CONSIST-7 DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4, §5.13 Pkt. 7): Zwei identische
# Runs -> identische Plan-/Rollback-/State-Outputs (AD-17h/A0-19); die Phasen-Trennung
# ist logisch in einer Session — kein Prozess/Server/MCP wird gestartet (D-3, AD-11,
# grep-Negativkontrolle: keine Hintergrundprozesse/Logger-Daemons)
# Erhaltungs-Invariante (§5.9 Pkt. 5 / AD-5 / FT-6) als HARDE Assertion je lauffaehigem
# Run; Frontmatter-Konformitaet (Vertrag §3.3/§3.4-Subset) je erzeugtem/aktualisiertem Concept.
# Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum.
set -u
ROOT=$(mktemp -d /tmp/sb37-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw"
cd "$SB"
git init -q
# Determinismus vs. Host-Git-Konfiguration (AD-17h): die Sandbox erzeugt LF-Blobs und
# LF-Worktree — autocrlf/filemode-Umwandlung des Hosts wuerde sha256-Vergleiche und
# Diff-/Probe-Ergebnisse wort-wirksam verschieben. Repo-Reparatur schliesst das aus.
git config core.autocrlf false
git config core.filemode false
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit = Merge-Base) ----------
# Mini-Bundle mit zwei Root-Concepts (alpha als Mutations-Objekt, gamma als Kontrolle).
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Gamma](gamma.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
EOF
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz v1: deterministische Init-Sequenz.
### S-2
Evidenz v1: ausschließlich lokale Netze.
EOF
cat > raw/gamma-v1.md <<'EOF'
### S-1
Evidenz v1: Gamma-Thema.
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo
# ---------- Helfer: Run-Label / Isolation (kein Carry-over zwischen Szenarien) ----------
runlabel() { echo; echo "########## $1 ##########"; }
isolate() {
git checkout -qf -B "$1" "$BASE"
git reset -q --hard "$BASE"
git clean -qfd wiki raw lease registry scratch plan-run
}
# Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6): Baseline-Diff + porcelain,
# normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert).
probe() {
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß (msg stderr)
local expected="$1" p u bad=0
got=$(inv_set)
for p in $got; do
case " $expected " in
*" $p "*) ;;
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
esac
done
for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do
u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||')
case " $expected " in
*" $u "*) ;;
*) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (§5.9 Pkt. 5)" >&2; bad=1;;
esac
done
return $bad
}
assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1
if inv_viol "$1"; then
echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff"
else
exit 1
fi
}
# ---------- Zustands-Restaurations-Invariante (§5.13 Pkt. 3) — harte Assertion ----------
# Nach Abbruch/FAIL muss der Post-Rollback-Diff gg. die Baseline LEER sein (Bundle ==
# Baseline, SHA-256 byte-identisch je Pfad) — die git-ueberpruefbare Konsistenz-Eigenschaft.
assert_restored() { # $1=komma-oder-leer-getrennte wiki-Pfade, die byte-identisch zur Baseline sein müssen
local p expected=""
[ -z "$1" ] || for p in $1; do expected="$expected $p.md"; done
# 1) git-Diff gg. Baseline: keine modifizierte/geloeschte Datei
if [ -n "$(git diff --name-only "$BASE" -- wiki/)" ]; then
echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): Post-Rollback-Diff gg. Baseline NICHT leer:" >&2
git diff --name-only "$BASE" -- wiki/ | sed 's/^/ /' >&2
exit 1
fi
# 2) porcelain: keine ungetrackten/gestagten Eintraege im wiki/-Scope
if [ -n "$(git status --porcelain -- wiki/)" ]; then
echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): ungetrackte/gestagte Reste im wiki/-Scope:" >&2
git status --porcelain -- wiki/ | sed 's/^/ /' >&2
exit 1
fi
# 3) byte-identisch zur Baseline (SHA-256); Neu-Anlage-Randfall (Pfad nur im Run angelegt):
# existiert der Pfad NICHT im Baseline-Commit, darf er nach dem Rollback auch nicht in der
# Working-Copy existieren (statt eines spurious byte-Vergleichs).
for p in $expected; do
if git cat-file -e "$BASE:wiki/$p" 2>/dev/null; then
[ -f "wiki/$p" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p fehlt (muss == Baseline sein)" >&2; exit 1; }
bsha=$(git show "$BASE:wiki/$p" | sha256sum | cut -d' ' -f1)
csha=$(sha256sum "wiki/$p" | cut -d' ' -f1)
[ "$bsha" = "$csha" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p weicht von Baseline ab ($csha != $bsha)" >&2; exit 1; }
else
# Neu-Anlage-Pfad (nicht im Baseline-Commit): nach Rollback darf er NICHT existieren.
[ ! -e "wiki/$p" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p ist Neu-Anlage-Pfad und muss nach Rollback NICHT existieren" >&2; exit 1; }
fi
done
echo "RESULT: PASS — Zustands-Restaurations-Invariante: Post-Rollback-Diff gg. Baseline leer, Bundle == Baseline (SHA-256 byte-identisch)"
}
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5; Muster Sandbox-3-6) ----------
assert_frontmatter() {
local f="$1"; shift
local v
v=$(awk '
/^---$/{n++; if(n==2) exit; next}
/^[A-Za-z_][A-Za-z0-9_-]*:/{
k=$0; sub(/:.*/,"",k)
if (k=="sources") top="sources"
else if (k=="generated" || k=="verified") top="genver"
else top="other"
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
if (seenk[k]++) print "DUP_KEY:" k
r=0
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
if (r>0) lastr=r
next
}
/^[[:space:]]/{
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
ik=substr($0,1,RLENGTH-1)
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
if (top=="other") print "INNER_UNBEFUGT:" ik
}
}
' "$f")
if [ -n "$v" ]; then
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
exit 1
fi
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
for r in "$@"; do
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
done
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
}
# ---------- log.md-Eintraege (§5.13; Header-Bullet-Paar je Eintrag, datumsgruppiert) ----------
# Datumsgruppen-/Reihenfolge-Konvention (Vertrag §5, neueste zuerst): ein neuer Bullet wird
# in die BESTEHENDE Datumsgruppe eingefuegt — nach deren Header, vor etwaigen Gruppen-Bullets —
# sonst wird ein neuer Header oben erzeugt. Deterministisch (kein Wanduhr).
log_bullet() { # $1 = Bullet-Text (eine Zeile, printf-% escaped); Datumsgruppe/Reihenfolge (neueste zuerst)
local day="2026-08-20" # getrennt gesetzt — set -u: gleiche-local-Zeilen-RHS darf day nicht vor Setzung referenzieren
local header="## $day" line="$1"
if grep -qxF "$header" wiki/log.md; then
tmp=$(mktemp)
awk -v h="$header" -v l="$line" '
BEGIN{p=0}
{ if (!p && $0==h) { print; print l; p=1; next } print }
' wiki/log.md > "$tmp" && mv "$tmp" wiki/log.md
else
tmp=$(mktemp)
{ printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" && mv "$tmp" wiki/log.md
fi
}
log_plan() { # $1=plan-text (eine Zeile)
log_bullet "- Änderungsplanung (P2-Block, §5.9 Pkt. 6 / §5.13 Pkt. 1): $1 (Determinismus AD-17h/A0-19; Baseline $BASE)"
}
log_abort() { # $1=benannte Abbruch-Ursache — NFR-4-textuelle Benennung (echo). Für einen
# FAILED-Run wird log.md NICHT persistent beschrieben: §6 Pkt. 3 / §5.3 rollen auch die
# log.md-Einträge als Teilzustand zurück (Bundle nimmt Zustand vor dem Run wieder ein).
echo "NFR-4-Benennung (textuell): Mutations-Abbruch (§5.13 Pkt. 3/4) — $1; Zustands-Restaurations-Invariante (Pkt. 3), kein Teilerfolg committet (AD-17f); log.md-Einträge des Runs werden mit dem Teilzustand zurückgerollt (§6 Pkt. 3 / §5.3)"
}
log_commit() { # $1=plan-vs-ist-diff-befund
log_bullet "- Validierung SUCCESS (§5.13 Pkt. 6): Mutationen als Ganzes committet; $1 (§6-Pkt.-4-Nachweis, kein Wanduhr-Trigger A0-20)"
}
# ---------- Reason/Mutate-Helfer (§5.13) ----------
# P2-Block (§5.9 Pkt. 6) als Änderungsplanung: capturet den konsistenten Plan als textuelles
# Variablen-Set (Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-
# Erhaltung) — deterministisch aus dem committeten Git-State (AD-17h/A0-19).
p2_plan() { # $1=komma-Liste Ziel-Pfade (OKF-ohne-.md; Neu-Anlage = +name) $2=komma-Liste raw-Quellen (Pfad)
local targets="$1" srcs="$2" p s np err=0
# (1) Input-Zustand (AD-17a): Working-Copy von raw/ und wiki/ gegen HEAD — uncommittet = Abbruch
# (Scope exakt wie Instruktion §5.9 Pkt. 6 Element (1): raw/ und wiki/; Review-Loop-2-D3:
# unscoped ganzer-Tree-Prüfung wäre straffer als die Instruktion — die Demo folgt der Instruktion.)
if [ -n "$(git status --porcelain -- raw wiki)" ]; then
echo "HARD-FAIL (Änderungsplanung, P2-Block-Element (1): Input-Zustand AD-17a): uncommittete Zwischenstaende als Input — Abbruch vor der Mutation (published/committed Input erforderlich)" >&2
return 1
fi
# (2) Ziel-Pfade (Kandidatenliste, §3 Pkt. 2)
[ -n "$targets" ] || { echo "HARD-FAIL (Änderungsplanung): leere Ziel-Pfad-Menge (P2-Element (2))" >&2; return 1; }
# (3) Quellen-Existenz (EC-1)
for s in ${srcs//,/ }; do
[ -f "raw/$s" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (3): Quellen-Existenz EC-1): raw/$s existiert nicht" >&2; return 1; }
done
# (4) Betroffenheit (§3 Pkt. 2, textuell-deterministisch nach §3.2): bestehende Kandidaten
# oder geplante Neu-Anlage-Zielpfade (+Prefix; §5.13 Pkt. 2 " Neu-Anlage") — Duplikat-Kontrolle:
# ein +Pfad, der bereits existiert, ist kein Neu-Anlage.
for p in ${targets//,/ }; do
case "$p" in
+*) np="${p#+}"
[ -f "wiki/$np.md" ] && { echo "HARD-FAIL (Änderungsplanung, P2-Element (4)): Neu-Anlage-Pfad wiki/$np.md existiert bereits (Duplikat — kein Neu-Anlage, §5.10 Pkt. 8)" >&2; return 1; } ;;
*) [ -f "wiki/$p.md" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (4)): Betroffenheits-Kandidat wiki/$p.md fehlt (und kein Neu-Anlage-Zielpfad +name)" >&2; return 1; } ;;
esac
done
# (5) wiki/index.md-V-1 (Vertrag §2)
[ -f "wiki/index.md" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (5)): fehlende Bundleroot — Run-FAIL (V-1)" >&2; return 1; }
# (6) Struktur-Erhaltungs-Check (Vertrag §3.3/§3.4-Subset) je betroffenem Concept
for p in ${targets//,/ }; do
if [ -f "wiki/$p.md" ]; then
v=$(awk '
/^---$/{n++; if(n==2) exit; next}
/^[A-Za-z_][A-Za-z0-9_-]*:/{ k=$0; sub(/:.*/,"",k)
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") { print k; exit } }' "wiki/$p.md")
[ -z "$v" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (6): Struktur-Erhaltung): unbefugter Key '$v' in wiki/$p.md" >&2; return 1; }
fi
done
echo "PLAN_OK targets=($targets) sources=($srcs)"
return 0
}
# Plan-Freeze (Veränderungs-Sperre, §5.13 Pkt. 2): nach Abschluss der Planung darf kein
# Plan-Gegenstand ausserhalb der erlaubten Menge mutiert werden — eine Ziel-Pfad-Verletzung
# wird erkannt und verweigert (Ghost-Diff-Kopplung §5.9 Pkt. 5).
freeze_check() { # $1=komma-Liste der im Run geplanten Ziel-Pfade (ok + neu.log + index)
local allowed=" $1 "
local m
# Normalisierung (wie §5.9 Pkt. 5-Probe): wiki/-Praefix + .md-Suffix strippen (beide Stromarten).
for m in $( { git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u); do
case "$allowed" in
*" $m "*) ;;
*) echo "HARD-FAIL (Plan-Freeze §5.13 Pkt. 2 / Ghost-Diff §5.9 Pkt. 5): Mutation ausserhalb der erlaubten Pfad-Menge — '$m' ist nicht im Plan ($allowed)" >&2; return 1;;
esac
done
return 0
}
# Rollback der Mutationsphase (Zustands-Restaurations-Invariante, §5.13 Pkt. 3): stellt die
# Baseline wieder her — §5.3-Pkt.-3/§6-Pkt.-3-Mechanik + Ghost-Diff-Rollback §5.9 Pkt. 5.
# (Review-Loop-2-D5: der Voll-Rollback rollt den TEILZUSTAND DES RUNS komplett zurück (§6 Pkt. 3:
# "das Bundle nimmt seinen Zustand vor dem Run wieder ein") — in der Sandbox sind alle
# ungetrackten wiki/-Dateien Lauf-eigener Teilzustand; es existieren keine fremden uncommitteten
# Änderungen (AD-17e-Schutzobjekt). Ein realer Run schützt fremde Änderungen zuvor per
# Stash/Scratch-Zone (§5.11 Pkt. 3), bevor ein Rollback greift.)
rollback() {
git checkout -q "$BASE" -- wiki/ 2>/dev/null || true
untracked_rest=$(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}')
if [ -n "$untracked_rest" ]; then
echo "ROLLBACK: ungetrackte Reste im wiki/-Scope bei Rollback entdeckt:" >&2
echo "$untracked_rest" | sed 's/^/ /' >&2
# Ghost-Diff-Rollback §5.9 Pkt. 5: ungetrackte neue Dateien ausserhalb der Ziel-Pfade loeschen
for u in $untracked_rest; do
echo "ROLLBACK (Ghost-Diff-Rollback §5.9 Pkt. 5): entferne ungetrackte neue Datei '$u' ausserhalb erlaubter Menge"
rm -f "$u"
done
fi
git reset -q --hard "$BASE"
git clean -qfd wiki
}
# ---- CONSIST-1: KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7; §5.13 Pkt. 1) ----
runlabel "CONSIST-1: KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7) — der P2-Block (§5.9 Pkt. 6) erzeugt aus committetem Input eine konsistente Änderungsplanung (Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-Erhaltung); textuelle Capture als Plan; Plan-Defizit (uncommitteter Input) ⇒ Abbruch VOR der Mutationsphase"
isolate consist1
echo "--- Neue committete Evidenz als Input (AD-17a): raw/alpha-v2.md ---"
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung (Story 3.7-Plan).
EOF
git add -A && git commit -qm "Evidenz v2"
echo "--- Änderungsplanung (P2-Block als Phase): p2_plan alpha raw/alpha-v2.md ---"
PLAN1=$(p2_plan "alpha" "alpha-v2.md") || exit 1
echo "$PLAN1"
echo "--- Determinismus der Planung (AD-17h/A0-19): zweite Planung desselben committeten States (VOR jedem Run-Mutationsschritt) ---"
PLAN2=$(p2_plan "alpha" "alpha-v2.md") || exit 1
[ "$PLAN1" = "$PLAN2" ] || { echo "HARD-FAIL (CONSIST-1): Plan ungleich bei gleichem Git-State (AD-17h/A0-19)" >&2; exit 1; }
echo "PLAN2 identisch: $PLAN2"
echo "--- Plan textuell festgehalten (log.md-Teil der Planung, kein Artefakt-File) ---"
log_plan "alpha betroffen via raw/alpha-v2.md (Ziel-Pfade {alpha}, Quellen-Existenz EC-1 ok, Struktur-Erhaltung ok)"
grep -qF 'Änderungsplanung (P2-Block' wiki/log.md || { echo "HARD-FAIL (CONSIST-1): Plan-Nachweis fehlt in log.md (§5.13 Pkt. 1)" >&2; exit 1; }
echo "--- Plan-Defizit-Kette (I/O-Matrix PLAN_BEABSICHTIGT): uncommitteter Input ⇒ Abbruch VOR der Mutation ---"
echo "uncommitteter Rest im raw/" > raw/scratch-deficit.txt
if p2_plan "alpha" "alpha-v2.md" >/dev/null 2>&1; then
echo "HARD-FAIL (CONSIST-1): uncommitteter Input wurde nicht als Plan-Defizit abgefangen (P2-Element (1), AD-17a)" >&2
exit 1
else
echo "BEFUND: Plan-Defizit textuell benannt (NFR-4) — Abbruch vor der Mutationsphase, keine Mutation"
fi
rm -f raw/scratch-deficit.txt
echo "--- Plan-Defizit-Kette (I/O-Matrix PLAN_BEABSICHTIGT): fehlende Quellen-Existenz (P2-Element (3), EC-1) ⇒ Abbruch VOR der Mutation ---"
# (Review-Loop-2-Patch: die Löschung wird COMMITTET — eine uncommittierte Löschung würde
# Element (1) (uncommitteter Input) zuerst feuern und die EC-1-Kontrolle nicht isolieren;
# im committeten Zustand ist Element (3) der einzige Trigger.)
git rm -q raw/alpha-v2.md && git commit -qm "Quelle entfernt"
if p2_plan "alpha" "alpha-v2.md" >/dev/null 2>&1; then
echo "HARD-FAIL (CONSIST-1): fehlende Quelle wurde nicht als Plan-Defizit abgefangen (P2-Element (3), EC-1)" >&2
exit 1
else
echo "BEFUND: Plan-Defizit (EC-1: raw/alpha-v2.md fehlt) textuell benannt (NFR-4) — Abbruch vor der Mutationsphase, keine Mutation"
fi
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5): nur log.md ---"; probe
assert_invariant "log"
echo "RESULT: PASS — CONSIST-1: konsistente Änderungsplanung aus committetem Input (P2-Block, §5.9 Pkt. 6 / §5.13 Pkt. 1); textuelle Capture; Determinismus (AD-17h/A0-19); Plan-Defizit ⇒ Abbruch vor der Mutation (PLAN_BEABSICHTIGT; P2-Element (1) AD-17a isoliert [untracked Datei] + P2-Element (3) EC-1 isoliert [committete Löschung]); Erhaltungs-Invariante"
# ---- CONSIST-2: PLAN_FREEZE (AC-1; §5.13 Pkt. 2) ----
runlabel "CONSIST-2: PLAN_FREEZE (§5.13 Pkt. 2) — Plan-Freeze = Veränderungs-Sperre: eine Mutation ausserhalb der Plan-Pfadmengen (Kandidatenliste Neu-Anlage log.md index.md) wird erkannt und verhindert (Ghost-Diff-Kopplung §5.9 Pkt. 5); Misch-Run (Update + Neu-Anlage): der geplante Neu-Anlage-Pfad ist erlaubter Teil der Plan-Menge (Review-Loop-2-Patch)"
isolate consist2
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
### S-4
Evidenz v2: Delta-Thema — bisher nicht im Bundle repraesentiert.
EOF
git add -A && git commit -qm "Evidenz v2"
p2_plan "alpha,+delta" "alpha-v2.md" >/dev/null || exit 1
log_plan "alpha betroffen + delta neu angelegt via raw/alpha-v2.md (Plan-Freeze aktiv, Misch-Run)"
echo "--- Mutationsphase: geplante Mutation von alpha — erlaubt (Plan-Ziel-Pfad) ---"
cat >> wiki/alpha.md <<'EOF'
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
grep -q "synchrone Kopplung" wiki/alpha.md || { echo "HARD-FAIL (CONSIST-2): geplante Mutation fehlgeschlagen" >&2; exit 1; }
echo "--- Mutationsphase: geplante Neu-Anlage von delta — erlaubt (Neu-Anlage im Plan, §5.13 Pkt. 2) ---"
cat > wiki/delta.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v2.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-20T00:00:00Z
---
Delta-Thema (raw/alpha-v2.md#S-4).
EOF
[ -f wiki/delta.md ] || { echo "HARD-FAIL (CONSIST-2): geplante Neu-Anlage fehlgeschlagen" >&2; exit 1; }
echo "--- Freeze-Verletzung simulieren: Ghost-Mutation auf NICHT geplanten Pfad gamma (Plan-Gegenstand ausserhalb der erlaubten Menge) ---"
echo "unbefugte gamma-Aenderung" >> wiki/gamma.md
if freeze_check "alpha delta log index"; then
echo "HARD-FAIL (CONSIST-2): Mutation ausserhalb der Plan-Pfadmengen wurde nicht erkannt (Plan-Freeze, §5.13 Pkt. 2)" >&2
exit 1
else
echo "BEFUND: Freeze-Verletzung erkannt (ghost gamma) — Veränderungs-Sperre greift"
fi
echo "--- Ghost-Diff-Rollback (§5.9 Pkt. 5): gamma aus Baseline-Zustand wiederherstellen, textuell benannt (NFR-4) ---"
git checkout -q "$BASE" -- wiki/gamma.md
echo "NFR-4-Nennung: Ghost-Diff auf 'gamma' (ausserhalb Plan-Menge) zurueckgerollt — textuell benannt"
freeze_check "alpha delta log index" || exit 1
echo "--- Voll-Rollback (Abbruch des Runs): Teilzustand {alpha, delta, log} wird komplett zurückgerollt (Zustands-Restaurations-Invariante) ---"
rollback
assert_restored "alpha gamma index log delta"
echo " BEFUND: Neu-Anlage-Pfad delta existiert nach Rollback NICHT (assert_restored-Neu-Anlage-Guard) — Misch-Run-Teilerfolg ist vollständig rückgerollt"
echo "RESULT: PASS — CONSIST-2: Plan-Freeze = Veränderungs-Sperre (erlaubte Menge {alpha, delta (Neu-Anlage), log, index}); Verletzung (gamma) erkannt + zurueckgerollt (Ghost-Diff-Rollback §5.9 Pkt. 5); Neu-Anlage-Pfad im Plan ist erlaubter Teil der Menge; Misch-Run-Voll-Rollback: Neu-Anlage nach Rollback nicht existent (Zustands-Restaurations-Invariante); Erhaltungs-Invariante"
# ---- CONSIST-3: ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2; §5.13 Pkt. 3) ----
runlabel "CONSIST-3: ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2) — fehlgeschlagene Mutation rollt auf exakte Baseline zurueck (Post-Rollback-Diff leer, SHA-256 byte-identisch); Bundle == Baseline; raw/ unverändert (AD-3)"
isolate consist3
raw_sha_before=$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
echo "--- Planung (konsistent) + Mutationsphase: Teilzustand wird geschrieben (alpha erweitert), dann bricht die Mutation AB (Fehler) ---"
p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1
log_plan "alpha betroffen via raw/alpha-v2.md"
cat >> wiki/alpha.md <<'EOF'
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
echo " (Teilzustand geschrieben: alpha erweitert)"
echo "--- Mutations-Abbruch: Rollback (Zustands-Restaurations-Invariante §5.13 Pkt. 3; §5.3-Pkt.-3-/§6-Pkt.-3-Rollback, Ghost-Diff-Rollback §5.9 Pkt. 5) ---"
rollback
log_abort "Fehler waehrend der Mutationsphase (alpha-Teilzustand)"
echo "--- KERN-ASSERTION: Post-Rollback-Diff leer, Bundle == Baseline (SHA-256 je Pfad) ---"
assert_restored "alpha gamma index log"
echo "--- raw/ unverändert (AD-3) ---"
[ "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" = "$raw_sha_before" ] || { echo "HARD-FAIL (CONSIST-3): raw/ verändert (AD-3)" >&2; exit 1; }
[ -f raw/alpha-v1.md ] || { echo "HARD-FAIL (CONSIST-3): raw/alpha-v1.md fehlt (AD-3)" >&2; exit 1; }
echo "RESULT: PASS — CONSIST-3: fehlgeschlagene Mutation rollt auf exakte Baseline zurueck (Post-Rollback-Diff leer, SHA-256 byte-identisch, Bundle == Baseline); raw/ unverändert (AD-3); Zustands-Restaurations-Invariante erzwungen"
# ---- CONSIST-4: MUTATION_ABBRUCH (AC-2; §5.13 Pkt. 3/4) ----
runlabel "CONSIST-4: MUTATION_ABBRUCH (AC-2) — Abbruch nach Teilerfolg (mehrere geplante Mutationen, nur ein Teil ausgefuehrt) hinterlaesst KEINEN Teilerfolg als fertige Mutation (Commit-Boundary = Mutations-Boundary, AD-17f); raw/ unberührt"
isolate consist4
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
cat > raw/gamma-v2.md <<'EOF'
### S-1
Evidenz v2: Gamma schaerft seine Abgrenzung.
EOF
git add -A && git commit -qm "Evidenz v2"
echo "--- Plan: ZWEI Ziel-Pfade (alpha, gamma) — Mutationsphase fuehrt nur alpha aus, dann Abbruch ---"
p2_plan "alpha,gamma" "alpha-v2.md,gamma-v2.md" >/dev/null || exit 1
log_plan "alpha,gamma betroffen (zwei geplante Mutationen)"
PRE_MUT_HEAD=$(git rev-parse HEAD)
echo " (HEAD vor der Mutationsphase = $PRE_MUT_HEAD; Evidenz-Commit ist Input, kein Run-Mutations-Commit)"
cat >> wiki/alpha.md <<'EOF'
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
echo " (alpha mutiert; gamma geplant aber NOCH NICHT ausgefuehrt — Abbruch)"
echo "--- Kein Teilerfolg darf als fertige Mutation veröffentlicht werden (AD-17f): nicht committen, zurueckrollen ---"
if [ "$(git log --oneline -1 --format='%H')" != "$PRE_MUT_HEAD" ]; then
echo "HARD-FAIL (CONSIST-4): es wurde ein Commit während des Abbruchs erzeugt (Commit-Boundary verletzt, AD-17f)" >&2
exit 1
else
echo " BEFUND: kein Commit während der Teilfolge (HEAD == $PRE_MUT_HEAD; Commit-Boundary = Mutations-Boundary gewahrt)"
fi
rollback
log_abort "Abbruch nach Teilerfolg (alpha ausgefuehrt, gamma nicht) — kein Teilerfolg committet"
echo "--- KERN-ASSERTION: Post-Rollback-Diff leer, Bundle == Baseline (kein Teilerfolg sichtbar) ---"
assert_restored "alpha gamma index log"
echo "--- raw/ unberührt (AD-3): Baseline-raw-Dateien byte-identisch nach Rollback ---"
# Setup-Existenz-Assert: die Baselines müssen im Baseline-Commit existieren, sonst wäre der
# SHA-Vergleich leerer-Input (der stille `|| sha256sum...`-Fallback wäre toter Pfad — kein pipefail).
git cat-file -e "$BASE:raw/alpha-v1.md" || { echo "HARD-FAIL (Setup): raw/alpha-v1.md fehlt im Baseline-Commit" >&2; exit 1; }
git cat-file -e "$BASE:raw/gamma-v1.md" || { echo "HARD-FAIL (Setup): raw/gamma-v1.md fehlt im Baseline-Commit" >&2; exit 1; }
rawv1_a=$(git show "$BASE:raw/alpha-v1.md" | sha256sum | cut -d' ' -f1)
rawv1_g=$(git show "$BASE:raw/gamma-v1.md" | sha256sum | cut -d' ' -f1)
[ "$rawv1_a" = "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-4): raw/alpha-v1.md verändert (AD-3)" >&2; exit 1; }
[ "$rawv1_g" = "$(sha256sum raw/gamma-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-4): raw/gamma-v1.md verändert (AD-3)" >&2; exit 1; }
echo " BEFUND: raw/alpha-v1.md + raw/gamma-v1.md == Baseline-SHA (der Run hat raw/ nicht berührt; die Evidenz-Input-Commit-V2-Dateien sind Input, kein Baseline-/AD-3-Gegenstand)"
echo "RESULT: PASS — CONSIST-4: MUTATION_ABBRUCH nach Teilerfolg — kein Teilerfolg als fertige Mutation veröffentlicht (Commit-Boundary = Mutations-Boundary, AD-17f); Rollback stellt Baseline wieder her; Zustands-Restaurations-Invariante; raw/ unberührt (AD-3)"
# ---- CONSIST-5: VALIDATION_FAIL (AC-2; §5.13 Pkt. 5) ----
runlabel "CONSIST-5: VALIDATION_FAIL (AC-2) — §6-Pkt.-3-Rollback bei Validierungs-FAIL: keine weiteren Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante); raw/ unverändert (AD-3)"
isolate consist5
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1
log_plan "alpha betroffen via raw/alpha-v2.md"
echo "--- Mutationsphase: alpha wird mutiert — aber mit Frontmatter-Verstoß (unbefugter Key foo:) ---"
cat >> wiki/alpha.md <<'EOF'
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
printf '\nfoo: bar\n' >> wiki/alpha.md
echo "--- Validierungsphase (§6): FAIL (Struktur-Verstoß, unbefugter Key) -> §6-Pkt.-3-Rollback, KEINE weiteren Mutationen ---"
if grep -q '^foo:' wiki/alpha.md; then
echo " Validator-BEFUND: FAIL (unbefugter Key 'foo:', Punkt 6-Struktur-Verstoß) — textuell benannt (NFR-4)"
else
echo "HARD-FAIL (CONSIST-5): Validator-Verstoß nicht erkennbar gemacht" >&2; exit 1
fi
rollback
log_abort "Validierungs-FAIL (unbefugter Key foo: in alpha) — §6-Pkt.-3-Rollback, keine weiteren Mutationen"
echo "--- KERN-ASSERTION: Endzustand konsistent (Baseline wiederhergestellt) ---"
assert_restored "alpha gamma index log"
# (Review-Loop-2-Patch: gg. den Baseline-Commit vergleichen — der Selbstvergleich wäre
# tautologisch und bewiese nichts; Muster CONSIST-4.)
rawv1_b=$(git show "$BASE:raw/alpha-v1.md" | sha256sum | cut -d' ' -f1)
[ "$rawv1_b" = "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-5): raw/ verändert (AD-3)" >&2; exit 1; }
echo "RESULT: PASS — CONSIST-5: VALIDATION_FAIL — §6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante); raw/ unverändert (AD-3)"
# ---- CONSIST-6: VALIDATION_SUCCESS (AC-3; §5.13 Pkt. 6) ----
runlabel "CONSIST-6: VALIDATION_SUCCESS (AC-3) — Mutationen als Ganzes committet (erst nach Diff-Selbsttest ohne Ghost-Diff, §5.9 Pkt. 5); Diff gg. Plan-Menge gedeckt; §6-Pkt.-4-Nachweis; kein Wanduhr-Trigger (A0-20)"
isolate consist6
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1
log_plan "alpha betroffen via raw/alpha-v2.md"
echo "--- Mutationsphase: geplante Mutation von alpha (regelkonform) — §5.9-Pkt.-2-Update-Form: Body-Erweiterung + sources-Zuwachs + generated.at-Bump ---"
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
- resource: raw/alpha-v2.md
id: s3
generated:
by: wow-compiler/0.1.0
at: 2026-08-20T00:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
echo "--- Diff-Selbsttest VOR dem Commit (§5.9 Pkt. 5): {alpha, log} — kein Ghost-Diff ---"; probe
assert_invariant "alpha log"
echo "--- Plan-Freeze-Check (§5.13 Pkt. 2): Diff gg. Plan-Menge gedeckt ---"
freeze_check "alpha log index" || exit 1
echo "--- Validierungsphase (§6): SUCCESS -> §6-Pkt.-4-Nachweis und SUCCESS-Bullet VOR dem Commit (§5.13 Pkt. 6) ---"
echo "--- §6-Pkt.-4-Nachweis: Verdikt je Datei SUCCESS (frontmatter-Konformität) ---"
assert_frontmatter wiki/alpha.md "raw/alpha-v1.md" "raw/alpha-v2.md"
echo " (§5.13-demo: der committete Zustand ist gültig — alpha frontmatter-konform (kein unbefugter Key, generated.at volles Datetime), sources-Nachführung der Update-Evidenz vollzogen (§5.9 Pkt. 2); der Body zitiert raw/alpha-v2.md#S-3 nur mit existierendem sources-Eintrag (EC-1/Punkt-3-konform))"
log_commit "Diff gg. Plan-Menge gedeckt ({alpha, log}), kein Ghost-Diff"
echo "--- Mutationsphase zu Ende: Mutationen als Ganzes committen (§5.13 Pkt. 6, Commit-Boundary AD-17f) — Commit enthält alpha + log.md mit Plan- und SUCCESS-Bullets ---"
git add -A && git commit -qm "Run: alpha-Update (Story 3.7 CONSIST-6)"
grep -qF 'Änderungsplanung (P2-Block' wiki/log.md || { echo "HARD-FAIL (CONSIST-6): Plan-Nachweis fehlt (Pkt. 1)" >&2; exit 1; }
grep -qF 'Validierung SUCCESS' wiki/log.md || { echo "HARD-FAIL (CONSIST-6): §6-Pkt.-4-Nachweis fehlt (Pkt. 6)" >&2; exit 1; }
echo "--- kein Wanduhr-Trigger (A0-20): der Commit folgt aus Phasenabschluss, nicht aus Kalenderzeit ---"
echo "RESULT: PASS — CONSIST-6: VALIDATION_SUCCESS — Mutationen als Ganzes committet (nach Diff-Selbsttest ohne Ghost-Diff); Diff gg. Plan-Menge gedeckt; §6-Pkt.-4-Nachweis; Aktivierungs-Befund zu Wanduhr-Trigger siehe CONSIST-7"
# ---- CONSIST-7: DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4; §5.13 Pkt. 7) ----
runlabel "CONSIST-7: DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4, AD-17h/A0-19, D-3/AD-11) — zwei identische Runs -> identische Plan-/Rollback-/State-Outputs; die Phasen-Trennung ist logisch in einer Session — keine Hintergrundprozesse/Server/MCP (grep-Negativkontrolle)"
echo "--- KEINE_ENGINE-Negativkontrolle: der Run startet keinen Prozess/Server/MCP (nur Shell/Git/Datei, D-3/AD-11) ---"
# Robustheits-Guard: pgrep bevorzugt, sonst ps-basierte Negativkontrolle (portabel, nicht-vakuum).
if command -v pgrep >/dev/null 2>&1; then
engine_proc=$(pgrep -f "wow-flow-engine|wow-server|wow-mcp" 2>/dev/null || true)
else
command -v ps >/dev/null 2>&1 || { echo "HARD-FAIL (KEINE_EIGENE_ENGINE): weder pgrep noch ps verfügbar — Negativkontrolle kann nicht greifen" >&2; exit 1; }
engine_proc=$(ps aux 2>/dev/null | grep -E "wow-flow-engine|wow-server|wow-mcp" | grep -v "grep" || true)
fi
if [ -n "$engine_proc" ]; then
echo "HARD-FAIL (KEINE_EIGENE_ENGINE §5.13 Pkt. 7 / D-3 / AD-11): ein Workflow-Engine-Prozess wurde gestartet" >&2
echo "$engine_proc" | sed 's/^/ /' >&2
exit 1
else
echo " BEFUND: keine Workflow-Engine-Prozesse (wow-flow-engine/wow-server/wow-mcp) — keine eigene Engine (AC-4)"
fi
consist7_run() { # deterministischer Run auf isoliertem Zustand (identische Plan-/Rollback-/State-Outputs)
local branch="$1"
isolate "$branch"
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
local plan cap_commit
plan=$(p2_plan "alpha" "alpha-v2.md") || return 99
# Plan-Capture-Disclaimer (BH-13): plan-run.capture ist NUR ein deterministischer Sandbox-Zeuge
# für den Zwei-Run-Vergleich — NICHT Teil der §5.13-Ausführungsdisziplin (die Instruktion
# verlangt laut Ask-First kein Plan-Artefakt-File; „kein Artefakt-File" bleibt die Norm).
echo "$plan" > plan-run.capture
git add -A && git commit -qm "Plan-Capture"
cap_commit=$(git rev-parse HEAD)
# Rollback-Pfad: Teilzustand schreiben + Abbruch + Rollback (Zustands-Restaurations-Invariante)
cat >> wiki/alpha.md <<'EOF'
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
rollback >/dev/null
# Plan-Capture aus dem committeten Baum lesen: rollback () resettet auf BASE und entfernt so
# die Arbeitskopie von plan-run.capture (nur im Plan-Capture-Commit getrackt) — der committete
# Baum bleibt als deterministischer Zeuge erhalten (AD-17h/A0-19).
plan_state=$(git show "$cap_commit:plan-run.capture")
plan_sha=$(git show "$cap_commit:plan-run.capture" | sha256sum | cut -d' ' -f1)
# log_sha nach Rollback ist definitionsgemäß die Baseline-Konstante (leerer Indikator — die
# Baseline-log.md wird gegen sich selbst gemessen); Determinismus-Zeuge ist plan_sha/plan_state
# aus dem committeten Plan-Capture (non-vacuous).
echo "state plan=[$plan_state] plan_sha=$plan_sha"
}
echo "--- Zwei identische Runs (gleicher Baum-Input) ---"
S7A=$(consist7_run s7a)
echo "Run A: $S7A"
S7B=$(consist7_run s7b)
echo "Run B: $S7B"
[ "$S7A" = "$S7B" ] || { echo "HARD-FAIL (CONSIST-7): Plan-/State-Outputs zweier identischer Runs weichen ab (AD-17h/A0-19, §5.13 Pkt. 1/7)" >&2; exit 1; }
echo "--- Zustands-Restaurations-Invariante im Zwei-Run-Pfad (Rollback >> Baseline leer) ---"
isolate s7c
[ "$(inv_set)" = "" ] || { echo "HARD-FAIL (CONSIST-7): kein sauberer Zustand nach Rollback erwartet (leer)" >&2; exit 1; }
echo "RESULT: PASS — CONSIST-7: Zwei identische Runs -> identische Plan-/Rollback-/State-Outputs (AD-17h/A0-19); keine Workflow-Engine gestartet (D-3/AD-11, KEINE_EIGENE_ENGINE §5.13 Pkt. 7, AC-4)"
echo
echo "===== Sandbox abgeschlossen (CONSIST-1..CONSIST-7) ====="
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
@@ -0,0 +1,725 @@
#!/usr/bin/env bash
# Story 3.8 — Sandbox-Tests des Determinismus-Vertrags (AD-17h/FT-10/A0-19) als
# Agent-Instruktions-Validator (§5.14, Revision 3.3)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb38)
# Zweck: die deterministische Bestätigungs-Mechanik (§5.14) als re-executierbarer
# Run-Demonstrator durchspielen —
# DET-1 BUNDLE_STATE_DEFINITION: ein Mini-Bundle erzeugen und die Bundle-State-
# Projektion deterministisch definieren/extrahieren (wiki/-Dateien + Plan-/
# Kandidaten-/Reihenfolge-Outputs; Extraktion zweimal -> identisch)
# DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum, zwei getrennte Worktrees): zwei frische
# Agent-Kontexte in getrennt aufgebauten, sauberen Worktrees über demselben
# committeten Baum -> identische Bundle-States bis auf at (Q-6/A0-19: eine zweite
# Ausführung in derselben Session genügt nicht); kanonisches Eingabemanifest +
# Run-Receipt (außerhalb des Bundles); keine hart codierten erwarteten Pläne/
# Concept-Bodies, kein pauschales verified-Maskieren; echte Content-Hashes
# (Witness weicht byte-weise von der Baseline ab — kein Vakuum)
# DET-3 ZWEI_RUN_ABWEICHUNG: künstlich divergenter Lauf -> als AD-16-Klassifikations-
# defekt klassifiziert und textuell benannt (kein Rauschen); korrigierter zweiter
# Lauf == erster Lauf (erneut bestätigt)
# DET-4 AT_GAP_AUSNAHME: generated.at-Wanduhr-Gap ist die einzige benannte Ausnahme
# (diff zwischen zwei Runs ausschließlich auf der at-Zeile; nach at-Maskierung
# byte-identisch); jede andere Differenz liegt ausserhalb der Ausnahme =
# AD-16-Klassifikationsdefekt
# DET-5 EM_DASH: Em-Dash — kollabiert auf dieselbe canonische Form wie En-Dash/
# Bindestrich/Unterstrich/Leerzeichen (§3.2 Pkt. 1b, [-–— _] -> -)
# DET-6 KOLLAPS: Läufe (a--b), führende (-x), trailende (x-) -> Kollaps auf
# genau ein - + Trim (Reichweite: jedes Vorkommen -> genau ein -)
# DET-7 MATCH_SCOPE: Stufe a matcht ganze Wörter über den Body, exklusive YAML-
# Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen)
# DET-8 ORPHAN: neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer -> unzugeordnet,
# log.md-verwaist protokolliert (Datumsgruppe, <Baseline-Commit>), kein Banner,
# keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default)
# Determinismus-Vertrag (AD-17h/A0-19) als HARDE Assertion je Szenario; KEINE_EIGENE_ENGINE-
# Negativkontrolle (D-3/AD-11) in DET-1; Frontmatter-/log.md-Konformitaet (Vertrag §3.3/§3.4, §5).
# Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum.
set -u
ROOT=$(mktemp -d /tmp/sb38-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw"
cd "$SB"
git init -q
# Determinismus vs. Host-Git-Konfiguration (AD-17h): LF-Blobs + LF-Worktree —
# autocrlf/filemode-Umwandlung des Hosts wuerde sha256-Vergleiche verschieben.
git config core.autocrlf false
git config core.filemode false
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit) ----------
# Mini-Bundle mit zwei Root-Concepts (alpha als Mutations-Objekt, gamma als Kontrolle).
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Gamma](gamma.md)
EOF
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
EOF
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz v1: deterministische Init-Sequenz.
### S-2
Evidenz v1: ausschließlich lokale Netze.
EOF
cat > raw/gamma-v1.md <<'EOF'
### S-1
Evidenz v1: Gamma-Thema.
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo
# ---------- Helfer: Run-Label / Isolation (kein Carry-over zwischen Szenarien) ----------
runlabel() { echo; echo "########## $1 ##########"; }
isolate() {
git checkout -qf -B "$1" "$BASE"
git reset -q --hard "$BASE"
git clean -qfd wiki raw lease registry scratch plan-run
}
# ---------- Deterministische Normalisierung (§3.2 Pkt. 1b, Story 3.8) ----------
# lowercasing + Binde-Varianten-Kollaps [-–— _] -> - (En-Dash , Em-Dash —,
# Bindestrich -, Unterstrich _, Leerzeichen), Kollaps-Reichweite: jedes Vorkommen ->
# genau ein -; Läufe kollabieren; führende/trailende Separatoren werden getrimmt.
norm() { # $1 = Begriff (Kollaps-Form, NICHT registry-aufloesend — Kollaps-Scope isoliert)
printf '%s' "$1" | sed \
-e 's|[–—]|-|g' \
-e 's|_| |g' \
-e 's| |-|g' \
-e 's|--*|-|g' \
-e 's|^-*||' \
-e 's|-*$||' \
| tr 'A-Z' 'a-z'
}
# Stufe-a-Match-Scope (§3.2 Pkt. 2a, Story 3.8): matcht ganze Wörter über den Body,
# exklusive YAML-Frontmatter. Implementierung: Frontmatter (zwischen ersten beiden ---)
# wird entfernt, danach Wortgrenzen-Match (rg mit Wortgrenzen bzw. grep '\b').
match_stufe_a() { # $1=Begriff $2=Datei-Liste ; liefert getroffene wiki-Pfade (ohne Präfix/Suffix)
local term="$1" file
# Symmetrische Normalisierung (Review-Loop-3, P-4; §3.2 Pkt. 2a): der Term wird über
# norm() (Lowercasing + Kollaps-Klasse [-–— _] -> -, §3.2 Pkt. 1b) normalisiert und der
# Body entsprechend lowercased — der Match läuft auf den beidseitig normalisierten Formen
# (kein host-abhängiges -i-Flag; Groß-/Kleinschreibung deterministisch über beide Seiten).
local term_lc
term_lc=$(norm "$term")
# Regex-Metazeichen im Begriff literal-escaped (deterministischer, literal-sicherer Vergleich,
# §3.2-Pkt.-2a/§5.14 Pkt. 5): ein Term wie "a+b" oder "x.y" darf nie als Muster umgedeutet werden.
local esc
esc=$(printf '%s' "$term_lc" | sed 's|[][\\.*^$+?(){}|]|\\&|g')
for file in "${@:2}"; do
# Body = alles nach dem zweiten "---" (Frontmatter exkludiert, deterministisch);
# CRLF-tolerant (Host-Checkout-Form normalisiert), sonst wäre der Wortgrenzen-Match unbestimmt.
# Der Body wird wie der Term lowercased (Symmetrie); die Kollaps-Äquivalenz des Em-Dash
# ist damit im Match-Pfad real ausgeübt (norm-then-match, Review-Loop-3, P-4).
body=$(tr -d '\r' < "$file" | tr 'A-Z' 'a-z' | awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}')
# Wortgrenzen-Match: ganze Wörter — Substring-/Frontmatter-Treffer liefern nichts.
if printf '%s' "$body" | grep -Eq "(^|[^A-Za-z0-9])${esc}([^A-Za-z0-9]|$)"; then
basename "$file" .md
fi
done
}
# log.md-Eintrag (datumsgruppiert, neueste zuerst; Vertrag §5). Deterministisch fester Tag.
# Fehler in mktemp/mv werden NICHT geschluckt: ein fehlgeschlagener Eintrag würde den
# log.md-Hash beider Läufe identisch (Baseline) lassen => Vakuum-Gleichheit (sha_log).
log_bullet() { # $1 = Bullet-Text (eine Zeile)
local day="2026-08-20"
local header="## $day" line="$1"
local tmp
if grep -qxF "$header" wiki/log.md; then
tmp=$(mktemp) || { echo "HARD-FAIL (log_bullet): mktemp fehlgeschlagen" >&2; exit 1; }
awk -v h="$header" -v l="$line" '
BEGIN{p=0}
{ if (!p && $0==h) { print; print l; p=1; next } print }
' wiki/log.md > "$tmp" && mv "$tmp" wiki/log.md || { echo "HARD-FAIL (log_bullet): log.md-Update fehlgeschlagen (Eintrag verloren)" >&2; exit 1; }
else
tmp=$(mktemp) || { echo "HARD-FAIL (log_bullet): mktemp fehlgeschlagen" >&2; exit 1; }
{ printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" \
&& mv "$tmp" wiki/log.md \
|| { echo "HARD-FAIL (log_bullet): log.md-Update fehlgeschlagen (Eintrag verloren)" >&2; exit 1; }
fi
}
# =====================================================================================
# DET-1 BUNDLE_STATE_DEFINITION (§5.14 Pkt. 1)
# =====================================================================================
runlabel "DET-1: BUNDLE_STATE_DEFINITION — Mini-Bundle erzeugen; Bundle-State-Projektion (wiki/-Dateien + Plan-/Kandidaten-/Reihenfolge-Outputs) deterministisch definieren/extrahieren; KEINE_EIGENE_ENGINE (D-3/AD-11)"
if command -v pgrep >/dev/null 2>&1; then
engine_proc=$(pgrep -f "wow-flow-engine|wow-server|wow-mcp" 2>/dev/null || true)
else
command -v ps >/dev/null 2>&1 || { echo "HARD-FAIL (KEINE_EIGENE_ENGINE): weder pgrep noch ps verfügbar" >&2; exit 1; }
engine_proc=$(ps aux 2>/dev/null | grep -E "wow-flow-engine|wow-server|wow-mcp" | grep -v "grep" || true)
fi
if [ -n "$engine_proc" ]; then
echo "HARD-FAIL (KEINE_EIGENE_ENGINE D-3/AD-11, §5.14): ein Workflow-Engine-Prozess wurde gestartet" >&2
echo "$engine_proc" | sed 's/^/ /' >&2
exit 1
else
echo " BEFUND: keine Workflow-Engine-Prozesse (wow-flow-engine/wow-server/wow-mcp) — der Agent-Instruktions-Validator ist die Instruktion selbst (kein Prozess/Server/MCP)"
fi
isolate det1
# Der Run erzeugt einen definierten Bundle-State: neue committete Evidenz (Input) ->
# Plan-/Kandidaten-Output -> Mutations-Commit (deterministischer Run-Ausgangszustand).
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
plan=$(printf 'cand=alpha;form=update;baseline=%s' "$BASE")
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
- resource: raw/alpha-v2.md
id: s3
generated:
by: wow-compiler/0.1.0
at: 2026-08-20T00:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
log_bullet "- Determinismus-Bestätigung (Bundle-State-Definition, §5.14 Pkt. 1): $plan"
git add -A && git commit -qm "Run: alpha-Update (DET-1)"
# Bundle-State-Extraktion (§5.14 Pkt. 1): (a) committeter Baum gg. Baseline,
# (b) Kandidatenliste in Zuwachs-Sicht-Ordnung, (c) Ausführungs-Entscheidung (Form-Wahl).
# Deterministische Projektion = (a)+(b)+(c) — zweimal extrahiert, byte-identisch.
# Dev-Demo-Notiz: die (b)-/(c)-Kandidaten-/Form-Outputs sind für die Wiederholungs-Probe
# eigentliche Run-Feststellungen (hier redaktionell verdichtet; die Tendenz "die Bundle-State
# wiederholt extrahieren ist deterministisch" wird von der zweimaligen Wiederholung bewiesen,
# die Ausführungs-Verdikte selbst belegen die DET-Szenarien DET-3/DET-4/DET-7).
bs_a() {
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
# Kandidatenliste (Stufe a, deterministisch; Zuwachs-Ordnung) — hier textuell festgehalten:
match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md
# Form-Wahl (Ausführungs-Entscheidung): alpha ist betroffen, gamma nicht
echo "form=update"
echo "baseline=$BASE"
}
B1=$(bs_a)
B2=$(bs_a)
[ "$B1" = "$B2" ] || { echo "HARD-FAIL (DET-1): Bundle-State-Projektion nicht deterministisch (gleiche Extraktion zweimal ungleich)" >&2; exit 1; }
case "$B1" in
*alpha*) ;;
*) echo "HARD-FAIL (DET-1): Bundle-State-Projektion enthaelt Wiki-Pfad-Projektion nicht (alpha fehlt)" >&2; exit 1;;
esac
echo " Bundle-State-Projektion (deterministisch, wiederextrahierbar):"
echo "$B1" | sed 's/^/ /'
echo "RESULT: PASS — DET-1: BUNDLE_STATE_DEFINITION — Bundle-State = deterministische Projektion des committeten Git-States ((a) Baum gg. Baseline, (b) Kandidaten-, (c) Plan-/Reihenfolge-/Entscheidungs-Outputs); Extraktion zweimal byte-identisch (AD-17h/A0-19); keine eigene Engine (D-3/AD-11)"
# =====================================================================================
# DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum, zwei getrennte Worktrees) — §5.14 Pkt. 2
# =====================================================================================
runlabel "DET-2: ZWEI_RUN_IDENTISCH (nicht-vakuum) — zwei frische Agent-Kontexte in getrennt aufgebauten, sauberen Worktrees über demselben committeten Baum -> identische Bundle-States bis auf at (Q-6/A0-19); kanonisches Eingabemanifest + Run-Receipt außerhalb des Bundles; keine hart codierten erwarteten Bundle-Outputs, verified nicht pauschal maskiert; echte Content-Hashes (Witness weicht byte-weise von Baseline ab)"
# --- Zwei getrennte, saubere Worktrees über demselben Baseline-Commit ($BASE) aufbauen ---
# Re-Run-Sicherheit: verwaiste worktree-Registrierungen aus früheren Läufen (anderes $ROOT)
# erst prune, sonst blockiert `git worktree add` oder trägt Altpfade nach.
git worktree prune
git worktree add -q "$ROOT/wt-a" "$BASE" || { echo "HARD-FAIL (DET-2): Worktree wt-a nicht aufgebaut" >&2; exit 1; }
git worktree add -q "$ROOT/wt-b" "$BASE" || { echo "HARD-FAIL (DET-2): Worktree wt-b nicht aufgebaut" >&2; exit 1; }
# Kanonisches Eingabemanifest (A0-19, §5.14 Pkt. 2): Baseline-Commit + geordnete Source-
# Eingaben + output-sichtbare Run-/Zeit-/Identitätswerte — identisch für beide Läufe.
# Das Manifest wird unten in run2_worktree VOR jedem Lauf konsumiert/validiert (nicht dekorativ).
cat > "$ROOT/manifest.yaml" <<EOF
baseline: $BASE
generated_by: wow-compiler/0.1.0
sources:
- raw/alpha-v1.md
- raw/alpha-v2.md
output_visible_run_identity:
- generated.by
- generated.at # A0-20: einzige benannte Ausnahme im Bundle-State-Vergleich (Pkt. 3)
EOF
manifest_path="$ROOT/manifest.yaml"
[ -f "$manifest_path" ] || { echo "HARD-FAIL (DET-2): kanonisches Eingabemanifest fehlt ($manifest_path; §5.14 Pkt. 2)" >&2; exit 1; }
run2_worktree() { # $1 = Worktree-Pfad ; $2 = Run-Name ; führt die Instruktion als EIGENER
# Ausführungskontext (frischer Agent-Kontext simulativ: eigener Worktree, eigene Subshell
# mit eigenem cwd, keine Session-Wiederholung) über demselben committeten Baum aus.
# Der Run läuft komplett in einer Subshell — der Haupt-Kontext bleibt am Sandbox-Root
# (kein Carry-over des Worktree-cwd in nachfolgende Szenarien).
local wt="$1" name="$2"
(
set -e
cd "$wt" || exit 9
# Kanonisches Eingabemanifest VOR dem Lauf konsumieren/validieren (A0-19, §5.14 Pkt. 2):
# Baseline-Commit und geordnete Source-Eingaben müssen dem Repository-State entsprechen
# — das Manifest ist Eingabe der Instruktion, nicht bloße Doku (kein leerer Lauf möglich).
m_base=$(sed -n 's/^baseline: //p' "$ROOT/manifest.yaml")
[ -n "$m_base" ] || { echo "HARD-FAIL (DET-2): Manifest ohne baseline-Key" >&2; exit 9; }
[ "$m_base" = "$BASE" ] || { echo "HARD-FAIL (DET-2): Manifest-baseline ($m_base) != tatsaechliche Baseline ($BASE)" >&2; exit 9; }
# Abschnittsbewusste Extraktion: nur Elemente unter dem jeweiligen Manifest-Key
m_src=$(awk '/^sources:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); sub(/ *#.*$/,""); print}' "$ROOT/manifest.yaml")
m_identity=$(awk '/^output_visible_run_identity:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); sub(/ *#.*$/,""); print}' "$ROOT/manifest.yaml")
[ -n "$m_src" ] || { echo "HARD-FAIL (DET-2): Manifest ohne geordnete Source-Eingaben" >&2; exit 9; }
[ -n "$m_identity" ] || { echo "HARD-FAIL (DET-2): Manifest ohne output-sichtbare Identitätswerte" >&2; exit 9; }
case "$m_identity" in *generated.at*) ;; *) { echo "HARD-FAIL (DET-2): Manifest muss generated.at als Identitätswert führen (A0-20-Ausnahme)" >&2; exit 9; };; esac
case "$m_identity" in *generated.by*) ;; *) { echo "HARD-FAIL (DET-2): Manifest muss generated.by als Identitätswert führen" >&2; exit 9; };; esac
# generated.by wird aus dem Manifest KONSUMIERT (nicht hart codiert; Review-Loop-3, P-6):
# der output-sichtbare Identitätswert des Laufs ist Manifest-Inhalt, nicht Skript-Konstante.
m_by=$(sed -n 's/^generated_by: //p' "$ROOT/manifest.yaml")
[ -n "$m_by" ] || { echo "HARD-FAIL (DET-2): Manifest ohne generated_by-Wert" >&2; exit 9; }
# Sicherstellen: Worktree am Baseline-Commit, sauber (Q-6: getrennt aufgebaut, sauber).
git checkout -qf "$BASE"
git clean -qfd wiki raw
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
# Source-Eingaben-Verdikt: JEDE manifestierte geordnete Source-Eingabe (raw/alpha-v1.md
# aus der Baseline, raw/alpha-v2.md als neuer Increment-Input) existiert im Worktree
# NACH dem Input-Commit (EC-1-Semantik: Quelle zum Verarbeitungszeitpunkt vorhanden).
for s in $m_src; do
[ -f "$s" ] || { echo "HARD-FAIL (DET-2): Manifest-Source fehlt im Worktree nach Input-Commit: $s" >&2; exit 9; }
done
# Mutations-Commit als commitierter Run-Ausgangszustand: generated.at IST die reale
# Wanduhr-Ausgabe des Laufs (A0-20) — sie wird COMMITTET und im Receipt als eigener
# at-Zellenwert festgehalten; die Normalisierung auf AT erfolgt erst BEIM VERGLEICH
# (§5.14 Pkt. 2: "diff ... ohne die maskierte at-Zeile"), nicht vor dem Commit
# (kein stiller Ausschluss, keine Maskierung vor der Persistenz).
at_now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
cat > wiki/alpha.md <<EOF
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
- resource: raw/alpha-v2.md
id: s3
generated:
by: $m_by
at: $at_now
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
# Plan-/Kandidaten-/Reihenfolge-Ableitung (§5.14 Pkt. 2) — abgeleitet aus dem
# deterministischen Run-State, NICHT hart codiert: Kandidat = Stufe-a-Treffer des
# Terms "kopplung" über den (deterministischen) Body (dieselbe Ableitung wie DET-1).
# cand kann mehrere Dateien zeilengetrennt liefern — auf eine leere/kommafreie Form
# normalisiert, damit der Receipt-Zeilenblock nicht durch eingebettete Newlines bricht.
local cand
cand=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md | tr '\n' ',' | sed 's/,$//')
local plan
plan="cand=$cand;form=update;baseline=$BASE"
log_bullet "- Determinismus-Bestätigung (Zwei-Run, §5.14 Pkt. 2): $plan; Baseline $BASE"
# Confirm: der at-Zellenwert ist als reale Wanduhr-Ausgabe committet (kein Maskieren vor Commit).
git add -A && git commit -qm "Run: alpha-Update ($name)"
committed_at=$(git show 'HEAD:wiki/alpha.md' | sed -n 's/^ at: //p')
[ "$committed_at" = "$at_now" ] || { echo "HARD-FAIL (DET-2): commitierte at != Lauf-at ($committed_at != $at_now; Maskierung vor Commit?)" >&2; exit 9; }
# --- Bundle-State projizieren (deterministisch) + RUN-RECEIPT (außerhalb des Bundles) ---
# Run-Receipt (A0-19, §5.14 Pkt. 2): Candidate-Liste, Reihenfolge, Plan, Entscheidungen,
# Output-Hashes, Bundle-TREE und die at-Ausnahme (je Run-Cell) — liegt AUSSERHALB des
# Knowledge Bundle (nicht in wiki//raw/), hier unter $ROOT/receipts/.
# Die Run-Identität steht im Dateinamen (run2a.receipt/run2b.receipt); der INHALT ist die
# deterministische Projektion plus die at-Ausnahme-Cell, damit der Zwei-Run-Abgleich BOTH
# (a) byte-identische deterministische Teile UND (b) die ausgewiesene at-Ausnahme prüft.
mkdir -p "$ROOT/receipts"
{
echo "baseline: $BASE"
echo "plan: $plan"
echo "candidates: $cand"
echo "order: update-$cand"
echo "decisions: form=update;candidate_match=stufe_a(kopplung)"
# sha_alpha wird über die at-NORMALISIERTE Content-Projektion gebildet (§5.14 Pkt. 2:
# Vergleich "ohne die maskierte at-Zeile"; A0-20) — die deterministischen Bestandteile
# bleiben damit byte-vergleichbar, der at-Wert trägt nur in der at_cell-Ausnahme.
echo "sha_alpha=$(git show 'HEAD:wiki/alpha.md' | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1)"
echo "sha_log=$(git show 'HEAD:wiki/log.md' | sha256sum | cut -d' ' -f1)"
echo "sha_index=$(git show 'HEAD:wiki/index.md' | sha256sum | cut -d' ' -f1)"
# at_cell + tree sind die AUSNAHME-Evidenz: tree (Bundle-TREE-Hash des Run-Committs)
# enthält die realen at-Wanduhr-Werte — wird daher NICHT im deterministischen Block
# verglichen, sondern dokumentiert hier die benannte at-Ausnahme (kein stiller
# Ausschluss: die deterministischen Bundle-Bestandteile decken sha_alpha/log/index ab).
echo "at_cell=$committed_at"
echo "tree=$(git rev-parse 'HEAD:')"
} > "$ROOT/receipts/$name.receipt"
)
local rc=$?
[ $rc -eq 0 ] || { echo "HARD-FAIL (DET-2): Lauf $name im Worktree fehlgeschlagen (rc=$rc)" >&2; exit 1; }
}
echo "--- Lauf A (frischer Agent-Kontext, Worktree wt-a) ---"
run2_worktree "$ROOT/wt-a" run2a
cat "$ROOT/receipts/run2a.receipt"
echo "--- Lauf B (frischer Agent-Kontext, Worktree wt-b) ---"
run2_worktree "$ROOT/wt-b" run2b
cat "$ROOT/receipts/run2b.receipt"
# at-Ausnahme-Demo (Review-Loop-3, P-7): die aktive Ausnahme (real unterschiedliche
# Wanduhr-at-Werte) wird ERZWUNGEN, nicht nur gedruckt — bei einer Wanduhr-Sekunden-
# kollision wird Lauf B deterministisch wiederholt (frischer Kontext, derselbe Baum),
# bis die at-Zellen differieren (begrenzte Wiederholungen; jede Kollision ist ein
# Host-Timing-Artefakt, keine Eigenschaft des committeten Zustands). Damit ist der
# Change-Log-Beleg „zwei Läufe mit real unterschiedlichen at_cell-Werten" eine
# Eigenschaft jeder Re-Execution.
at_a=$(grep '^at_cell=' "$ROOT/receipts/run2a.receipt" | cut -d= -f2)
for try in 1 2 3 4 5; do
at_b=$(grep '^at_cell=' "$ROOT/receipts/run2b.receipt" | cut -d= -f2)
[ "$at_a" != "$at_b" ] && break
if [ "$try" -eq 5 ]; then
echo "HARD-FAIL (DET-2): at-Ausnahme nicht demonstrierbar (5x Sekundenkollision — Host-Timing-Anomalie)" >&2
exit 1
fi
echo " at-Kollision (Wanduhr-Sekunde) — Lauf B wird im frischen Kontext wiederholt (Versuch $try)"
run2_worktree "$ROOT/wt-b" run2b
done
# Receipt-Gerüst-Nicht-Vakuum: beide Receipts müssen den deterministischen Zeilenblock tragen
# (diff könnte sonst zweifach-leere Dateien als "identisch" PASSen lassen).
for r in "$ROOT/receipts/run2a.receipt" "$ROOT/receipts/run2b.receipt"; do
[ -s "$r" ] || { echo "HARD-FAIL (DET-2): Receipt $r leer oder fehlt (Vakuum)" >&2; exit 1; }
done
echo "--- Abgleich der zwei Run-Receipts (nicht nur jeder Lauf gg. Baseline; keine hart codierten Erwartungswerte) ---"
# Deterministischer Vergleich: ALLE Zeilen bis auf die beiden Ausnahme-Cells (at_cell=, tree=)
# müssen zwischen den zwei Runs byte-identisch sein — at_cell/tree tragen die realen, je Lauf
# unterschiedlichen Wanduhr-at-Werte und dokumentieren damit die benannte Ausnahme (A0-20,
# §5.14 Pkt. 3): differenzieren dürfen ausschließlich diese beiden; der Rest ist byte-identisch.
filtered_a=$(grep -vE '^(at_cell|tree)=' "$ROOT/receipts/run2a.receipt")
filtered_b=$(grep -vE '^(at_cell|tree)=' "$ROOT/receipts/run2b.receipt")
at_a=$(grep '^at_cell=' "$ROOT/receipts/run2a.receipt" | cut -d= -f2)
at_b=$(grep '^at_cell=' "$ROOT/receipts/run2b.receipt" | cut -d= -f2)
tree_a=$(grep '^tree=' "$ROOT/receipts/run2a.receipt" | cut -d= -f2)
tree_b=$(grep '^tree=' "$ROOT/receipts/run2b.receipt" | cut -d= -f2)
# (a) die deterministischen Bestandteile: byte-identisch
if [ "$filtered_a" != "$filtered_b" ]; then
echo "HARD-FAIL (DET-2): deterministische Receipt-Bestandteile weichen ab (AD-16-Klassifikationsdefekt, ausserhalb der at-Ausnahme; §5.14 Pkt. 2/4):" >&2
diff -u <(printf '%s\n' "$filtered_a") <(printf '%s\n' "$filtered_b") >&2 || true
exit 1
fi
# (b) at-Ausnahme benannt vorhanden (dokumentierte Ausnahme, kein stiller Ausschluss)
[ -n "$at_a" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme-Cell fehlt in receipt A" >&2; exit 1; }
[ -n "$at_b" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme-Cell fehlt in receipt B" >&2; exit 1; }
[ -n "$tree_a" ] && [ -n "$tree_b" ] || { echo "HARD-FAIL (DET-2): tree-Cell fehlt in einem Receipt" >&2; exit 1; }
# P-7-Assertion: die aktive Ausnahme ist jetzt hart belegt (Wiederholungs-Schleife oben
# hat at_a != at_b erzwungen) — kein stilles „BEFUND: at-Grenzfall" mehr.
[ "$at_a" != "$at_b" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme nicht demonstriert — beide Läufe tragen identischen at-Wert (P-7: aktive Ausnahme-Assertion)" >&2; exit 1; }
echo " BEFUND: at-Ausnahme aktiv (assertiert) — Lauf A at=$at_a, Lauf B at=$at_b (benannte Ausnahme, §5.14 Pkt. 3: übrige Bestandteile byte-identisch)"
# (d) Der deterministische Content weicht als Ganzes von der Baseline ab (Witness, echte Hashes).
ABS_A=$(git -C "$ROOT/wt-a" show "$BASE:wiki/alpha.md" | sha256sum | cut -d' ' -f1)
SHA_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)
[ -n "$ABS_A" ] && [ -n "$SHA_A" ] || { echo "HARD-FAIL (DET-2): Witness-Hashes leer (Vakuum — Assertion wertlos)" >&2; exit 1; }
[ "$ABS_A" != "$SHA_A" ] || { echo "HARD-FAIL (DET-2): Witness == Baseline (vakuum — der Vergleich bewiese nichts; AD-17h/A0-19: echte Content-Identität gefordert)" >&2; exit 1; }
# verified wird NICHT pauschal maskiert: das demonstrierte Bundle trägt ausschließlich die
# Identitätswerte generated.by + generated.at (kein verified[]-Feld — die Ausnahme-Menge ist
# in diesem Mini-Bundle exakt {generated.at}); die einzige erlaubte Differenz der
# Bundle-State-Vergleiche ist die benannte at-Ausnahme (Pkt. 3), die hier als at_cell real
# committet und beim Vergleich dokumentiert normalisiert ist (kein Maskieren vor Commit,
# kein stiller Ausschluss — eine weitere Differenz würde den deterministischen Abgleich brechen).
echo " WITNESS: Baseline-alpha-SHA ($ABS_A) != Run-alpha-SHA ($SHA_A) — der Zwei-Run-Vergleich ist nicht-vakuum (Content weicht von Baseline ab, echte Hashes)"
echo " MANIFEST: $ROOT/manifest.yaml (identisch für beide Läufe, VOR jedem Lauf validiert); RECEIPTS: $ROOT/receipts/ (außerhalb des Bundles)"
echo "RESULT: PASS — DET-2: ZWEI_RUN_IDENTISCH — zwei frische Agent-Kontexte in getrennt aufgebauten, sauberen Worktrees (wt-a/wt-b, demselben committeten Baum) produzieren identische Bundle-States (Run-Receipts byte-identisch bis auf die als at_cell ausgewiesene generated.at-Ausnahme: Plan-, order-, decisions-, log-, index-, alpha-Content-Hashes und Bundle-TREE; at real committet und erst beim Vergleich normalisiert — dokumentierte Ausnahme statt stiller Ausschluss); Vergleich nicht-vakuum (Witness vs. Baseline, echte Content-Hashes, §5.14 Pkt. 2, FT-10/AD-17h AC-1, Q-6/A0-19)"
# =====================================================================================
# DET-3 ZWEI_RUN_ABWEICHUNG (§5.14 Pkt. 4)
# =====================================================================================
runlabel "DET-3: ZWEI_RUN_ABWEICHUNG — künstlich divergenter Lauf -> als AD-16-Klassifikationsdefekt klassifiziert und textuell benannt (kein Rauschen); korrigierter zweiter Lauf == erster Lauf (erneut bestätigt)"
# Referenz-Run (deterministische Bundle-State-Projektion)
run3_ref() {
isolate run3ref
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
- resource: raw/alpha-v2.md
id: s3
generated:
by: wow-compiler/0.1.0
at: AT
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
git add -A && git commit -qm "Run: alpha-Update (Referenz)"
echo "SHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)"
}
# Divergenter Run: veränderter Body-Content (RAUSCHEN-fremde Abweichung — ein deterministischer
# Bestandteil weicht ab, hier eine andere Formulierung der Aussage) trotz gleichem Git-State+Input.
run3_div() {
isolate run3div
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
- resource: raw/alpha-v2.md
id: s3
generated:
by: wow-compiler/0.1.0
at: AT
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
Alpha wird um eine SYNCHRONE KOPPLUNG erweitert (raw/alpha-v2.md#S-3).
EOF
git add -A && git commit -qm "Run: alpha-Update (divergent)"
echo "SHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)"
}
R3=$(run3_ref)
D3=$(run3_div)
echo "Referenz-Run: $R3"
echo "Divergenter Lauf: $D3"
SHA_REF=$(echo "$R3" | cut -d= -f2)
SHA_DIV=$(echo "$D3" | cut -d= -f2)
[ -n "$SHA_REF" ] && [ -n "$SHA_DIV" ] || { echo "HARD-FAIL (DET-3): Hashes leer (Vakuum)" >&2; exit 1; }
if [ "$SHA_REF" = "$SHA_DIV" ]; then
echo "HARD-FAIL (DET-3): der divergente Lauf blieb inspiziert ununterscheidbar (Abweichung nicht erkannt; §5.14 Pkt. 4 verlangt Klassifikation)" >&2
exit 1
fi
echo "NFR-4-Benennung (textuell): ZWEI_RUN_ABWEICHUNG — Lauf 'divergent' weicht ausserhalb der benannten at-Ausnahme vom Referenz-Lauf ab (unterschiedlicher Content-Hash $SHA_DIV != $SHA_REF bei gleichem Git-State + gleichem Eingabeset) => AD-16-Klassifikationsdefekt (FT-10/AD-17h-AC), kein Rauschen; der Run wird als Fehler im Klassifikations-Mechanismus behandelt, textuell benannt und korrigiert/zurückgerollt (§5.14 Pkt. 4, Zustands-Restaurations-Invariante §5.13 Pkt. 3)"
echo "--- Korrigierter zweiter Lauf: rollt zurueck und wiederholt deterministisch (== Referenz) ---"
rollback3() {
git checkout -q "$BASE" -- wiki/ 2>/dev/null || true
git reset -q --hard "$BASE"
git clean -qfd wiki
}
rollback3
R3B=$(run3_ref)
SHA_REFB=$(echo "$R3B" | grep '^SHA=' | cut -d= -f2)
[ "$SHA_REF" = "$SHA_REFB" ] || { echo "HARD-FAIL (DET-3): korrigierte Wiederholung weicht von der Referenz ab (AD-17h verletzt)" >&2; exit 1; }
echo " BEFUND: korrigierter Lauf == Referenz-Lauf (SHA $SHA_REF) — der Vertrag ist nach Korrektur erneut bestätigt (Pkt. 4: 'Befund wird (ggf. als korrigierter zweiter Lauf) erneut bestätigt')"
echo "RESULT: PASS — DET-3: ZWEI_RUN_ABWEICHUNG — künstlich divergenter Lauf wurde als AD-16-Klassifikationsdefekt erkannt (Content-Hash-Differenz bei gleichem State+Input — kein Rauschen, keine Umgebungs-Streuung) und textuell benannt (NFR-4); korrigierter zweiter Lauf reproduziert den Referenz-Bundle-State (Zustands-Restaurations-Invariante, §5.13 Pkt. 3; §5.14 Pkt. 4)"
# =====================================================================================
# DET-4 AT_GAP_AUSNAHME (§5.14 Pkt. 3)
# =====================================================================================
runlabel "DET-4: AT_GAP_AUSNAHME — generated.at-Wanduhr-Gap ist die einzige benannte Ausnahme; zwei Runs mit unterschiedlichem at -> diff ausschließlich auf der at-Zeile; nach at-Maskierung byte-identisch; jede andere Differenz = AD-16-Klassifikationsdefekt"
isolate det4
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2"
mkrun4() { # $1 = at-Wert — schreibt alpha mit EXAKT unterschiedlichen at-Werten
cat > wiki/alpha.md <<EOF
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
- resource: raw/alpha-v2.md
id: s3
generated:
by: wow-compiler/0.1.0
at: $1
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
EOF
}
mkrun4 "2026-08-16T09:00:00Z"
git add -A && git commit -qm "Run at=A"
mkrun4 "2026-08-19T23:59:59Z"
git add -A && git commit -qm "Run at=B"
# diff zwischen beiden Run-Zuständen: ausschließlich die at-Zeile
DABC=$(git diff HEAD~1 HEAD -- wiki/alpha.md)
[ -n "$DABC" ] || { echo "HARD-FAIL (DET-4): die zwei Runs sind identisch — at-Gap nicht demonstriert (Vakuum)" >&2; exit 1; }
non_at=$(echo "$DABC" | grep '^[-+][^-+]' | grep -v '^[-+] at: ' | grep -v '^[+-][+-]')
if [ -n "$non_at" ]; then
echo "HARD-FAIL (DET-4): der diff zwischen den zwei Runs betrifft BESTANDTEILE ausserhalb der at-Ausnahme:" >&2
echo "$non_at" | sed 's/^/ /' >&2
exit 1
else
echo " BEFUND: diff ausschließlich auf der at-Zeile (einzige benannte Ausnahme, §5.14 Pkt. 3)"
fi
echo "--- Nach at-Maskierung byte-identisch (restliche Bundle-Bestandteile) ---"
H1=$(git show HEAD~1:wiki/alpha.md | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1)
H2=$(git show HEAD:wiki/alpha.md | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1)
[ "$H1" = "$H2" ] || { echo "HARD-FAIL (DET-4): nach at-Maskierung weichen die Runs ab (bestandteil über at hinaus; §5.14 Pkt. 3 verlangt byte-Identität der übrigen Teile)" >&2; exit 1; }
echo " BEFUND: nach at-Maskierung byte-identisch (SHA $H1) — dokumentierte Ausnahme, kein stiller Ausschluss"
echo "--- Negative Kontrolle: eine Differenz AUSSERHALB der Ausnahme ist ein AD-16-Klassifikationsdefekt ---"
mkrun4 "2026-08-16T09:00:00Z"
printf '\nAndere Aussagenformulierung.\n' >> wiki/alpha.md
git add -A && git commit -qm "Run at=B' (Differenz im Body)"
non_at2=$(git diff HEAD~1 HEAD -- wiki/alpha.md | grep '^[-+][^-+]' | grep -v '^[-+] at: ' | grep -v '^[+-][+-]')
[ -n "$non_at2" ] || { echo "HARD-FAIL (DET-4): Body-Differenz wurde nicht erkannt (Negativkontrolle leer)" >&2; exit 1; }
echo "NFR-4-Benennung (textuell): Body-Differenz ausserhalb der at-Ausnahme-Menge => AD-16-Klassifikationsdefekt (kein Rauschen), textuell benannt (§5.14 Pkt. 3/4)"
echo "RESULT: PASS — DET-4: AT_GAP_AUSNAHME — generated.at-Wanduhr-Gap ist die einzige benannte Differenz im Bundle-State-Vergleich (A0-20-Konvention, §5.9 Pkt. 2/§5.10 Pkt. 8): zwei Runs diffieren ausschließlich in der at-Zeile, nach at-Maskierung byte-identisch; jede andere Differenz liegt ausserhalb der Ausnahme = AD-16-Klassifikationsdefekt (Negativkontrolle, §5.14 Pkt. 3)"
# =====================================================================================
# DET-5 EM_DASH (§3.2 Pkt. 1b, §5.14 Pkt. 5)
# =====================================================================================
runlabel "DET-5: EM_DASH — Em-Dash — normalisiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen (§3.2 Pkt. 1b: Kollaps-Klasse [-–— _] -> -)"
# Term-Basis mit Em-Dash / En-Dash / Bindestrich / Unterstrich / Leerzeichen-Separator
EM=$(norm "wissen — relevanz")
EN=$(norm "wissen relevanz")
HY=$(norm "wissen - relevanz")
US=$(norm "wissen_relevanz")
SP=$(norm "wissen relevanz")
[ "$EM" = "$EN" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != En-Dash-Form" >&2; exit 1; }
[ "$EM" = "$HY" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Bindestrich-Form" >&2; exit 1; }
[ "$EM" = "$US" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Unterstrich-Form" >&2; exit 1; }
[ "$EM" = "$SP" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Leerzeichen-Form" >&2; exit 1; }
case "$EM" in
*"—"*) echo "HARD-FAIL (DET-5): Em-Dash blieb in der canonischen Form erhalten (Kollaps-Klasse greift nicht)" >&2; exit 1;;
esac
echo " canonische Form (alle fünf Separatoren): '$EM'"
echo " Em-Dash-Term kollabiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen (kein stiller Ausschluss, §3.2 Pkt. 1b)"
echo "RESULT: PASS — DET-5: EM_DASH — Em-Dash — ist in der Kollaps-Klasse [-–— _] -> - (§3.2-Pkt.-1b-Ergänzung, §5.14 Pkt. 5): Term mit Em-Dash erhält dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen-Varianten — deterministisch, keine unterschiedliche canonische Form je Separator"
# =====================================================================================
# DET-6 KOLLAPS (§3.2 Pkt. 1b, §5.14 Pkt. 5)
# =====================================================================================
runlabel "DET-6: KOLLAPS — Kollaps-Reichweite: Läufe (a--b), führende (-x), trailende (x-) -> Kollaps auf genau ein - + Trim (§3.2 Pkt. 1b)"
[ "$(norm "a--b")" = "a-b" ] || { echo "HARD-FAIL (DET-6): Lauf a--b kollabiert nicht auf a-b (norm='$(norm "a--b")')" >&2; exit 1; }
[ "$(norm "-x")" = "x" ] || { echo "HARD-FAIL (DET-6): führender Separator -x wird nicht getrimmt (norm='$(norm "-x")')" >&2; exit 1; }
[ "$(norm "x-")" = "x" ] || { echo "HARD-FAIL (DET-6): trailender Separator x- wird nicht getrimmt (norm='$(norm "x-")')" >&2; exit 1; }
[ "$(norm "a--b")" = "a-b" ] && [ "$(norm "a---b")" = "a-b" ] && [ "$(norm "---x---")" = "x" ] || { echo "HARD-FAIL (DET-6): Läufe/Trim-Kombination inkonsistent" >&2; exit 1; }
echo " Läufe a--b -> '$(norm "a--b")'; führend -x -> '$(norm "-x")'; trailend x- -> '$(norm "x-")'"
echo "RESULT: PASS — DET-6: KOLLAPS — Kollaps-Reichweite deterministisch (§3.2 Pkt. 1b, §5.14 Pkt. 5): Läufe kollabieren auf genau ein -, führende/trailende Separatoren werden getrimmt — glaubhaft re-executierbar, keine offene Reichweiten-Frage"
# =====================================================================================
# DET-7 MATCH_SCOPE (§3.2 Pkt. 2a, §5.14 Pkt. 5)
# =====================================================================================
runlabel "DET-7: MATCH_SCOPE — Stufe a matcht ganze Wörter über den Body, exklusive YAML-Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen, §3.2 Pkt. 2a)"
isolate det7
# Concept: Body enthält "kopplung" als GANZES Wort; Frontmatter enthält "kopplung" als
# Substring im Quell-Pfad raw/kopplung-v1.md (Frontmatter-Treffer-Wächter).
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/kopplung-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Alpha als Wissenseinheit: hier steht einzig das ganz Wort kopplung ohne weitere Bindung.
EOF
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma: das Wort entkopplung belegt nur einenerweiternden Teil — kein eigener ganzer Treffer.
EOF
git add -A && git commit -qm "Stufe-a-Flex"
# 1) Substring-Kontrolle: 'kopplung' (ganz) trifft alpha; 'opplung' (Substring) trifft nichts
HIT1=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md)
[ "$HIT1" = "alpha" ] || { echo "HARD-FAIL (DET-7): ganzes-Wort-Match 'kopplung' liefert '$HIT1' (Soll: alpha; Substring-/Gamma-Treffer unerlaubt)" >&2; exit 1; }
HIT2=$(match_stufe_a "opplung" wiki/alpha.md)
[ -z "$HIT2" ] || { echo "HARD-FAIL (DET-7): Substring-Match 'opplung' liefert einen Kandidaten (Stufe a matcht ganze Wörter, §3.2 Pkt. 2a)" >&2; exit 1; }
# 2) Frontmatter-Exklusion: Suchbegriff exakt gleich einem Frontmatter-RESOURCE-Substring
# (raw/kopplung-v1.md enthält 'kopplung') — zugleich im Body von alpha steht 'kopplung'
# nur als ganzes Wort => Treffer genau EINMAL (alpha). Der Frontmatter ist exkludiert,
# ein zusätzlicher Body-Treffer erzeugt keinen zweiten Kandidaten — wir prüfen, dass
# das Frontmatter-Vorkommen keinen falschen Kandidaten (z. B. durch RegExp über die ganze
# Datei inkl. Frontmatter) erzeugt. Konkret: wir legen einen zweiten Concept-Body VOR, der
# 'kopplung' NUR im Frontmatter traegt (Quell-Pfad raw/kopplung-mirror.md) — Stufe a muss
# ihn verwerfen, weil der Frontmatter exkludiert ist.
cat > wiki/mirror.md <<'EOF'
---
type: concept
sources:
- resource: raw/kopplung-mirror.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma-Mirror ohne Kopplungs-Begriff im Bodytext.
EOF
git add -A && git commit -qm "Frontmatter-only-Kandidat"
HIT3=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md wiki/mirror.md)
[ "$HIT3" = "alpha" ] || { echo "HARD-FAIL (DET-7): Frontmatter-Treffer erzeugt Kandidat (mirror) — Stufe a matcht nur Body, exklusive YAML-Frontmatter (§3.2 Pkt. 2a)" >&2; exit 1; }
# 3) Direkte Formel-Probe der operativen Stufe-a-Formel (§3.2-Pkt.-2a-Zweistufen-Mechanik):
# Sweep mit der Formel -> dann deterministischer Scope-Filter (ganze Wörter, Body, after second ---).
SWEEP=$(grep -rl 'kopplung' --exclude=log.md wiki/ | LC_ALL=C sort)
# Sweep-muss alpha, gamma (Substring entkopplung) und mirror (Frontmatter-only) finden —
# der Scope-Filter verwirft gamma+mirror; als Kandidaten bleiben die Body-ganz-Wort-Treffer.
FILTERED=$(for f in $SWEEP; do
if match_stufe_a "kopplung" "$f" | grep -q .; then basename "$f" .md; fi
done | LC_ALL=C sort | tr '\n' ' ')
FILTERED="${FILTERED% }"
[ "$FILTERED" = "alpha" ] || { echo "HARD-FAIL (DET-7): operative Stufe-a-Zweistufen-Mechanik liefert '$FILTERED' (Soll: alpha) — Scope-Filter unvollständig (§3.2 Pkt. 2a, §5.14 Pkt. 5)" >&2; exit 1; }
echo " Direkte Formel-Probe: Sweep (grep -rl) -> Scope-Filter verwirft Substring-/Frontmatter-Treffer -> Kandidat: alpha"
echo "RESULT: PASS — DET-7: MATCH_SCOPE — Stufe a matcht ganze Wörter (Wortgrenzen) über den Body, exklusive YAML-Frontmatter: Substring-Treffer ('opplung') und Frontmatter-only-Kandidaten (mirror) liefern keine Kandidaten (§3.2 Pkt. 2a, §5.14 Pkt. 5) — deterministischer Stufe-a-Scope, keine falschen Kandidaten; operative Zweistufen-Mechanik (Sweep+Filter) abgesichert"
# =====================================================================================
# DET-8 ORPHAN (§5.10 Pkt. 8, §5.14 Pkt. 5)
# =====================================================================================
runlabel "DET-8: ORPHAN — neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer -> unzugeordnet, log.md-verwaist protokolliert (Datumsgruppe, <Baseline-Commit>), kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default)"
isolate det8
cat > raw/orphan-v1.md <<'EOF'
### S-1
Evidenz: gänzlich fremdes Thema, das in keinem bestehenden Concept-Body Wort-Treffer hat.
EOF
git add -A && git commit -qm "Verwaiste Evidenz"
# Ziel-Pfad-Treffer-Prüfung: Term über die neuen Quellen enthält 'fremdes-thema' — kein
# bestehender Concept-Body trifft (Stufe a, deterministisch); kein Update-Kandidat.
CAND=$(match_stufe_a "fremdes" wiki/alpha.md wiki/gamma.md)
[ -z "$CAND" ] || { echo "HARD-FAIL (DET-8): verwaiste Evidenz erzielt einen Ziel-Pfad-Treffer (Kein-Orphan erwartet)" >&2; exit 1; }
# Reconcile-Orphan-Regel (§5.10 Pkt. 8 / §5.14 Pkt. 5): deterministisch aus dem committeten
# Git-State (Zuwachs gg. <Baseline-Commit>) als verwaist befunden -> datumsgruppierter
# log.md-Eintrag (Header YYYY-MM-DD, Quell-Pfad + <Baseline-Commit>); keine eigenständige Anlage.
log_bullet "- Reconcile-Orphan (Story 3.8; §5.10 Pkt. 8 / §5.14 Pkt. 5): raw/orphan-v1.md (Zuwachs gg. <Baseline $BASE>) ohne Ziel-Pfad-Treffer — unzugeordnet, verwaist protokolliert; kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default, Epic-4-Interface)"
echo "--- Kein Banner / keine stille Vorbearbeitung / keine eigenständige Anlage ---"
if [ -f wiki/orphan.md ]; then
echo "HARD-FAIL (DET-8): verwaiste Evidenz wurde eigenständig als Concept angelegt (verboten, AD-16-Default)" >&2
exit 1
fi
# Nur log.md ist mutiert (Orphan-Eintrag) — Erhaltungs-Invariante §5.9 Pkt. 5 (log.md ist
# erlaubtes Mitglied des Diff-Selbsttest-Satzes); keine weiteren Mutations-Pfade.
MUT=$( { git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u)
[ "$MUT" = "log" ] || { echo "HARD-FAIL (DET-8): Verwaist-Run mutiert ausserhalb {log} — Pfade: [$MUT]" >&2; exit 1; }
grep -qF "Reconcile-Orphan" wiki/log.md || { echo "HARD-FAIL (DET-8): Verwaist-Protokoll fehlt in log.md (§5.10 Pkt. 8)" >&2; exit 1; }
grep -qF "## 2026-08-20" wiki/log.md || { echo "HARD-FAIL (DET-8): log.md-Eintrag nicht in Datumsgruppe (Vertrag §5)" >&2; exit 1; }
grep -qF "$BASE" wiki/log.md || { echo "HARD-FAIL (DET-8): <Baseline-Commit> fehlt im Verwaist-Eintrag (§5.10 Pkt. 8, deterministisch auflösbar D-2)" >&2; exit 1; }
echo " BEFUND: raw/orphan-v1.md bleibt in raw/ unangetastet (AD-3); log.md trägt datumsgruppierten Verwaist-Eintrag mit <Baseline-Commit>"
echo "RESULT: PASS — DET-8: ORPHAN — neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer bleibt unzugeordnet und wird als datumsgruppierter log.md-Eintrag (Quell-Pfad + <Baseline-Commit>) verwaist protokolliert (§5.10 Pkt. 8 / §5.14 Pkt. 5): kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default) — Erhaltungs-Invariante gewahrt (nur log.md mutiert)"
echo
echo "===== Sandbox abgeschlossen (DET-1..DET-8) ====="
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,279 @@
---
title: 'Inkrementellen Datenfluss implementieren (Interpret → Reconcile → Synthesize → Update) (Story 3.1)'
type: 'feature'
created: '2026-08-18'
status: 'done'
review_loop_iteration: 2
baseline_commit: 802a5576eb4bbf54761a947e43e68b7e7ea9d67d
context:
- _bmad-output/implementation-artifacts/epic-3-context.md
---
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** Die Compiler-Instruktion (`schema/compiler.md`) kennt heute nur den **Neu-Anlage-Pfad** (Erzeugung neuer Concepts). Die Erweiterung/Präzisierung/Korrektur bestehender Concepts wird an zwei Stellen explizit als Epic-3-Vorbehalt ausgeschlossen (`compiler.md:40` §3.2-Kollision-Hold „Aktualisierung ist Epic 3"; `compiler.md:287` §7), und der §0-Aufruf (`compiler.md:13`) beschreibt die Phasenfolge Interpret → Reconcile → Synthetisieren → Mutieren → Validieren nur als Erzeugungs-Ablauf. Damit ist der zweite Compilation Run gegen bestehendes Wissen (FR-4) instruktionsseitig nicht definiert: Es gibt keinen deterministischen Pfad, wie ein Run neue Evidenz mit den tatsächlich betroffenen Concepts verrechnet, ohne unverändertes Wissen zu regenerieren (AD-5, A0-6, FR-6, FR-12).
**Approach:** Story 3.1 verankert den **inkrementellen Datenfluss** als Erweiterungs-Instruktion in `schema/compiler.md` (D-3, rein textuell — kein Code, kein Standalone): (a) das bestehende Reconcile (§3) wird vom bloßen Dateikollisions-Hold zum **Update-Routing** (erkannte bestehende Wissenseinheit → Update-Kandidat, kein Duplikat, kein stilles Überschreiben); (b) eine neue Update-Mutations-Sektion (§5.9) spezifiziert, wie ein bestehendes Concept erweitert/präzisiert/korrigiert wird (FR-6) und wie Inline-Provenienz (§5.5) und `sources` nachgeführt werden; (c) die **Erhaltungs-Invariante** wird verankert: nicht betroffene Concept-Pfade bleiben bit-identisch unverändert (FT-6, FR-12), Git-Änderungen konzentrieren sich auf betroffene Concepts — nachweisbar über einen deterministischen Diff-Selbsttest; (d) die Defer-Bausteine**R-1** (Input-Zuwachserkennung: welche `raw/`-Dateien sind neu/modifiziert — via `git diff` und/oder SHA-256-Record aus `source.md`) und **P2** (Pre-Run-Reconcile-Check-Block als wiederverwendbarer, textueller Vorprüf-Block) werden als Teil des inkrementellen Runs eingearbeitet. Vertrag (`schema/wiki-compiler.md`), Validator (`schema/validator.md`) und `raw/` bleiben read-only (AD-3); keine neue §7-Invaliditätsklasse.
## Boundaries & Constraints
**Always:**
- **Story 3.1 ist eine Instruktions-Story (D-3).** Der inkrementelle Datenfluss wird ausschließlich in `schema/compiler.md` als deterministische Text-Instruktion verankert. Kein ausführbares Programm, kein Standalone-Validierungs-Tool, keine neue §7-Invaliditätsklasse, kein Change an `schema/wiki-compiler.md` / `schema/validator.md` / `raw/` (AD-3).
- **Das Update ersetzt den Abbruch, nicht die Erhaltung.** Wo heute der Kollisions-Hold bei „Concept existiert bereits" den Run für die Einheit abbricht, tritt das Update-Routing: eine erkannte bestehende Wissenseinheit wird im **bestehenden Concept-Pfad** aktualisiert (keine neue Datei, kein Duplikat — FR-6). Beibehaltende und neue belegte Aussagen werden sauber geführt. Kein stummer Überschreib (bestehende Provenienz/Inhalte werden nie ohne Beleg entfernt — AD-16-Default behält die Erhaltung; die AD-16-Klassifikation selbst ist Epic 4, Story 4.1, und wird hier nicht vorweggenommen).
- **Erhaltungs-Invariante (Kern):** Ein Compilation Run darf nur die Concepts **neu anlegen oder verändern**, die durch den erkannten neuen Erkenntnis-Zuwachs tatsächlich betroffen sind. Nicht betroffene Concept-Pfade bleiben byte-identisch unverändert (FT-6, FR-12); es gibt **nie** „Regenerate Everything" aus allen Rohquellen (AD-5, A0-6). Nachweisbar über einen re-executierbaren Diff-Selbsttest (`git diff --stat` auf `wiki/` zeigt ausschließlich betroffene Dateien + `log.md` + Index-Nachführungen).
- **published/committed als Input (AD-17.2/AD-17a):** Der Run verarbeitet ausschließlich veröffentlichte (committete) Inhalte als Input — neues Source Material, sobald es unter `raw/` committet ist, und das bestehende `wiki/` aus committetem Zustand. Zwischenstände während einer Mutation sind nie Input (§1.1 bleibt bestehende Regel, wird referenziert).
- **Reconcile-Ergebnis ist die Update-Kandidatenliste.** Der Reconcile-Schritt erzwingt, dass der Producer die Menge der betroffenen Concepts als **nachvollziehbare Kandidatenliste (Concept-Pfade)** erhebt, bevor mutiert wird. Der feinkörnige Relevanz-Findungsmechanismus (grep/ripgrep/Markdown-Traversal/Link-Following als eigene Ausformulierung) ist Story 3.2 vorbehalten; Story 3.1 bindet die Kandidatenerhebung an textuelle, deterministische Mittel (Konzept-/Term-Überschneidung zwischen neuer Evidenz und bestehenden Concept-Bodies via `grep`/`ripgrep`, `index.md`-Traversal, Link-Following) — keine Embeddings/Vector (AD-13).
- **Kein Leasing-/Dirty-Tree-Scope in 3.1.** Leasing, Dirty-Tree-Schutz, Staleness, Merge-vermittelte Kollision sind Story 3.5/3.6. Story 3.1 behält die bestehende Commit-Boundary = Mutations-Boundary-Regel (AD-17f, `compiler.md:13`/`:63`) bei und ändert sie nicht.
- **Defer-Bausteine werden mitgezogen:** deferred-work **R-1** (Input-Zuwachserkennung via `git diff` + SHA-256-Record aus `source.md`) und **P2** (Pre-Run-Reconcile-Check-Block bündeln) werden als Teil der inkrementellen Run-Instruktion eingearbeitet und in `deferred-work.md` als aufgegriffen markiert (append-only, keine Duplikate).
- `sprint-status.yaml`: Key `3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile`**in-progress**.
**Ask First:** AD-7d-Renames/Redirects bestehender Concepts · Einführung einer echten Kollisions-Klassifikation (CORRECTING/CONTRADICTING etc.) vor Epic 4 · Wechsel des `generated.at`-Verhaltens bei Updates gegenüber der hier festgelegten Konvention (s. Design Notes) · Validator-/Vertrags-/`raw/`-Change · Änderung der Commit-Boundary-Regel.
**Never:** Änderungen an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3) · neue §7-Invaliditätsklasse · Einführung eines Standalone-Programms/Validators (D-3) · „Regenerate Everything" über bestehende Concepts (AD-5, A0-6) · stummer Überschreib oder Löschung bestehender Provenienz ohne Beleg (AD-16-Default: Erhaltung) · Embedding/Vector-Suche (AD-13) · Duplikat-Anlage eines bestehenden Concept-Pfads.
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| HAPPY_PATH_UPDATE | Bestehendes Concept `wiki/<a>/<c>.md` + neue committete Evidenz `raw/…` betrifft es (Term-/Konzept-Überschneidung) | Update-Routing: Concept wird im bestehenden Pfad erweitert/präzisiert (§5.9); Inline-Provenienz + `sources` nachgeführt (nur echt neue Belege ergänzt); `log.md`-Eintrag (Vertrag §5-Format) mit Story 3.1; kein neues File, kein Duplikat | N/A |
| UNTOUCHED_CONCEPT | Neue Evidenz betrifft ein bestehendes Concept **nicht** (keine Überschneidung) | Concept bleibt **unverändert**: keine Dateiänderung, kein Index-, kein `log.md`-Zusatz (FT-6/FR-12); Diff-Selbsttest zeigt keinen Ghost-Diff auf diesem Pfad | N/A |
| INPUT_UNCOMMITTED | Run erhält uncommittete Zwischenstände (Arbeitskopie `wiki/`/`raw/` abweichend von HEAD) als Input | Run lehnt ab bzw. bezieht nur published/committed Zustand ein (AD-17a); keine Mutation gegen Zwischenstände | textuell benannter Abbruch „published/committed Input erforderlich" (§1.1-Fortsetzung) |
| CHANGE_DETECTION (R-1) | `raw/` enthält (a) eine neue, (b) eine unveränderte Evidenzdatei | Zuwachserkennung: nur die neue/modifizierte Datei wird als Zuwachs interpretiert (git-diff-basiert und/oder SHA-256-Record aus `source.md`); unveränderte bleiben außen vor | unlesbarer/fehlender SHA-256-Record → Hinweis, Datei dennoch als Zuwachs nicht doppelt verarbeiten |
| CONCEPT_COLLISION_BESTEHEND | Erkannte Wissenseinheit = bereits existierendes Concept (Ziel-Pfad belegt) | Statt bisherigem Kollision-Hold-Abbruch: Update-Routing (§5.9) — Mutation im bestehenden Pfad; kein Duplikat, kein stummer Überschreib; Index-Link bleibt unverändert gültig | Widersprechender Inhalt ohne Ersetzungsevidenz → Erhaltung; explizite Ablage in `log.md` (Epic-4-Interface, keine Korrektur-Klassifikation hier) |
| PRE_RUN_RECONCILE (P2) | Run-Start mit neuem Zuwachs | Pre-Run-Reconcile-Check-Block (Zielpfade, Quellen-Existenz EC-1, Betroffenheits-Kandidatenliste, `wiki/index.md`-V-1) wird vor jeder Mutation durchlaufen und textuell festgehalten | fehlende Bundleroot → Run-FAIL (V-1, besteht fort) |
</frozen-after-approval>
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3, einziger Instruktions-Ort):
- §0-Aufruf (`:13`): Ablaufstruktur um die Update-Variante präzisieren — die sechs Phasen bleiben; Reconcile/Mutieren betreffen auch **bestehende** Concepts.
- §3 Reconcile (`:3741`): Pkt. 2 Kollision-Hold (`:40`) — Epic-3-Abbruch („Aktualisierung ist Epic 3") **ersetzen** durch Update-Routing (→ §5.9); Candidate-Erhebung (betroffene Concepts) textuell-deterministisch; Pkt. 3 V-1-Prüfung (`:41`) bleibt.
- **Neue §5.9 „Inkrementelles Update bestehender Concepts (Story 3.1)"** nach §5.8 (nach `:233`, vor §6 `:234`): (1) Update-Stimulus = Reconcile-Kandidatenliste (betroffene Concept-Pfade); (2) Mutationsmechanik — Erweitern (Absatz/Beleg ergänzen), Präzisieren (Aussage schärfen, Beleg neu/nachführen), Korrigieren (ersetzte Aussage + Ersetzungsbeleg, keine stille Löschung), Frontmatter-`sources` nur um echte neue Belege ergänzen, `generated.at` = aktueller Run-Zeitstempel (Konvention, s. Design Notes), vorhandenes `verified` bleibt stehen; (3) Index-/Link-Form unverändert (§5.6-Pin), kein neuer Link bei reinem Body-Update; (4) `log.md`-Eintragspflicht (Vertrag §5; „Story 3.1-Update" markieren); (5) deterministischer Diff-Selbsttest der Erhaltungs-Invariante (nur betroffene Dateien + log.md + Index im `git diff --stat` auf `wiki/`); (6) Change-Detection (Defer R-1: `git diff` auf `raw/` und/oder SHA-256-Record aus `raw/**/source.md` bestimmt den Zuwachs) und Pre-Run-Reconcile-Check-Block (Defer P2) als wiederverwendbare textuelle Check-Blöcke der Run-Vorphase.
- §7 (`:287`): Epic-3-Vorbehalt auf die verbleibenden 3.x-Themen kürzen (Synthese über mehrere Concepts → 3.4; Leasing/Dirty-Tree → 3.5/3.6; Relevanz-Verfeinerung → 3.2) — das Update-Thema ist damit aus dem Vorbehalt entlassen.
- §8 Revisionslog (`:291317`): **Revision 2.4** (Story 3.1) + Normreferenzen unverändert (AD-5, AD-6, AD-17h sind schon gelistet; A0-6/FR-6/FR-12 ggf. ergänzen); Abschlussklausel (kein Vertrag-/Validator-/`raw/`-Change, keine neue §7-Klasse, kein Standalone).
- `wiki/log.md`**append**: Story-3.1-Eintrag (Vertrag §5-Format, `## YYYY-MM-DD` Gruppe nach `:3`) mit Update-Semantik, Erhaltungs-Invariante, Diff-Selbsttest-Beleg (Ist-Baum: kein Ghost-Diff), Statuswechsel `3-1-…` `backlog → in-progress`, per-Datei-Validator-Verdikt-Zeile (Ist-Baum SUCCESS) — Analogie Story-2.5-Eintrag (`log.md:5`).
- `_bmad-output/implementation-artifacts/deferred-work.md`**mutiert** (append-only): Defer R-1 (`:126128`) und P2 (`:160162`): `status`-Nachführungs-Notiz „aufgegriffen (2026-08-18, Story 3.1 — §5.9)" — bestehende Einträge nicht verändern, nur Status-Ergänzung im vorgegebenen append-only-Stil.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: Key `3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile` (`:54`) → `in-progress`.
- `schema/validator.md`, `schema/wiki-compiler.md`, `raw/…`, bestehende Concept-Inhalte — **read-only** (AD-3). Story 3.1 mutiert selbst keine Concept-Inhalte (Instruktions-Story); Demo-/Nachweis-Läufe laufen im Sandbox-Baum oder gegen den Ist-Baum ohne Inhalts-Mutation (Diff-Selbsttest + Validator-Verdikt).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — §3-Pkt.-2-Hold → Update-Routing; Candidate-Erhebung; neue §5.9 (Mutationsmechanik, log.md, Diff-Selbsttest, R-1-Change-Detection, P2-Check-Block); §0-Präzisierung; §7-Vorbehalt kürzen; §8-Revision 2.4. Kein Vertrag-/Validator-/`raw/`-Change.
- [x] `wiki/log.md` — Story-3.1-Eintrag (Vertrag §5-Format) mit Update-Semantik, Erhaltungs-Invariante + Diff-Selbsttest-Beleg, Statuswechsel, Validator-Verdikt.
- [x] `deferred-work.md` — R-1-/P2-Einträge mit „aufgegriffen (Story 3.1)"-Statusnachführung markieren (append-only); `sprint-status.yaml` — Key `3-1-…``in-progress`.
- [x] Edge-Tests im Sandbox-Baum (I/O-Matrix): HAPPY_PATH_UPDATE, UNTOUCHED_CONCEPT (kein Diff), CONCEPT_COLLISION_BESTEHEND (kein Duplikat), CHANGE_DETECTION (nur Zuwachs), PRE_RUN_RECONCILE.
**Acceptance Criteria:**
- Given ein bestehendes Bundle und neues Source Material, when ein Compilation Run startet, then folgt er dem inkrementellen Datenfluss Interpret → Reconcile → Synthesize → Update affected Concepts (AD-5, A0-6) — instruktionsseitig in `schema/compiler.md` (§3 + neue §5.9) verankert.
- Given ein Run, when er einen bestehenden Concept-Pfad nicht betrifft, then bleibt dessen Inhalt unverändert erhalten — keine Regeneration (FT-6, FR-12); belegt durch den re-executierbaren Diff-Selbsttest (nur betroffene Dateien + `log.md` + Index im `git diff --stat`).
- Given ein Lauf, when er abgeschlossen ist, then konzentrieren sich die Git-Änderungen auf durch die neue Erkenntnis betroffene Concepts (AD-5, FR-6) — kein Ghost-Diff, kein Duplikat bestehender Concept-Pfade.
- Given der Run-Input, when verarbeitet, then verwendet er ausschließlich published/committed Inhalte und nie Zwischenstände während der Mutation (AD-17.2/AD-17a) — bestehende §1.1-Regel bleibt und wird referenziert.
- Given die Instruktion, when geprüft, then bleibt `schema/validator.md`/`schema/wiki-compiler.md`/`raw/` unverändert (AD-3), es gibt keine neue §7-Klasse und kein Standalone (D-3) — der Validator läuft auf dem Ist-Bundle SUCCESS (per-Datei-Verdikt-Nachweis in `wiki/log.md`).
### Review Findings
bmad-code-review 2026-08-19 (Loop 2, 4 Layer: blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor; Diff-Baseline `802a557``efc543c`). Verifiziert am Ist-Baum: `grep "Aktualisierung ist Epic 3" schema/compiler.md` → kein Treffer (Rev-2.4.1-Claim korrekt); `rg -l 'FR-12' wiki/``wiki/knowledge-kompilation-inkrementell.md` + `wiki/log.md` (Worked-Example-Ziel `wissensarchitektur/source-material.md` NICHT getroffen).
**Decision-needed (alle 2026-08-19 per Nutzer-Entscheidung 1/1/1/1 aufgelöst — jeweils empfohlene Option gewählt):**
- [x] [Review][Decision] D-1: Diff-Selbsttest-Probe neu definieren — `git diff --name-only -- wiki/` ist blind für (a) ungetrackte neue Dateien (Duplikat-Verstoß unsichtbar), (b) den Zustand nach dem Commit (leere Ausgabe = vacuous Test, Commit-Boundary = Mutations-Boundary AD-17f), und die erlaubte Menge enthält keine Neu-Anlage-Zielpfade (Misch-Runs → jede Neu-Anlage fälschlich Ghost-Diff; §5.9 Pkt. 5 vs. §3 Pkt. 2 „betroffene bestehende Concepts"). Empfohlene Lösung (P1): Probe = `git diff --name-only <Baseline-Commit> -- wiki/` + `git status --porcelain -- wiki/`, fixiert VOR dem Commit (im P2-Block), erlaubte Menge = Kandidatenliste Neu-Anlage-Zielpfade (§5.1/§5.7) `log.md` `index.md`; Ausgabe-Pfade normalisieren (Stripping `wiki/`-Präfix + `.md`). **Resolution (Nutzer 1/1, 2026-08-19, Option 1 — empfohlen):** wie Empfohlene Lösung umgesetzt (Baseline + porcelain + Neu-Anlage + Normalisierung, Pre-Commit-Zeitpunkt).
- [x] [Review][Decision] D-2: R-1-Baseline deterministisch auflösbar machen + Abweichungsregel — „HEAD der vorherigen Mutations-Boundary" (Rev-2.4.1-F12) ist ohne Lauf-State-Record nicht deterministisch feststellbar (AD-14: Git liefert Historie, nicht Domain-State; `log.md` ist Dokumentation, kein State); der Claim „derselbe Baseline-Commit wie Pkt. 5" ist faktisch falsch (Pkt.-5-Probe hat kein Commit-Argument); bei git-diff ↔ SHA-256-Record-Diskrepanz ist die Ergebnis-Regel undefiniert. Empfohlene Lösung (P2): Baseline-Commit wird vom Producer im `wiki/log.md`-Run-Eintrag notiert; bei Diskrepanz gewinnt `git diff` (Commit-Boundary = Mutations-Boundary), SHA-256 bleibt Sekundär-Fingerprint; Fallback für Workspace ohne vorherige Mutations-Boundary definieren (alles als Zuwachs). **Resolution (Nutzer 1/1, 2026-08-19, Option 1 — empfohlen):** Baseline-Commit wird vom Producer im `wiki/log.md`-Run-Eintrag notiert; bei Diskrepanz gewinnt `git diff`; SHA-256 = Sekundär-Fingerprint; Fallback „alles als Zuwachs".
- [x] [Review][Decision] D-3: INPUT_UNCOMMITTED-Abbruch implementieren + Anker-Divergenz dokumentieren — das gefrorene I/O-Matrix-Szenario verlangt den textuell benannten Abbruch „published/committed Input erforderlich"; implementiert ist nur die Referenz auf §1 Pkt. 1 (keine Zustandsprüfung, keine Abbruchmeldung, kein Test, kein Defer). Die gefrorene Spec zitiert 3× „§1.1" (Zeilen 26/42/74), das in `schema/compiler.md` nicht existiert (§1 ist eine nummerierte Liste Pkt. 14) — Rev-2.4.1 korrigierte nur die Anker in compiler.md. Empfohlene Lösung (P3): Input-Zustandsprüfung (Working-Copy vs. HEAD für `raw/` + `wiki/`) als P2-Check-Block-Element mit genanntem Abbruch; die Spec↔Anker-Divergenz als dokumentierte Fußnote (Rev-Log + log.md), da die Spec frozen bleibt. **Resolution (Nutzer 1/1, 2026-08-19, Option 1 — empfohlen):** Input-Zustandsprüfung (Working-Copy vs. HEAD für `raw/` + `wiki/`) als P2-Check-Block-Element mit genanntem Abbruch; Anker-Divergenz als dokumentierte Fußnote (Rev-Log + log.md).
- [x] [Review][Decision] D-4: Sandbox-Edge-Test-Nachweis als hängenden Zeiger heilen — `wiki/log.md:4` und `deferred-work.md` behaupten „deterministische Formel-Ausgaben je Szenario in der Spec-Verification", aber die `## Verification`-Sektion der Spec enthält keine Szenario-Ausgaben (keine Sandbox-Bäume, keine Befehle, keine ausgeführten Outputs; repo-weit existiert kein Execution-Record für die fünf Szenarionyme, nur frozen Matrix + Tasks + Prosa). Repo-Idiom (spec-2-1 „Verification Record (Ausführungs-Beleg)", spec-2-5 Pkt. 1) liefert die Vorlage. Empfohlene Lösung (P4): fünf synthetische Sandbox-Bäume (HAPPY_PATH_UPDATE, UNTOUCHED_CONCEPT, CONCEPT_COLLISION_BESTEHEND, CHANGE_DETECTION, PRE_RUN_RECONCILE) ausführen, echte Ausgaben als „Verification Record (Ausführungs-Beleg)" in die (nicht gefrorene) `## Verification` schreiben, `log.md`/`deferred-work.md`-Zeile darauf zeigen. **Resolution (Nutzer 1/1, 2026-08-19, Option 1 — empfohlen):** volle Sandbox-Execution der fünf I/O-Matrix-Szenarien; echte Ausgaben als „Verification Record (Ausführungs-Beleg)" in die nicht gefrorene `## Verification`.
**Patch:**
- [x] [Review][Patch] Worked Example §5.9 Pkt. 7 nicht reproduzierbar: `rg -l 'FR-12' wiki/` trifft am Ist-Baum `wiki/knowledge-kompilation-inkrementell.md` + `wiki/log.md`, NICHT `wiki/wissensarchitektur/source-material.md` (dort 0 Treffer) — das „an die reale Ist-Lage gebundene" Example reproduziert sich nicht [schema/compiler.md:259]
- [x] [Review][Patch] P2-Check-Block-Zeitpunkt widersprüchlich: „einmal pro Run führend an der Spitze erhoben", aber die Kandidatenliste entsteht erst im Reconcile — Block muss nach Reconcile, an der Spitze von Mutieren, durchlaufen werden [schema/compiler.md:258]
- [x] [Review][Patch] Stale-Anker „§3.2-Kollisionsprüfung" — §3 ist eine nummerierte Liste (Pkt. 1/2/3), §3.2 existiert nicht [schema/compiler.md:42]
- [x] [Review][Patch] Stale-Referenz „§3-Voraussetzungsprüfung" (P2-Block Pkt. 4 zitiert §3 Pkt. 3 und diese Pseudo-Sektion doppelnd) [schema/compiler.md:258]
- [x] [Review][Patch] §5.8 Pkt. 3: Zeiger „die in Pkt. 1 als offen notierte Punkt-11-Grenze" — die Lücken-Notiz stand in Pkt. 2 [schema/compiler.md:232]
- [x] [Review][Patch] Befehls-/Ausgabe-Inkonsistenz der Diff-Selbsttest-Belege: `wiki/log.md:4` (b) zitiert `git diff --stat -- wiki/` (Rev-2.4.1: „statt `--stat`" umgestellt), `deferred-work.md` notiert den `--name-only`-Befehl mit `--stat`-stiler Ausgabe („1 Insertion") [wiki/log.md:4; _bmad-output/implementation-artifacts/deferred-work.md:172]
- [x] [Review][Patch] Log-Eintrag dokumentiert „Revision 2.4", der Commit enthält 2.4 + 2.4.1; die Step-04-Review-Patch-Runde und `in-progress → review` sind nur implizit (sprint-status-Zweizeitstempel) [wiki/log.md:4]
- [x] [Review][Patch] F17-Defer-Eintrag: kein `source_spec`-Feld (Schemaabweichung zu allen anderen Einträgen), „Executive spiegelt" = undefinierter Akteur in D-3-Semantik, „Nutzer-Entscheidung 1/1 (Story 3.2)" referenziert eine nicht existierende Story (3.2 ist `backlog`) [_bmad-output/implementation-artifacts/deferred-work.md:177-179]
- [x] [Review][Patch] No-Op-Kandidat (Kandidat mit null substantiellem Delta) undefiniert: Mutation/`at`-Bump/`log.md`-Eintrag? — Spurious-Churn vs. verpasste Pflichteintrag; zusätzlich: Konsolidierungsreihenfolge nur für „vollständig unabhängige" Einheiten definiert (lexicografisch), partiell-abhängige haben keine Regel (AD-17h) [schema/compiler.md:241-248]
- [x] [Review][Patch] Update-Pfad-Rollback undefiniert: §5.3/§6.3 definieren Teilzustand-Rollback nur für neue Dateien („gelöscht, Index entfernt, log.md-Eintrag entfernt"); für modifizierte bestehende Concepts fehlt die Rücksetzregel (Validierungs-FAIL im Update-Pfad) [schema/compiler.md:265-266]
- [x] [Review][Patch] `generated.at` = Wanduhr-Zeitstempel kollidiert mit AD-17h-Determinismus (zwei unabhängige Runs, gleicher Input → verschiedene `at`-Werte → verschiedene Bundle-States); P1-Konvention heilt nur Drift innerhalb eines Runs — die Lücke ist weder in §5.9 noch in `deferred-work.md` als Gap benannt (Story-3.8-Anker) [schema/compiler.md:246]
- [x] [Review][Patch] Term-Ableitung für die Kandidatenerhebung (a) `rg -l '<konzeptterm>' wiki/` nicht deterministisch: welcher Term, welche Normalisierung, Case-Sensitivity, mehrere Terme pro Einheit? — ist Interpretationsentscheidung (genau das, was AD-13 für Auswahl ausschließt); Story-3.2-Vorbehalt ist nur für den „feinkörnigen Mechanismus" formuliert, nicht für die Term-Ableitung [schema/compiler.md:41]
- [x] [Review][Patch] Probe-Ausgabe-Pfade nicht normiert: `git diff --name-only` liefert `wiki/`-Präfix + `.md`; die Kandidatenliste ist definiert als „relative OKF-Pfade ohne `.md`" — die Teilmenge-Vergleichsoperation ist nicht definiert (Stripping-Regel fehlt) [schema/compiler.md:255]
- [x] [Review][Patch] Fallback fehlt: kein vorheriger Mutations-Boundary-Commit vorhanden (frischer Workspace) → `<Baseline-Commit>` für R-1 Change-Detection nicht auflösbar [schema/compiler.md:257]
- [x] [Review][Patch] Link-Following (Kandidatenerhebung (c)) ohne visited-set/Tiefen-Schranke — zyklische Concept-Links → unendliche Traversal / unbeschränkte Kandidatenmenge [schema/compiler.md:41]
- [x] [Review][Patch] Ghost-Diff-Rollback-Mechanik nicht spezifiziert: „rollt den Ghost-Diff zurück" ist Prozess-Beschreibung ohne Mechanismus (welcher Befehl? `git restore`? §5.3-Sequenz?); für D-3/AD-17h muss die Rücksetz-Aktion deterministisch beschreibbar sein [schema/compiler.md:255]
**Defer (vorbestehend, nicht von Story 3.1 verursacht):**
- [x] [Review][Defer] V-1-Cross-Referenz dreistufig (compiler §3 Pkt. 3 / Validator-Voraussetzungsprüfung / Vertrag §2) und nie explizit aufgelöst — bestehendes Referenz-Idiom der Instruktion [schema/compiler.md:258] — deferred, pre-existing
- [x] [Review][Defer] `epic-3-context.md` ist ein neues Artefakt außerhalb der Spec-Code-Map, nirgends referenziert/dokumentiert (log.md/deferred-work) — Kontext-Datei wird vom build-Verfahren erzeugt, keine Story-Inhalts-Mutation [_bmad-output/implementation-artifacts/epic-3-context.md] — deferred, pre-existing
## Spec Change Log
- **2026-08-19 (bmad-code-review Loop 2 — Resolution & Patch-Runde):** 4 decision-needed per Nutzer-Entscheidung 1/1/1/1 aufgelöst (D-1 Probe-Befestigung + Neu-Anlage + Normalisierung; D-2 Baseline-Commit-Notiz + Diskrepanz-Regel + Fallback; D-3 INPUT_UNCOMMITTED-Abbruch als P2-Element + Anker-Divergenz-Fußnote; D-4 Sandbox-Execution + Verification Record); 16 patch umgesetzt — `schema/compiler.md`**Revision 2.4.2** (Diff-Selbsttest operationalisiert, R-1-Baseline + Abweichungsregel, P2-Block um Input-Zustand + Zeitpunkt, No-Op-Kandidat, Mehrfach-Treffer-Reihenfolge, Update-Pfad-Rollback §5.3, AD-17h-Gap benannt, Term-Ableitung abgegrenzt, visited-set, Worked Example korrigiert, stale-Anker nachgeführt) + `wiki/log.md` (2026-08-19-Eintrag: Befund-Befehlsform korrigiert, Rev 2.4.1/2.4.2 + Review-Handoff dokumentiert, Sandbox-Zeiger auflösbar, Baseline-Commit-Konvention) + `deferred-work.md` (F17-Eintrag: `source_spec` + Akteurskorrektur + Story-3.2-Referenz behoben; VG-Beleg: Befehls-/Ausgabe-Form korrigiert, Zeiger auf diesen Verification Record); 2 defer in `deferred-work.md` verankert; 6 dismissed (s. Review Findings-Einleitung); `## Verification Record (Ausführungs-Beleg)` angehängt (re-executierbares Sandbox-Skript + echte Outputs, S1S5 I/O-Matrix + S6 D-3-Kontrolle); Frontmatter `review_loop_iteration` → 2; `sprint-status.yaml`: `3-1-…``done`, `last_updated` 08-19-2026.
- **2026-08-19 (bmad-code-review Loop 2):** Review Findings-Sektion angehängt (4 decision-needed, 16 patch, 2 defer, 6 dismissed als Noise — u. a.: Key-Abkürzungs-Muster in sprint-status ist Projekt-Schema, Chronologie „Review-Ergebnis im selben Commit wie Review-Handoff" ist prozedural korrekt, Validator-7/7-Lauf gilt für Endzustand inkl. Append, AA-Teilbehauptung „Hold-String überlebt in Rev-1.4-Log" ist am Ist-Baum widerlegt — grep liefert keinen Treffer).
- **2026-08-18 (Erstellung):** Initiale Approve-Baseline.
## Design Notes
**Warum §5.9 als eigene Sektion statt Umbau von §5:** Die 5.x-Sektionen sind die nachgelagerten Spezifikations-Ebenen der Mutationsphase (§5.5 Claim-Provenienz, §5.6 Links, §5.7 Bereichszuordnung, §5.8 Discovery). Das inkrementelle Update ist die nächste Spezifikations-Ebene desselben §5 (Mutieren) — konsistent als §5.9, verortet nach §5.8 vor §6. Der bestehende §3-Reconcile wird nicht umgeschrieben, sondern seine Pkt.-2-Ausnahme („Aktualisierung ist Epic 3") durch das Update-Routing ersetzt — der Kollisions-Hold-Teil („nicht stumm überschreiben") bleibt als Schutzprinzip erhalten und wird lediglich um den Update-Ausweg ergänzt.
**Erhaltungs-Invariante als Diff-Selbsttest:** Die Inkrementalität ist nur dann AD-17h-fest belegbar, wenn sie mechanisch kontrollierbar ist. Konvention: Nach jedem Run prüft der Producer über `git diff --stat -- wiki/` (bzw. `git status --porcelain`), dass die geänderten Dateien ⊆ (betroffene Concepts `log.md` nachgeführte `index.md`) sind. Eine Abweichung (Ghost-Diff) ist ein Instruktions-Verstoß (FT-6) und wird textuell benannt. Für Story 3.1 selbst (Instruktions-Story ohne Inhalts-Mutation) ist der Nachweis: Ist-Baum zeigt keinen neuen Ghost-Diff nach dem Instruktions-Ergänzungs-Commit.
**`generated.at`-Konvention bei Updates (A0-20):** Ein maschinelles Update eines maschinell erzeugten Concepts bleibt maschinell → `generated` bleibt gesetzt, `at` wird auf den aktuellen Run-Zeitstempel aktualisiert (einmalige `at`-Festlegung pro Run, Dryrun-P1-Konvention). `verified` wird durch ein maschinelles Update **nicht** gesetzt und ein vorhandenes human-`verified` wird **nicht** entfernt (menschliche Kuratierung ist Bestandswissen, A0-21/FR-13-Nähe). Ein menschliches Update ändert `generated`/`verified` nicht automatisch.
**Defer-Bausteine inside der Run-Vorphase:** Defer R-1 (Change-Detection) beantwortet „welche `raw/`-Dateien sind Zuwachs" — als textueller Check-Block vor der Interpretation (git-diff-basiert und/oder SHA-256-Record aus `source.md`); Defer P2 bündelt die bestehenden textuellen Vorprüfungen (Zielpfad, Quellen-Existenz EC-1 via Validator-Punkt, Reconcile-Kandidaten-Erhebung, V-1) zu einem wiederverwendbaren Pre-Run-Reconcile-Check-Block. Beide sind **keine** neuen Prozesse — nur reproduzierbare Check-Blöcke innerhalb der Instruktions-Ausführung (D-3).
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. **Instruktions-Integrität:** `grep -n "5.9" schema/compiler.md` liefert die neue Sektion; `grep -n "Aktualisierung ist Epic 3" schema/compiler.md` liefert **keinen** Treffer mehr (Hold durch Update-Routing ersetzt); §7-Vorbehalt enthält „Update" nicht mehr.
2. **Diff-Selbsttest (Erhaltungs-Invariante):** vor dem Story-Commit den Ist-Baum-Status aufzeichnen, nach dem Commit `git diff --stat -- wiki/` prüfen — erwartet: nur die von Story 3.1 berührten Dateien (`log.md`, ggf. `index.md`) bzw. nach dem Instruktions-Commit der Story selbst kein weiterer Ghost-Diff; auf dem Sandbox-Baum: HAPPY_PATH_UPDATE zeigt ausschließlich betroffene Datei + log.md + Index.
3. **Validator-Lauf:** alle `wiki/`-Dateien SUCCESS (unverändert zur Story 2.5; der Validator ist eine reine Text-Instruktion, human-mechanisch ausgeführt, D-3 — es gibt keinen CLI-Invoker); das per-Datei-Verdikt wird als Ausführungs-Nachweis im `log.md`-Eintrag geführt.
**Manual checks:**
- §3-Pkt.-2-Hold ersetzt (Update-Routing) und §5.9 vorhanden (Update-Semantik, Diff-Selbsttest, R-1/P2-Bausteine); `compiler.md` §8-Revision 2.4 mit Abschlussklausel; kein `schema/validator.md`-/`schema/wiki-compiler.md`-/`raw/`-Diff; §7-Vorbehalt gekürzt; `wiki/log.md`-Eintrag datiert mit Story-3.1-Semantik, Diff-Selbsttest-Beleg, Statuswechsel `backlog → in-progress`, per-Datei-Verdikt; `deferred-work.md`-R-1-/P2-Einträge als aufgegriffen markiert (append-only); `sprint-status.yaml` konsistent (`3-1-…` → in-progress).
## Verification Record (Ausführungs-Beleg, 2026-08-19 — bmad-code-review Loop 2, D-4-Resolution)
**Re-executierbar:** `bash _bmad-output/implementation-artifacts/sandbox-3-1/run-sandbox.sh` (ab Workspace-Root; Git-Bash; Sandbox-Bäume unter `/tmp/sb31-XXXXXX`, reiner Test-Baum — nie der reale `wiki/`-Bundle; kein Carry-over über Szenarien via `isolate()`: Branch auf Baseline + `git reset --hard` + `git clean`). Das Skript setzt das compiler.md-§5.9-Update-Szenario auf einem synthetischen Basis-Baum (`wiki/index.md` + `alpha.md` + `beta.md` + `log.md`, `raw/alpha-v1.md` + `raw/beta-v1.md`, committer Baseline) gegen die fünf I/O-Matrix-Szenarien + die D-3-Abbruch-Kontrolle durch. Die Probe-Funktion implementiert die D-1-Probe wörtlich: `{ git diff --name-only <BASE> -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}' }` → Normalisierung (Strip `wiki/`-Präfix + `.md`-Suffix, `LC_ALL=C sort -u`).
**Baseline-Commit (Sandbox-Baum, reiner Test-Baum):** `f7335a3f4f98aafcbb1294bc8a4ba5ca76db143a`
**S1 — HAPPY_PATH_UPDATE** (Zuwachs `raw/alpha-v2.md` mit Term-Ueberschneidung `quanten-protocol-schlüssel`; Mutation im bestehenden Pfad `wiki/alpha.md` + `log.md`):
```
--- Kandidaten-Erhebung: rg -l 'quanten-protocol-schlüssel' wiki/ ---
wiki/alpha.md
--- Probe (git diff --name-only <BASE> -- wiki/ + git status --porcelain -- wiki/, normalisiert) ---
alpha
log
--- Erwartet: log, alpha (betroffen); beta NICHT; keine neue Datei (porcelain o.??) ---
M wiki/alpha.md
M wiki/log.md
--- Duplikat-Check: existiert alpha.md weiterhin exakt 1x? ---
wiki/alpha.md
0
0 untracked
```
→ Probe ⊆ erlaubte Menge (`alpha` betroffen, `log` = `log.md`; `beta` NICHT; keine ungetrackte Datei = kein Duplikat). FT-6/FR-12 erfüllt.
**S2 — UNTOUCHED_CONCEPT** (Zuwachs `raw/gamma.md`, Term `gamma-observatorium-thema`; kein Kandidat, keine Mutation):
```
--- Kandidaten-Erhebung: rg -l 'gamma-observatorium-thema' wiki/ ---
(leer — kein Kandidat)
--- Folge: keine Mutation, kein log.md-Zusatz (UNTOUCHED_CONCEPT) ---
--- Probe ---
(leere Ausgabe = kein Ghost-Diff; leere Menge ist Teilmenge jeder erlaubten Menge)
```
→ Konzept bleibt unverändert (keine Dateiänderung, kein Index-, kein `log.md`-Zusatz).
**S3 — CONCEPT_COLLISION_BESTEHEND** (Zielpfad `wiki/alpha.md` belegt → Update-Routing):
```
--- Vor-Mutation-Check: Ziel-Pfad wiki/alpha.md belegt? ---
JA — Update-Routing (kein Duplikat, kein stummer Ueberschreiben)
--- Vor-Mutation-Zustand (muss leer sein) ---
(leer)
--- Mutation im bestehenden Pfad (Update) ---
--- Nach-Mutation: nur M-Eintraege, KEIN ?? (keine neue Datei = kein Duplikat) ---
M wiki/alpha.md
M wiki/log.md
--- Probe ---
alpha
log
```
→ Statt Kollision-Hold: Mutation im bestehenden Pfad; nur `M`-Einträge, kein `??` (keine neue Datei = kein Duplikat); Index-Link unverändert gültig.
**S4 — CHANGE_DETECTION (R-1)** (Zuwachs `raw/alpha-v2.md`; `alpha-v1.md`/`beta-v1.md` unverändert):
```
--- git diff --name-only <BASE> -- raw/ ---
raw/alpha-v2.md
--- Erwartet: AUSSCHLIESSLICH raw/alpha-v2.md (alpha-v1.md/beta-v1.md bleiben aussen) ---
--- SHA-256-Abgleich (Sekundaer-Fingerprint, D-2): alpha-v1 unveraendert? ---
21e7fc1dd09baa0d3fed56bf36e7eba19b1b3376f236b79ecbce8a907761b50b *raw/alpha-v1.md
21e7fc1dd09baa0d3fed56bf36e7eba19b1b3376f236b79ecbce8a907761b50b *-
--- (identische Summen = unveraendert; bei Diskrepanz gewinnt git diff, D-2) ---
```
→ Nur die neue Datei wird als Zuwachs interpretiert (git-diff-basiert); unveränderte bleiben außen vor; SHA-256-Record bestätigt (identische Summen), Sekundär-Fingerprint gemäß D-2-Diskrepanz-Regel.
**S5 — PRE_RUN_RECONCILE (P2)** (Setup: `wiki/index.md` fehlt → fehlende Bundleroot):
```
--- Check-Block: (4) wiki/index.md-V-1-Vorbedingung ---
Run-FAIL (V-1, Vertrag §2): wiki/index.md fehlt — keine Mutation darf erfolgen
--- Zustand nach Abbruch + Rueckstellung (muss leer sein — der Run selbst mutierte nichts) ---
(leer)
```
→ Pre-Run-Reconcile-Check-Block durchlaufen **vor jeder Mutation**; fehlende Bundleroot → Run-FAIL (V-1, besteht fort); der Run mutierte nichts (Zustand nach Abbruch leer; die Rückstellung ist Setup, kein Run-Zustand).
**S6 — INPUT_UNCOMMITTED (D-3-Abbruch-Kontrolle)** (Setup: `raw/alpha-v1.md` im Working-Copy abweichend von HEAD):
```
--- Check-Block: Working-Copy vs. HEAD fuer raw/ + wiki/ ---
Abbruch: published/committed Input erforderlich (AD-17a) — ungepublishter Zustand:
M raw/alpha-v1.md
```
→ P2-Check-Block-Element (1) **Input-Zustand**: Working-Copy von `raw/`+`wiki/` gegen HEAD; bei Abweichung **benannter Abbruch „published/committed Input erforderlich"** vor Interpretation und vor jeder Mutation — die gefrorene I/O-Matrix-Verpflichtung (Szenario `INPUT_UNCOMMITTED`) ist jetzt als Check-Block-Element in `schema/compiler.md` §5.9 Pkt. 6 (1) implementiert (Rev 2.4.2, D-3-Resolution).
**Gesamtergebnis:** Alle 5 I/O-Matrix-Szenarien + die D-3-Abbruch-Kontrolle deterministisch ausgeführt — keine Abweichung vom Erwartungsverhalten; die `wiki/log.md`- und `deferred-work.md`-Verweise auf „deterministische Formel-Ausgaben/Nachweise je Szenario in der Spec-Verification" sind damit auflösbar (D-4).
## Suggested Review Order
**Inkrementeller Datenfluss — Update-Routing (Einstieg)**
- §3 Reconcile: Kollision-Hold durch Update-Routing ersetzt — der architektonische Kern der Story
[`compiler.md:40`](../../schema/compiler.md#L40)
- Textuell-deterministische Kandidatenliste (AD-13) als Update-Stimulus
[`compiler.md:41`](../../schema/compiler.md#L41)
- §0-Aufruf: sechs Phasen für Neu-Anlage- und Update-Variante präzisiert
[`compiler.md:13`](../../schema/compiler.md#L13)
**§5.9 Update-Mutationsmechanik (neue Sektion)**
- §5.9-Überblick: einziger Instruktions-Ort der Update-Mutationsmechanik (D-3)
[`compiler.md:236`](../../schema/compiler.md#L236)
- Update-Stimulus: Kandidatenliste, published/committed-Input (§1 Pkt. 1, AD-17a)
[`compiler.md:240`](../../schema/compiler.md#L240)
- Mutationsmechanik: Erweitern/Präzisieren/Korrigieren + Mehrfach-Treffer-Konsolidierung
[`compiler.md:242`](../../schema/compiler.md#L242)
- Index-/Link-Form unverändert (§5.6-Pin); `log.md`-Eintragspflicht bleibt davon unberührt
[`compiler.md:249`](../../schema/compiler.md#L249)
- Worked Example an den realen Ist-Baum gebunden (FR-12, kein Ghost-Diff)
[`compiler.md:259`](../../schema/compiler.md#L259)
**Erhaltungs-Invariante & Diff-Selbsttest (Review-Patch-Kern)**
- Diff-Selbsttest operationalisiert: `git diff --name-only -- wiki/` ⊆ Kandidatenliste log.md Index
[`compiler.md:251`](../../schema/compiler.md#L251)
- Run-Vorphase-Bausteine: Change-Detection (R-1, Baseline = HEAD der vorherigen Mutations-Boundary) + Pre-Run-Reconcile-Check-Block (P2)
[`compiler.md:255`](../../schema/compiler.md#L255)
- Revisionslog 2.4.1: Review-Patch-Runde mit Abschlussklausel (AD-3/D-3 gehalten)
[`compiler.md:348`](../../schema/compiler.md#L348)
**Referenz-Abgrenzung §5.7/§5.8**
- §5.7 Pkt. 3: Top-Level-Kollision → Update-Routing, kein MOVE
[`compiler.md:199`](../../schema/compiler.md#L199)
- §5.8 Pkt. 2: Rev-9-Punkt-11-Area-Lesart als geschlossene Lücke dokumentiert
[`compiler.md:231`](../../schema/compiler.md#L231)
- §5.8 Pkt. 3: §5.8-Hold bewusst nicht das Update-Routing (Zwei-Ebenen-Kartografie)
[`compiler.md:232`](../../schema/compiler.md#L232)
**Nachweise (Log, Deferred-Work, Sprint-Status)**
- `wiki/log.md`: Story-3.1-Nachweis mit Diff-Selbsttest-Beleg und Rev-9-Präzisierung
[`log.md:4`](../../wiki/log.md#L4)
- `deferred-work.md`: R-1 (Change-Detection) und P2 (Pre-Run-Reconcile) als aufgegriffen markiert
[`deferred-work.md:129`](deferred-work.md#L129)
- Review-Findings-Protokoll (VG-Nachweis, F9/F12, F17-Defer als Folge-Aufgabe)
[`deferred-work.md:164`](deferred-work.md#L164)
- `sprint-status.yaml`: Story 3.1 `in-progress` (Sprint-Sync konsistent)
[`sprint-status.yaml:54`](sprint-status.yaml#L54)
@@ -0,0 +1,140 @@
---
title: 'Relevanzbestimmung textual-deterministisch umsetzen (grep/ripgrep + Markdown-Traversal + Link-Following) (Story 3.2)'
type: 'feature'
created: '2026-08-19'
status: 'done'
review_loop_iteration: 3
baseline_commit: e3e7ec346df3e6190644d2c94e5cc7c42e6ed239
context:
- _bmad-output/implementation-artifacts/epic-3-context.md
---
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** Die Compiler-Instruktion (`schema/compiler.md` §3 Pkt. 2, Revision 2.4.2) erhebt die Update-Kandidatenliste bisher über einen **platzgehaltenen `<konzeptterm>`** („die Term-Auswahl folgt der §2-Interpretation"), dessen Ableitung (Kanonisierung, Synonyme, Mehrfach-Terme je Einheit) explizit „Story 3.2 vorbehalten" ist (`compiler.md:41`; §7 `:319`). Damit ist die Relevanzbestimmung nicht vollständig deterministisch vorgegeben (AD-17h/A0-19): dieselbe Evidenz kann je nach Term-Wahl unterschiedliche Candidate-Listen erzeugen — ein Embedding-freier, textuell-deterministischer Mechanismus fehlt (PRD OQ-3, AD-13, A0-18).
**Approach:** Story 3.2 löst die beiden Story-3.2-Vorbehalte auf und verankert in `schema/compiler.md` (D-3, rein textuell — kein Code, kein Standalone) den **feinkörnigen, textuell-deterministischen Relevanz-Findungsmechanismus** als verbindliche Ausformulierung der §3-Pkt.-2-Kandidatenerhebung: (a) **Term-Ziehverfahren** — die Candidate-Terme werden deterministisch aus der neuen Evidenz abgeleitet (bedeutungstragende Token-Folgen nach §2-Interpretation, durchgängige Normalisierung: lowercasing, `[-_ ]`-Bindestrich-Varianten, ein kanonischer Schreibweisen-Resolver, mehrere Terme je Einheit erlaubt); (b) **Term-übergreifende Erhebung** über `wiki/` — grep/ripgrep, `index.md`-Traversal, Link-Following (dreistufig, mündet in die nachvollziehbare Candidate-Liste als relative OKF-Pfade ohne `.md`); (c) **Determinismus-Vertrag** — gleicher Git-State + gleiche Eingabemenge → gleiche Candidate-Liste in gleicher Reihenfolge (AD-17h/A0-19). Damit werden die Story-3.2-Vorbehalte in §3 Pkt. 2 und §7 aufgehoben. Vertrag (`schema/wiki-compiler.md`), Validator (`schema/validator.md`) und `raw/` bleiben read-only (AD-3); keine neue §7-Invaliditätsklasse.
## Boundaries & Constraints
**Always:**
- **Story 3.2 ist eine Instruktions-Story (D-3).** Der Relevanz-Findungsmechanismus wird ausschließlich in `schema/compiler.md` als deterministische Text-Instruktion verankert. Kein ausführbares Programm, kein Standalone, keine neue §7-Invaliditätsklasse, kein Change an `schema/wiki-compiler.md` / `schema/validator.md` / `raw/` (AD-3).
- **Determinismus (Kern, AD-17h/A0-19):** Die Relevanzbestimmung ist **deterministisch**: gleicher Git-State + gleiche Eingabemenge → identische Candidate-Liste, in identischer Reihenfolge (Zuwachs-Sicht-Ordnung; lexikografisch als deterministischer Tie-Break bei identischem Ort). Damit ist sie als nachvollziehbare Candidate-Liste (Concept-Pfade) verfügbar — deterministisch statt probabilistisch (AC-3).
- **Ausschließlich textuelle Mittel (AD-13, AC-1/AC-4, No-Goals):** Die Erhebung nutzt ausschließlich grep/ripgrep über `wiki/`, Markdown-Traversal von `index.md` und Link-Following (§5.6-Pin). **Kein** Einsatz von Embeddings, Vektor-Suche oder Knowledge-Graph-Datenbank im Compiler-Kern.
- **Kein Leasing-/Dirty-Tree-Scope in 3.2:** Leasing, Dirty-Tree, Staleness, Merge-Kollisionen bleiben Story 3.5/3.6; Commit-Boundary = Mutations-Boundary-Regel (AD-17f) und die Story-3.1-Bausteine (§5.9 Diff-Selbsttest, R-1/P2-Check-Block, INPUT_UNCOMMITTED-Abbruch, Update-Routing) bleiben unverändert — Story 3.2 ändert nur die **Erhebungs-Mechanik** der Candidate-Liste, nicht die Mutationspfade.
- `sprint-status.yaml`: Key `3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip`**in-progress**.
**Ask First:** Einführung nicht-textueller Mittel (Embeddings/Vector/KG) im Compiler-Kern (verboten durch AD-13, nur per Autorisierung änderbar) · Änderung der §5.6-Linkform (verboten durch A0-9) · AD-7d-Renames/Redirects · Validator-/Vertrags-/`raw/`-Change · Änderung der Commit-Boundary-Regel.
**Never:** Änderungen an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3) · neue §7-Invaliditätsklasse · Standalone-Programm/Validator (D-3) · Embedding/Vector-Suche/KG im Compiler-Kern (AD-13, No-Goals) · Duplikat-Anlage eines bestehenden Concept-Pfads · „Regenerate Everything" (AD-5, A0-6).
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| TERM_ABLEITUNG_SINGLETON | Neue Evidenz `raw/…` mit genau einem markanten Fachbegriff (z. B. `quanten-protocol-schlüssel`) | Term-Ziehverfahren ergibt genau diesen Begriff; grep-Erhebung über `wiki/` findet genau die Concept-Pfade mit diesem Term | N/A |
| TERM_ABLEITUNG_SYNONYM | Evidenz nennt einen Begriff in einer Schreibweise-Variante (z. B. `quanten protocol` vs. canonisch `quanten-protocol`) | Kanonisierungs-Resolver normalisiert auf die canonische Schreibweise; Erhebung findet die Concept-Pfade der canonischen Form | keine canonische Auflösung → Term wird wie notiert verwendet (kein stiller Ausschluss) |
| TERM_ABLEITUNG_MEHRFACH | Evidenz enthält mehrere bedeutungstragende Terme | Candidate-Liste = Vereinigung der Treffer über alle Terme; Vereinheitlichung über besuchte Menge (ein Pfad nur einmal) | N/A |
| TRAVERSAL_REACH_ONLY | Ein Konzeptterm trifft nur `wiki/index.md` (oder Area-`index.md`), nicht Concept-Bodies | `index.md`-Traversal (Root → Area → Concept, §5.8) liefert die darunter gewurzelten Concept-Pfade als Treffer | fehlende Bundleroot → Run-FAIL (V-1, besteht fort) |
| LINK_FOLLOWING_ZYKLUS | Concept-Links bilden einen Zyklus (A → B → A) | Link-Following mit **besuchter Menge** (aus Story 3.1): jeder Pfad nur einmal — keine Endlos-Schleife, endliche Candidate-Liste | N/A |
| NO_MATCH | Neuer Term trifft kein bestehendes Concept (keine Überschneidung) | Leere Candidate-Liste → UNTOUCHED_CONCEPT (Story-3.1-Pfad): keine Mutation, kein `log.md`-Zusatz | leere Menge ist Teilmenge jeder erlaubten Menge (Ghost-Diff-negativ) |
</frozen-after-approval>
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3, einziger Instruktions-Ort):
- §3 Reconcile Pkt. 2 (`:41`): **Story-3.2-Vorbehalt auflösen** — der feinkörnige Term-/Relevanz-Mechanismus wird eingearbeitet (Term-Ziehverfahren, Kanonisierungs-Resolver, drei Erhebungs-Stufen grep/ripgrep + Traversal + Link-Following, Candidate-Liste als relative OKF-Pfade ohne `.md`, Determinismus-Beschreibung); verortet als **neue Sektion §3.2 „Relevanzbestimmung (Story 3.2)"** direkt nach §3 — Kern-Anker bleibt Pkt. 2 („Erhebung nach §3.2").
- §7 Selbstbegrenzung (`:319`): **Story-3.2-Vorbehalt entfernen** („Feinkörnige Relevanz-Verfeinerung" entlassen) — verbleibende 3.x-Themen: Synthese → 3.4, Leasing/Dirty-Tree → 3.5/3.6.
- §8 Revisionslog (`:350352`): **Revision 2.5** (Story 3.2) + **2.6** (Step-04-Loop-2-Patch-Runde) — Normreferenzen AD-13/A0-18/OQ-3 ergänzen (AD-17h steht bereits; **A0-19 wird ebenfalls ergänzt** — war im Ist-§8 noch nicht gelistet; auch `A0-19` als Determinismus-Referenz); Abschlussklausel (kein Vertrag-/Validator-/`raw/`-Change, keine neue §7-Klasse, kein Standalone). Rev 2.6 trägt zusätzlich die Loop-2-Patches (§4-Überschrift, rg-tool-korrekte Formen).
- §5.9 Pkt. 6 / P2-Check-Block: die Candidate-Erhebung über einen Zeiger (nicht Duplizieren) an den neuen §3.2-Mechanismus binden — **einschl. `--exclude=log.md`** (analog §5.6/§5.8-Formel), die Candidate-Liste ist auf Concept-Pfade definiert.
- **Resolver-Materialisierung:** der kanonische Schreibweisen-Resolver wird als **`canonical-terms.md` unter `schema/compiler.md` nebengeordnet** (neues Artefakt, gleiche read-only-Hierarchie): committete, append-only Registry (canonische Form + erlaubte Schreibvarianten + `[]`), als `§3.2`-Ziel referenziert. Damit ist der Resolver Bestandteil des Git-States und der Determinismus (gleicher Git-State → gleiche Liste) pinbar. (Kein `schema/`-Root-Change; Einbindung als normierte Referenz im §8.)
- §1 Pkt. 1/4, §5.6, §5.7, §5.8 — **read-only / nur Referenz** (Linkform, Bereichszuordnung, Discovery bleiben unverändert).
- `wiki/log.md`**append** (append-only, Vertrag §5: bestehende Bullets — Story-3.1-done unter 2026-08-19, Validierungs-Rev-9, Story 2.5… — **bleiben unverändert erhalten**; der Story-3.2-Eintrag wird als weitere Bullet ergänzt, nicht ersetzt): Relevanzbestimmung-Semantik, Determinismus-Selbsttest-Beleg (Membership + Identität; der `--exclude=log.md`-Fakt aus der Stufe (a) wird ehrlich dokumentiert — `A0-18` trifft nur noch `wiki/knowledge-kompilation-inkrementell.md`, nicht `log.md`), Statuswechsel `3-2-…` `backlog → in-progress`, per-Datei-Validator-Verdikt-Zeile (Ist-Baum SUCCESS), einzeilige Nennung des 3.1-`epic-3-context.md`-Defer-Handoffs.
- `_bmad-output/implementation-artifacts/deferred-work.md`**mutiert** (append-only): 3.1-Defer-Eintrag `epic-3-context.md` (`:335`, „Home: Story 3.2-Handoff oder Sprint-Sync") als aufgegriffen markieren; der F17-Handoff-Eintrag („Split 2026-08-19, Home: Story 3.8") ist bereits eingetragen. Bestehende Einträge nicht verändern, nur Status-Ergänzung im append-only-Stil.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: Key `3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip` (`:55`) → `in-progress`.
- `schema/validator.md`, `schema/wiki-compiler.md`, `raw/…`**read-only** (AD-3). Story 3.2 mutiert selbst keine Concept-Inhalte; Demonstration per Sandbox-Durchlauf (Muster: `_bmad-output/implementation-artifacts/sandbox-3-1/run-sandbox.sh`, S1S6) oder als textueller Selbsttest gegen den Ist-Baum (z. B. `rg -l`-Erhebung mit realem Term, ohne Inhalts-Mutation).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — Story-3.2-Vorbehalte auflösen (§3 Pkt. 2 + §7): §3.2-Relevanzbestimmung als verbindliche dreistufige Erhebungs-Mechanik (Term-Ziehverfahren + Kanonisierungs-Resolver; `log.md`-Exklusion — rg `-g '!log.md'` bzw. grep `--exclude=log.md` — über `wiki/`; `index.md`-Traversal; Link-Following mit besuchter Menge; Candidate-Liste als relative OKF-Pfade ohne `.md`; Determinismus-Vertrag AD-17h/A0-19 mit Reihenfolge auch für Stufe-b/c und Normalisierung in §3.2 selbst); **canonical-terms.md** (Resolver-Registry: canon. Form + Varianten, append-only); §8-Revision 2.5 + Normreferenzen AD-13/A0-18/A0-19/PRD-OQ-3; Abschlussklausel. Kein Vertrag-/Validator-/`raw/`-Change. _(Inkl. Rev-2.6-Patch-Runde aus Step-04-Review Loop 2: §4-Überschrift wiederhergestellt, rg-tool-korrekte Formen, Mini-Sandbox verankert — s. Spec Change Log.)_
- [x] `wiki/log.md` — Story-3.2-Eintrag (Vertrag §5-Format, append-only — bestehende Bullets unverändert) mit Relevanzbestimmung-Semantik, Determinismus-Selbsttest-Beleg (Membership + Identität via `log.md`-Exklusion), Statuswechsel, Validator-Verdikt, epic-3-context-Defer-Nennung. _(Loop-2-Patch-Note (Rev 2.6) als eigene oberste Log-Zeile ergänzt.)_
- [x] `deferred-work.md` — epic-3-context-Eintrag als aufgegriffen markieren (append-only); `sprint-status.yaml` — Key `3-2-…``in-progress`. _(Zusätzlich Em-Dash-Determinismus-Frage als neuer Defer (Home: Story 3.8) aus Loop 2.)_
- [x] Edge-Tests (Sandbox, I/O-Matrix + Membership-Pin): TERM_ABLEITUNG_SINGLETON/SYNONYM/MEHRFACH, TRAVERSAL_REACH_ONLY, LINK_FOLLOWING_ZYKLUS, NO_MATCH — inkl. mini-Sandbox-Baum, der einen realen Term auf Concept-Bodies **und** `log.md` verteilt und die exakte Candidate-Liste (nur Concept-Pfade, `log.md` ausgeschlossen) als erwartete Ausgabe pindet. _(Sandbox T1T4 re-executiert: alpha exklusiv, Zwei-Run-Identität, NO_MATCH leer, alpha+gamma Vereinigung.)_
**Acceptance Criteria:**
- Given eine neue committete Source, when ein Run die Relevanzbestimmung ausführt, then nutzt er ausschließlich textuelle, deterministische Verfahren — Term-übergreifende grep/ripgrep über `wiki/`, Markdown-Traversal von `index.md`, Link-Following (AC-1; AD-13, A0-18) — als verbindliche Ausformulierung der §3-Pkt.-2-Erhebung; die Story-3.2-Vorbehalte (§3 Pkt. 2, §7) sind aufgehoben.
- Given gleicher Git-State + gleiche Eingabemenge, when zwei unabhängige Runs die Relevanzbestimmung ausführen, then erzeugen sie dieselbe Candidate-Liste in derselben Reihenfolge (AC-2; AD-17h, A0-19) — ohne Embedding/Vector/KG im Compiler-Kern (AC-4; AD-13).
- Given ein Ergebnis der Relevanzbestimmung, when es weiterverarbeitet wird, then ist es als nachvollziehbare Candidate-Liste (relative OKF Concept-Pfade ohne `.md`) verfügbar (AC-3).
- Given die Instruktion, when geprüft, then bleibt `schema/validator.md`/`schema/wiki-compiler.md`/`raw/` unverändert (AD-3), keine neue §7-Klasse, kein Standalone (D-3) — Validator läuft auf dem Ist-Bundle SUCCESS.
### Review Findings
bmad-code-review Loop 3 (2026-08-19, 4 Layer: blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor; Diff `e3e7ec3 → 3aa484b`, branch `story-3-2`). Triage: 5 `decision-needed`, 2 `patch`, 4 `defer` (→ `deferred-work.md`), 39 dismissed als Noise (u. a. doppelte Layer-Quellen pro Finding; `last_updated`-Zeitstempel = Story-3.1-Loop-2-Präzedenz; `deferred-work.md`-Bestands-Eintrag-in-Platz-Edit = vom frozen-Task-3-Defer-Handoff selbst befohlen; Em-Dash-Lücke bereits korrekt defer an Story 3.8; `sprint-status`-Key-Kürzung = Projekt-Schema; Sandbox-/tmp-Reste + `git add -A;`-Semikolon = Story-3.1-Sandbox-Muster-Präzedenz; `F17`-Home-„mit Story 3.2" = vorbestehend (Story-3.1-Review); `rg`-Verfügbarkeit im Audit-Environment = Audit-Beschränkung, keine Story-Lücke; `§5.6-Scan-Scope-Konvention`-Anker ist real (`compiler.md:157`); Formel-4-Ist-Zählung `38` re-executiert = `38`; „Validator 7 wiki/-Dateien" = korrekt, `ls wiki/` liefert 6 Einträge = 7 `.md`-Dateien (Area-Verz. zählt nicht); `canonical-terms.md`-Zwei-`[]`-Lesarten sind durch die Eröffnungs-Zeile + den Hinweis explizit aufgelöst; `A0-19`-Ergänzung in §8 = spec Change Log B5 als KEEP verankert).
- [x] [Review][Decision] Status-Flip `backlog → done` gegen gefrorene Always-Klausel (`→ in-progress`) — keine dokumentierte Renegotiation [sprint-status.yaml:55] — **Option 1 umgesetzt:** `done` bestätigt; Statuskette `in-progress → done` als eigener dokumentierter Schritt in `wiki/log.md` (neue oberste Bullet, Loop-3/Rev-2.7-Zeile)
- Befund: Der gefrorene `Boundaries & Constraints`-Abschnitt (Always) verlangt `sprint-status.yaml`-Key → **`in-progress`**; der Commit setzt direkt **`done`**. Die nicht-gefrorene Spec-Tail (`Manual checks`) sagt zwar „`backlog → in-progress → done`", aber (a) der Zwischenschritt `in-progress` ist in keinem `wiki/log.md`-Eintrag als eigener dokumentierter Statuswechsel belegt (Rev-2.5-Log zitiert `backlog → in-progress`, Rev-2.6 zitiert keinen `done`-Flip), (b) der Review-Workflow-Skript-Sync (bmad-build `sprint status` + Story 3.1-Präzedenz: Review-Abschluss setzt `done` im selben Loop) legt nahe, dass `done` der intendierte Endzustand ist. Entweder wurde die Always-Klausel stillschweigend superseded (nicht dokumentiert) oder der Key ist falsch.
- Optionen: **1 (empfohlen)**`done` bestätigt (Sprint-Sync-Konvention: Review-Loop-Abschluss setzt `done`); in den Rev-2.6-Log-Zeile den Statusflip `in-progress → done` als dokumentierten Schritt ergänzen (Lückenschluss der Statuskette, keine Status-Änderung). **2** — Key auf `in-progress` zurücknehmen; Story bleibt `in-progress` bis ein späterer Run den `done`-Flip belegt.
- [x] [Review][Decision] Sandbox deckt I/O-Matrix-Szenarien `LINK_FOLLOWING_ZYKLUS`, `TRAVERSAL_REACH_ONLY`, `TERM_ABLEITUNG_SYNONYM` nicht ab — Task-4-Checkbox `[x]` überdeklariert [run-sandbox.sh:1-142] — **Option 1 umgesetzt:** T5/T6/T7 ergänzt, T2-Kommentar korrigiert, re-executiert (alle 7 Tests PASS, exit 0); Beleg in `wiki/log.md` (Loop-3-Zeile)
- Befund: `run-sandbox.sh` implementiert T1 (Membership), T2 (Zwei-Run-Identität), T3 (NO_MATCH), T4 (Vereinigung). `LINK_FOLLOWING_ZYKLUS` hat **keinen** Test (der Sandbox-Baum enthält keine Concept-Links — die besuchte Menge wird nie geübt); `TRAVERSAL_REACH_ONLY` ist nur Nebenprodukt von T2 (Term `alpha` trifft `index.md`; als Kommentar, nicht als gepinntes Szenario — und die gepinnte Ausgabe `index`+`alpha` widerspricht der §3.2-3b-Ordnungs-Semantik, weil Stufe-b-Treffer *nach* Stufe-a-Treffern positioniert sind und `index` kein Concept-Pfad ist); `TERM_ABLEITUNG_SYNONYM` ist ungetestet (leere Registry → der Fallback „wie notiert" ist der Standardfall, aber die Kanonisierung selbst wird nie geübt). Die Execution-Task-4-Checkbox `[x]` und Spec-Verification Pkt. 3 deklariert die volle I/O-Matrix-Abdeckung.
- Optionen: **1 (empfohlen)** — Sandbox um T5 (LINK_FOLLOWING_ZYKLUS: A→B→A-Links, erwartete endliche, doppelungsfreie Liste), T6 (TRAVERSAL_REACH_ONLY: Term nur in `index.md`, erwartete gewurzelte Concept-Pfade) und T7 (TERM_ABLEITUNG_SYNONYM: Registry-Eintrag mit Variante → Auflösung auf canonische Form) erweitern; T2-Kommentar zu `index` korrigieren; dann re-executieren und den Beleg nachführen. **2** — nur die fehlenden Szenarien als `deferred-work.md`-Eintrag an die nächste Compiler-Instruktions-Revision (oder Story 3.8, die den Determinismus-Vertrag absichert) übergeben; Task-4-Checkbox-Notiz ehrlich nachführen. **3** — T6/T7 als Defer, T5 (besuchte Menge) als Patch jetzt (weil §3.2-2c die Endlichkeits-Garantie trägt).
- [x] [Review][Decision] `wiki/log.md`-Selbsttest-Beleg (b) beschreibt einen Sandbox-Baum, der nicht dem committierten `run-sandbox.sh` entspricht (Term `quanten-protocol`, `sub/beta.md`, SHA-256 `159092bb…`) — Stale-Verifikations-Falle (exakt B1 aus Loop 2) [wiki/log.md:5] — **Option 1 umgesetzt:** Beleg (b) gegen die echte Sandbox-Ausgabe neu belegt (neue Log-Zeile, append-only); Rev-2.5-Zeile bleibt historisch
- Befund: Der Rev-2.5-Log-Eintrag (b) behauptet: Term `quanten-protocol` auf `wiki/alpha.md`, `wiki/sub/beta.md`, `log.md`; Candidate-Liste `alpha`, `sub/beta`; Zwei-Run-Identität via SHA-256 `159092bb…`. Das committierte, re-executierbare `run-sandbox.sh` trägt durchgängig den Term `deterministische-relevanz-bestimmung`, **keinen** `sub/`-Baum (Beta ist root-level `beta.md`) und **keine** SHA-256-Berechnung. Der „re-executierte" Nachweis ist damit nicht aus dem verankerten Artefakt reproduzierbar — dieselbe Stale-Evidenz-Falle, die Story 3.2 (Loop-2-B1) gerade schließen sollte.
- Optionen: **1 (empfohlen)** — Log-Beleg (b) gegen die echte `run-sandbox.sh`-Ausgabe re-executiert neu belegen (Term/Pfade/Ergebnisse an den Script-Ist-Baum angleichen); historischer Rev-2.5-Zeile eine Korrektur-Fußnote als neue Log-Zeile anhängen (append-only-Vertrag §5 erlaubt neue Zeilen, nicht Edit — die Rev-2.5-Zelle bleibt historisch, die neue Zeile korrigiert). **2** — die Sandbox auf den log.md-belegten Baum (Term `quanten-protocol`, `sub/`-Area) ändern und neu ausführen (weicht vom bereits in Rev-2.6 referenzierten Script ab).
- [x] [Review][Decision] `§3.2`-Pkt.-3b-Ordnung: „Reihenfolge des ziehenden Terms" ist undefiniert für Mehrfach-Terme — AD-17h-Zwei-Run-Identität hängt an einer nicht fixierten Term-Ordnung [schema/compiler.md:60] — **Option 1 umgesetzt:** Pkt. 3b auf reine Lexikografie (LC_ALL=C) gehoben; Term-Auftritt nur für Verarbeitungsreihenfolge (Rev-2.7)
- Befund: Pkt. 3b ordnet Stufe-a-Treffer „in der Reihenfolge des ziehenden Terms, dann lexikografisch als deterministischer Tie-Break bei identischem Ort", während Pkt. 1c mehrere Terme je Einheit erlaubt. Die Reihenfolge der **Terme selbst** (Auftreten in der Evidenz? lexikografisch? §2-Interpretations-Reihenfolge?) ist nirgends festgelegt — zwei rechtmäßige Term-Ordnungen erzeugen zwei rechtmäßige, aber unterschiedliche Listen. Zusätzlich ist die Formulierung intern doppelt definiert („Reihenfolge des ziehenden Terms" vs. „innerhalb jeder Stufe lexikografisch aufsteigend").
- Optionen: **1 (empfohlen)** — Pkt. 3b auf eine eindeutige Regel heben: Stufe-a-Treffer **rein lexikografisch (LC_ALL=C)**, Term-Auftritt dient nur der Verarbeitungs-/Interpretations-Reihenfolge, nicht der Listen-Ordnung (einfachste deterministische Form, kompatibel mit der „lexikografisch als Tie-Break"-Klausel). **2** — Term-Ordnung = lexikografisch über die gezogenen Terme, Stufe-a-Treffer in Term-Ordnung, dann lexikografisch (bewahrt die „Zuwachs-Sicht"-Semantik, ist etwas komplexer zu implementieren und zu beweisen).
- [x] [Review][Decision] `canonical-terms.md`-Registry-Lookup-Semantik ist unvollständig — Doppelbelegung von Varianten ist nicht deterministisch auflösbar, Lookup-Reihenfolge (vor/nach Kollaps, Groß/Klein) ist offen [schema/canonical-terms.md:25-35] — **Option 1 umgesetzt:** Lookup-Reihenfolge fixiert (normalisierte Form) + Eindeutigkeits-Invariante + Konflikt-Verfahren (Rev-2.7)
- Befund: (a) Zwei Einträge können dieselbe Schreibvariante mit unterschiedlichen canonischen Formen tragen — der Registry-Format-Abschnitt definiert keine Eindeutigkeits-Regel (kein „Variante in genau einem Eintrag", kein Konflikt-Verfahren; der „Append-only"-Regel-Abschnitt behandelt nur Umbenennungen). (b) Die Normalisierungs-Reihenfolge ist offen: lowercasing + `[-_ ]`-Kollaps **vor** dem Registry-Lookup? Lookup auf der rohen Variante? Die Varianten-Spalte ist als „bündel-findend per `-`-Kollaps, lowercased" beschrieben, aber die verbindliche Lookup-Operation steht nicht da. Genau das ist der Kern des deterministischen Resolvers (AD-17h).
- Optionen: **1 (empfohlen)** — in `canonical-terms.md` Registry-Format ergänzen: (i) Lookup-Reihenfolge fixiert (lowercase → Kollaps → Lookup auf der normalisierten Form; die Varianten-Spalte trägt ebenfalls normalisierte Formen), (ii) Eindeutigkeits-Invariante „eine Variante in genau einem Eintrag" + Konflikt-Fall (Append nicht erlaubt; `deferred-work.md`-Handoff / Ask-First analog zur bestehenden Umbenennungs-Regel). **2** — nur (ii) ergänzen; (i) als `defer` an die nächste Registry-Befüllung (erstes Entry).
- [x] [Review][Patch] `run-sandbox.sh`: T1 (Membership-Pin) und T4 (Vereinigung) werden nur ausgegeben, nie als Pass/Fail geprüft — das gepinnte „exakte Candidate-Liste"-Verhalten (Task 4) wird nicht erzwungen [run-sandbox.sh:80-113, 136-141] — **umgesetzt:** T1/T4 tragen harte Asssertionen (`exit 1` bei Abweichung)
- Befund: T1 gibt die Candidate-Liste und „Erwartet: AUSSCHLIESSLICH 'alpha'" aus, vergleicht aber nie; T4 gibt `alpha, gamma` (gesortet) aus, prüft aber nicht gegen die Erwartung. Ein Regressions-Fall (z. B. `log` oder `index` rutscht in die Liste, oder `gamma` fehlt) beendet das Skript mit Exit 0 und „Sandbox abgeschlossen (alle 4 Tests)". T3 (NO_MATCH) und T2 (Zwei-Run-Identität) haben dagegen echte `if`-Checks mit `exit 1`.
- Fix: T1 `out=$(terms 'deterministische-relevanz-bestimmung' | normalize); [ "$out" = "alpha" ] || { echo "FAIL: …" >&2; exit 1; }`; T4 `out=$(terms '…' | normalize | LC_ALL=C sort | paste -sd, -); [ "$out" = "alpha,gamma" ] || { echo "FAIL: …" >&2; exit 1; }`.
- [x] [Review][Patch] Typos/Fehlzeichnungen im committeten normativen Text — `Determinsmus` (×4+ in `compiler.md`/`log.md`/`deferred-work.md`/Spec), `§3.2-beankert` (soll „§3.2-angeankert" sein), `Membrum` (soll „Mitglied" sein), `Konventionelle Determinismus-Lücke` (soll „Bekannte Determinismus-Lücke" sein — verweist auf `compiler.md:53`, wo es „Bekannte" heißt) [schema/compiler.md:351,373; wiki/log.md:5; deferred-work.md:342; spec:80,93,106] — **umgesetzt** (reiner Text-Patch; `raw/`-Source-Typo und der präexistierende `außschließlich`-Typo im 3.1-Log bleiben unberührt — out-of-scope)
- Befund: Keine AC-Auswirkung, aber der Text ist die committete Instruktion/der committete Nachweis. `Determinsmus` steht auch in der Source `raw/epics/` (3×), die Instruktion sollte den Begriff aber konsistent korrekt verwenden, wie es der Rest des Dokuments tut. `Resovierung` (spec:80,106) ist zusätzlich zu `Resolution`/`Auflösung` zu korrigieren.
- Fix: orthografische Korrektur in den genannten Zeilen (reiner Text-Patch, keine Semantik-Änderung).
- [x] [Review][Defer] `§3.2`-Pkt.-2a: Wortgrenzen-/Frontmatter-Scope der Stufe-a-Match-Semantik ist offen (Substring-Match ohne `\b`, Frontmatter-Treffer zählen mit) [schema/compiler.md:55] — deferred, pre-existing
- [x] [Review][Defer] `§3.2`-Pkt.-1b-ii: Kollaps bei aufeinanderfolgenden/führenden/trailenden Separatoren ist unbestimmt (`a--b`, `-x`) [schema/compiler.md:51] — deferred, pre-existing
- [x] [Review][Defer] Stufe-a-Treffer auf `index.md`-Pfade (z. B. T2 `index`) werden nicht deterministisch aus der finalen Candidate-Liste entfernt (Stufe-b-Re-Routing implizit, nicht fixiert) [schema/compiler.md:55-56] — deferred, pre-existing
- [x] [Review][Defer] Rev-2.5-/Rev-2.6-Log-Zeile und compiler.md-Revisionslog: Nachweis-Klärigungen ohne Semantik-Auswirkung — (a) Rev-2.5-Log-Zelle zitiert die (nun historisch korrigierte) `rg --exclude`-Form im aktiven Mechanismus-Teil, ohne eigene Inline-Korrektur (Rev-2.6-Zelle erklärt es nur), (b) `last_updated: 08-19-2026 07:10` liegt vor dem Commit-Zeitpunkt (09:16), (c) Rev-2.6 steht im Revisionslog **über** Rev-2.5 (chronologisch umgekehrt), (d) Validator-„7/7 SUCCESS" als Aggregat ohne per-Datei-Liste, (e) Rev-2.6-`git status`-Beleg listet `canonical-terms.md`/`_bmad-output/`-Änderungen nicht (nämte „`git status`" als Prüfgrundlage), (f) Formel-4-Ist-Zählung `38` ohne Baseline-`≡`-Partner (re-executiert: Ist = 38, korrekt; nur der Beleg-String ist unvollständig) [wiki/log.md:4-5; schema/compiler.md:372-373; sprint-status.yaml:32] — deferred, pre-existing
## Spec Change Log
- **2026-08-19 (bmad-code-review Loop 3 — Review-Findings umgesetzt; 5 Decisions (alle Option 1) + 2 Patches + 4 Defers):** `schema/compiler.md`**Revision 2.7**: (1) **D-4** — §3.2-Pkt.-3b-Ordnung auf **reine Lexikografie (LC_ALL=C)** gehoben; die Term-Verarbeitungsreihenfolge bestimmt nur die Interpretation/Erhebung, **nicht** die Listen-Ordnung (dieselbe Treffermenge → identische Liste, AD-17h/A0-19); (2) **D-5**`schema/canonical-terms.md`: **Lookup-Verfahren** deterministisch fixiert (lowercasing → `[-_ ]``-`-Kollaps → Lookup der **normalisierten** Form; Spalten tragen ausschließlich normalisierte Formen) + **Eindeutigkeits-Invariante** (jede normalisierte Form in genau einem Eintrag — Canon oder Variante, nie beides/zweimal) + **Konflikt-Verfahren** (keine stille Anhängung; `deferred-work.md`/Ask-First, analog zur Umbenennungs-Regel); (3) **D-1** — Statuskette `in-progress → done` als eigener dokumentierter Schritt in `wiki/log.md` (Sprint-Sync-Konvention; Story-3.1-Präzedenz); Key bleibt `done`; (4) **D-2 + P-1**`run-sandbox.sh` um **T5** `LINK_FOLLOWING_ZYKLUS` (A↔B-Zyklus; besuchte Menge → endlich/doppelungsfrei), **T6** `TRAVERSAL_REACH_ONLY` (Term nur in Area-`index.md` → gewurzelte Concept-Pfade), **T7** `TERM_ABLEITUNG_SYNONYM` (Registry-Test-Doppel; beide Lookup-Pfade + Negativ-Fall) erweitert; T1/T4 mit harten Asssertionen; T2-Kommentar korrigiert; re-executiert: **alle 7 Tests PASS, exit 0**; (5) **D-3**`wiki/log.md`-Selbsttest-Beleg (b) gegen die echte `run-sandbox.sh`-Ausgabe neu belegt (neue Log-Zeile, append-only; Rev-2.5-Zeile historisch unverändert); (6) **P-2** — Typos korrigiert (`Determinsmus``Determinismus`, `beankert``angeankert`, `Membrum``Mitglied`, `Konventionelle``Bekannte`, `Resovierung``Auflösung`; `raw/`-Source und der präexistierende 3.1-Log-Typo bleiben unberührt). **4 Defers** (Wortgrenzen-/Frontmatter-Scope; Separator-Kollaps-Reichweite; Stufe-a-`index.md`-Treffer aus finaler Liste; Rev-2.5/2.6-Beleg-Klärigungs-Bündel) in `deferred-work.md` (append-only, Sektion „Deferred from: code review of spec-3-2-… (2026-08-19)"). **Keine AC-Änderung, kein frozen-Schnittstellen-Change, kein Validator-/Vertrags-/`raw/`-Change (AD-3), keine neue §7-Klasse, kein Standalone (D-3).** Frontmatter: `status: 'done'`, `review_loop_iteration: 3`. `sprint-status.yaml`: Key `3-2-…` bleibt `done`, `last_updated` → 08-19-2026.
- **2026-08-19 (bmad-code-review Loop 2 — bad_spec-Loopback):** 5 bad_spec-Findings ausgelöst, Code-Änderungen auf Baseline revertiert, nicht-gefrorene Spec-Tail-Sektionen geamendet:
- **B1/VG-1 `log.md`-Kontamination:** `rg -l '<term>' wiki/` ohne `--exclude=log.md``log.md` wird ab dem Moment selbst „Kandidat", in dem ein Log-Eintrag den Term zitiert; der Determinismus-Selbsttest verankert das A0-18-1-File-Find in einem Commit, das selbst `A0-18` enthält (Stale-Evidenz; Reproduzierbarkeits-Claim widerlegt sich selbst). Bekannt-bös: permanente Drift + falsches Verification-Expect.
- **B2 Reihenfolge:** `rg -l`-Ausgabe-Reihenfolge (FS-Traversal) nicht auf Zuwachs-Sicht-Ordnung abbildbar; Stufe-b/c ohne definierte Position. Bekannt-bös: AC-2-Zwei-Run-Identität nur bei undokumentiertem `rg`-Tie-Break.
- **B3 Resolver nicht enumeriert:** „eine canonische Form je Semantik" ohne committete Registry → Determinismus nicht pinbar. Bekannt-bös: gleiche Liste nur, wenn Resolver Teil des Git-States (nicht spezifiziert).
- **B4 Em/En-Dash:** Klassenliteral `[-_ ]` deckt nur En-``, nicht Em-`—`; Kollaps-Reichweite inkonsistent. Bekannt-bös: Em-Varianten fallen nicht unter den Kollaps (Synonymlücke). **Gewählter Weg:** `-`-Kollaps bleibt; explizite `—`-Auflösung wird als offene Determinismus-Lücke in Step-4-Defer an Story 3.8 übergeben (nicht stillschweigend hinzugefügt).
- **B5 §8-A0-19-Claim:** Spec sagte „A0-19 steht bereits in §8", war im Ist-Baum nicht kommutiert; Implementierung ergänzte A0-19 (korrekt). Spec korrigiert, A0-19-Ergänzung als KEEP verankert (fehlleiten bei Re-Baseline verhindern).
- **B6 NO_MATCH / §5.9-P2-Block-Scope:** werkt zusammen → zusammengefasst; in B1 und Task-Patch-Membership-Pin überführt.
- **B7 §5.9-P2-Block-Zeiger:** nicht mehr nötig — die fixierte Stufe (a) `--exclude=log.md` macht den Zeiger redundant (kein log.md-Kandidat mehr); **verworfen** (keine unnötige Pflicht).
- **KEEP-Instruktionen (müssen Neu-Ableitung überleben):** neue §3.2-Sektion nach §3 etc.; alle 6 I/O-Matrix-Szenarien; Determinismus-Vertrag (identische Reihenfolge, KEEP: nur-Parallel = deterministisch-MECHANISMUS, NICHT zufällig); `--exclude=log.md`-Stufe-a; Determinismus-Selbsttest MIT Membership-Pin (Zwei-Run-Identität + Mengen-Genauigkeit) und `canonical-terms.md`-Resolver as Resolver im Git-State; `Wiki/log.md` als append-only (Story-3.1-Bullet bleibt eigenständig stehen); die A0-19/Schema-Referenz ergänzen. NOT-KEEP: Spec-A0-19-Formulierung „steht bereits" (korrigiert).
- Restliche patch- und defer-Findings (B6/NO_MATCH, B5/Doppel, Log-Selbsttest-Beleggenaue, F17+Em-Morphologie-Story-3.8) werden in der Step-4-Patch-Runde umgesetzt bzw. in `deferred-work.md` nachbestätigt.
## Design Notes
**Warum §3.2 als eigene Sektion statt Streckung in Pkt. 2:** §3 ist heute eine nummerierte Liste (Pkt. 13); der Story-3.2-Vorbehalt sitzt IN Pkt. 2. Die feinkörnige Mechanik (Term-Ziehverfahren, Kanonisierungs-Resolver, dreistufige Erhebung, Determinismus) ist zu umfangreich, um Pkt. 2 verständlich zu halten. Design-Entscheidung: **neue Sektion §3.2 „Relevanzbestimmung (Story 3.2)"** direkt nach §3 — Pkt. 2 bleibt der Kern-Anker („Erhebung nach §3.2"), die Komplexität wird ausgelagert, bestehende §5.9-Pkt.-6-Bindungen bleiben gültig. Nur falls der §3-Aufbau eine andere Einfüge-Stelle nahelegt (z. B. Pkt. 2a), wird das Label angepasst — Pkt. 2 bleibt in jedem Fall der verbindliche Einstieg.
**Term-Ziehverfahren — was deterministisch heißt:** Die Ziehung ist deterministisch, weil sie allein von der committeten Evidenz-Datei abhängt: (1) bedeutungstragende Fachbegriffe = Token-Folgen mit fachlicher Signifikanz, aus der §2-Interpretation benannt (nicht freie LLM-Auswahl); (2) Normalisierung über einen canonischen Schreibweisen-Resolver (lowercasing; `[-_ ]``-`; eine canonische Form je Semantik); (3) mehrere Terme je Einheit erlaubt — Candidate-Liste = Vereinigung der Treffer über alle Terme, bereinigt über besuchte Menge; (4) kein stiller Ausschluss: nicht auflösbare Varianten werden wie notiert verwendet. Die Erhebung *mit festem Term* war schon in Rev-2.4.2 deterministisch — Story 3.2 macht zusätzlich die **Term-*Auswahl*** deterministisch (exakt der bisherige Vorbehalt).
**Determinismus-Referenz:** AD-17h und A0-19 sind bereits Normreferenzen in §8 Revision 2.4 (Story 3.1); Story 3.2 ergänzt **AD-13**, **A0-18** und **PRD OQ-3** (Architekturfrage „Compilation Scope"). A0-19 war im Ist-§8 (Baseline `e3e7ec3`) zwar nicht gelistet — die Implementierung fügt es als Determinismus-Referenz der Relevanzbestimmung hinzu (kein Verstoß, Design-KEEP: A0-19-Anker wird in §8 **und** §3.2 gesetzt; die frühere Spec-Formulierung „steht bereits" war falsch und ist hiermit korrigiert). `wiki/knowledge-kompilation-inkrementell.md:48` referenziert A0-18/AD-13 bereits; **zur Erhebung wird `wiki/log.md` ausgeschlossen** (`--exclude=log.md`, §5.6-Muster) — sonst wäre der eigene `log.md`-Eintrag bei jedem Term, den er zitiert, selbst „Kandidat" (A0-18/FR-12-Fall; die Candidate-Liste ist auf Concept-Pfade definiert, §5.9 Pkt. 5 behandelt `log.md` gesondert als erlaubtes Mitglied).
**Determinismus-Selbsttest (Membership + Identität):** Der Selbsttest prüft nicht nur Zwei-Run-Identität, sondern zusätzlich, dass die Ausgabe **Mengen-Genauigkeit** hat (nur Concept-Pfade, `log.md` ausgeschlossen) — er wird gegen einen mini-Sandbox-Baum ausgeführt, der einen realen Term auf Concept-Bodies und `log.md` verteilt und die exakte Candidate-Liste als erwartete Ausgabe pindet. Damit ist die Stale-Verifikations-Falle („das Artefakt widerlegt sich selbst") geschlossen.
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. **Vorbehalts-Auflösung:** `grep -n "Story 3.2 vorbehalten" schema/compiler.md` liefert **keinen** Treffer mehr (beide Vorbehalte aufgehoben: §3 Pkt. 2 + §7).
2. **Neue Sektion:** `grep -n "Relevanzbestimmung\|3.2" schema/compiler.md` liefert die neue §3.2-Sektion; §7-Vorbehalt enthält „3.2" nicht mehr; §8 trägt Revision 2.5 + 2.6 (Patch-Runde: §4-Kopf, rg-tool-korrekte Formen).
3. **Determinismus-Selbsttest (Membership + Identität):**
- `rg -l '<term>' -g '!log.md' wiki/` (rgs native Glob-Exklusionsform; `--exclude` ist kein rg-Flag — das GNU-grep-Äquivalent ist `grep -rl '<term>' --exclude=log.md wiki/`) zweimal ausführen, Ausgaben **identisch** — deterministische Erhebung ohne `log.md`-Kontamination; z. B. `rg -l 'A0-18' -g '!log.md' wiki/` → genau `wiki/knowledge-kompilation-inkrementell.md` (ein Concept-Pfad, `log.md` ausgeschlossen); `rg -l 'A0-18' wiki/` **ohne** Exklusion hingegeben würde `wiki/log.md` mit-treffen (Selbstkontamination — genau der ausgeschlossene Fall).
- mini-Sandbox-Baum (`_bmad-output/implementation-artifacts/sandbox-3-2/`, re-executierbar via `bash run-sandbox.sh` — Term auf Concept-Bodies **und** `log.md` verteilt): exakte Candidate-Liste (nur Concept-Pfade) wie erwartet; Normalisierung Strip `wiki/` + `.md` → relative OKF-Pfade; zwei aufeinanderfolgende Läufe liefern byte-identische Ausgaben (Zwei-Run-Identität, AD-17h).
4. **Validator-Lauf:** alle `wiki/`-Dateien SUCCESS (unverändert; reine Text-Instruktion, human-mechanisch ausgeführt, D-3) — per-Datei-Verdikt als Ausführungs-Nachweis im `log.md`-Eintrag.
5. **Resolver-Materialisierung:** `schema/canonical-terms.md` existiert (committete, append-only Registry: canon. Form + erlaubte Varianten); `grep -n "canonical-terms" schema/compiler.md` liefert die §3.2-Referenz. <br>_(Optionale Em-Dash-Prüfung: die Normalisierungs-Klasse deckt `-` ab; explizite `—`-Varianten-Auflösung ist eine erkannte Determinismus-Lücke → Story 3.8, s. Spec Change Log.)_
**Manual checks:**
- §3 Pkt. 2 zeigt auf die neue §3.2-Mechanik; Term-Ziehverfahren + Kanonisierungs-Resolver textuell benannt; Candidate-Liste als relative OKF-Pfade ohne `.md`; Determinismus-Vertrag in der Instruktion; `compiler.md` §8-Revision 2.5 + 2.6 mit Abschlussklausel; kein `schema/validator.md`-/`schema/wiki-compiler.md`-/`raw/`-Diff; §7-Vorbehalt ohne „3.2"; `wiki/log.md`-Eintrag datiert mit Story-3.2-Semantik, Determinismus-Beleg, Statuswechsel `backlog → in-progress → done` (Patch-Note Rev 2.6 oben), per-Datei-Verdikt; `deferred-work.md`-epic-3-context-Eintrag als aufgegriffen markiert + Em-Dash-Defer (Story 3.8); `sprint-status.yaml` konsistent (`3-2-…` → done).
@@ -0,0 +1,148 @@
---
title: 'Bestehende Concepts erweitern/präzisieren/korrigieren — Update-Formen operativ schärfen (Story 3.3)'
type: 'feature'
created: '2026-08-19'
status: 'done'
review_loop_iteration: 0
baseline_commit: 52f88fdc5f3ea0fe5323412ca3ba8b4a1bdbf549
context:
- _bmad-output/implementation-artifacts/epic-3-context.md
---
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** Story 3.1 verankerte die Update-Mechanik (Erweitern/Präzisieren/Korrigieren) in `schema/compiler.md` §5.9 als Regeltext, Story 3.2 die Candidate-Erhebung (§3.2). Unoperativ bleibt die **Ausführung je Update-Form** — v. a. das Story-3.3-AC aus FR-6: „der Text wird präzisiert oder korrigiert, **ohne die Struktur zu zerstören**". Der Compiler hat keine deterministische Anleitung, wie Präzisieren (Schärfen einer bestehenden Aussage) von Erweitern (Anfügen) abzugrenzen ist und was die „Struktur" (Frontmatter, bestehende belegte Aussagen, Provenienz-Verkettung) beim Übergriff auf einen bestehenden Absatz schützt.
**Approach:** Story 3.3 schärft §5.9 (Revision 2.8): die drei Update-Formen werden **operationell** ausformuliert — je Form ein deterministisch prüfbares **Abgrenzungskriterium** (Welche Form greift wann?), die **Struktur-Erhaltungsregel** (was bleibt unverändert: Frontmatter-Subset, bestehende belegte Aussagen + §5.5-Inline-Verweise, §5.6-Pin) und der Textgenauigkeits-Rahmen (Schärfen an der Aussage, kein Satz-Umbau, kein Neuschreiben ohne Ersetzungsbeleg). Zusätzlich eine **re-executierbare Sandbox** (`sandbox-3-3/run-sandbox.sh`, Muster Story-3.1): Demonstriert einen echten Update-Run mit committeter Neuevidenz in allen drei Formen + No-Op und prüft die Erhaltungs-Invariante (§5.9 Pkt. 5) als harte Assertion. Keine Inhalts-Mutation des realen Bundles (kein neues `raw/`-Material, AD-3); kein neues Prädikat, kein Vertrag-/Validator-/`raw/`-Change; kein Standalone (D-3).
## Boundaries & Constraints
**Always:**
- **Instruktions-Story (D-3):** Schärfung ausschließlich in `schema/compiler.md` §5.9 als deterministischer Text. Kein executable, kein Standalone, keine neue §7-Invaliditätsklasse, kein Change an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3).
- **Struktur-Erhaltungs-Invariante (Kern, FR-6/AC-2):** Update tastet die Struktur nicht an — (1) Frontmatter bleibt Subset-konform (nur `sources`-Zuwachs um echten neuen Beleg + `generated.at`-Bump zulässig); (2) bestehende belegte Aussagen werden nicht umgeschrieben ohne Präzisieren an ihr selbst bzw. Korrigieren mit Ersetzungsbeleg (§5.5-Inline-Verweise bleiben gültig soweit Beleg Body-Bestand); (3) gepinnte Linkform unverändert, keine neuen Links außer bei echten Beziehungen (§5.9 Pkt. 3).
- **Determinismus (AD-17h/A0-19):** Form-Wahl folgt aus der committeten Evidenz; gleicher Git-State + gleiche Eingabemenge → identischer Update-Vorgang. Abgrenzungsregeln textuell prüfbar (neue Aussage → Erweitern; Schärfung einer bestehenden → Präzisieren; ersetzende → Korrigieren).
- **Kein Leasing-Scope in 3.3:** AC-4 (Mutation nur im geleasten Bereich, AD-17.3) bleibt Story 3.5/3.6; AD-17f-Basis unverändert. §5.9 Pkt. 5/6-R1/P2-Bausteine unverändert.
- `sprint-status.yaml`: Key `3-3-bestehende-concepts-erweitern-präzisieren-korrigieren`**in-progress** (bei Implementierung).
**Ask First:** §5.6-Linkform-Änderung (A0-9) · AD-7d-Renames · Inhalts-Mutation des realen Bundles · Validator-/Vertrags-/`raw/`-Change · Commit-Boundary-Änderung · Leasing-Ausweitung (3.5-Scope).
**Never:** Änderungen an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3) · neue §7-Invaliditätsklasse · Standalone (D-3) · Duplikat-Anlage eines bestehenden Pfads · „Regenerate Everything" (AD-5/A0-6) · stille Löschung von Provenienz ohne Ersetzungsbeleg (AD-16) · stiller Absatz-Neuschreib (Structure-Preservation-Fall).
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| HAPPY_ERWEITERN | Neue committete Evidenz trägt eine neue belegte Aussage zum bestehenden Concept | **Erweitern:** neue Aussage als Absatz ergänzt (mit §5.5-Inline-Beleg), bestehende Aussagen unverändert; `sources`-Zuwachs um echten neuen Beleg; `at`-Bump; `log.md`-Eintrag „Story 3.1-Update" | NO_MATCH → UNTOUCHED_CONCEPT (keine Mutation) |
| HAPPY_PRAEZISIEREN | Evidenz schärft eine bestehende Aussage (präzisierende Information, FR-6/AC-2) | **Präzisieren:** Formulierung/Abgrenzung an der bestehenden Aussage geschärft, Position/Struktur erhalten; Beleg nachgeführt; kein Satz-Umbau/Neuschreib | keine bestehende Aussage als Anker → nicht Präzisieren (ggf. Erweitern) |
| HAPPY_KORRIGIEREN | Evidenz ersetzt eine fehlerhafte/überholte Aussage | **Korrigieren:** ersetzte Aussage **nicht still gelöscht**, sondern explizit abgelöst + Ersetzungsbeleg (AD-16-Default Erhaltung) | widersprechender Inhalt ohne Ersetzungsevidenz → bleibt erhalten, Disagreement in `log.md` (Epic-4-Interface) |
| NO_OP_KANDIDAT | Evidenz bereits vollständig im Body enthalten | **No-Op:** keine Mutation, kein `at`-Bump, kein `sources`-Zusatz, kein `log.md`-Eintrag — byte-identisch | im Zweifel greift Erweitern/Präzisieren (No-Op = engere Auslegung) |
| STRUKTUR_ERHALT | Update greift in Body mit belegten Aussagen + Inline-Verweisen | Struktur unverändert: Subset, Verweise, Linkform; kein unbefugter Key | Verstoß → Update fehlerhaft; Rollback + textuell benannter Verstoß (NFR-4) |
| MEHRFACH_TREFFER | Mehrere Einheiten desselben Runs treffen denselben Pfad | Konsolidierung: ein Update, ein `log.md`-Eintrag, ein konsolidierter `sources`-Zuwachs, ein `at` | Reihenfolge = Zuwachs-Sicht; lexikografischer Tie-Break |
</frozen-after-approval>
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3, einziger Instruktions-Ort):
- §5.9 Pkt. 2 (`:261-269`): drei Update-Formen **operationell ausformulieren** (a) Abgrenzungskriterium je Form, (b) Struktur-Erhaltungsregel (Definition „Struktur" + was unverändert bleibt), (c) Textgenauigkeits-Rahmen für Präzisieren. Bestehender Regeltext (Form-Bullets, `sources`-Regel, `at`-Konvention, No-Op, Konsolidierung) bleibt normativ **unverändert** — Schärfung als Ausführungs-Ebene ohne neue Logik.
- §5.9 Pkt. 5 (`:272-278`): Erhaltungs-Invariante — Struktur-Erhaltungs-Bezug ergänzen (Diff-Probe erfasst Pfad-Mengen; Struktur pro Pfad über Pkt.-2-Regeln geprüft).
- §5.9 Pkt. 6 (`:279-281`): P2-Block — Struktur-Erhaltungs-Check als zusätzliches textuelles Element (keine unbefugten Keys, keine stille Löschung, Links unverändert).
- §8 Revisionslog (`:375`): **Revision 2.8** (Story 3.3) mit Abschlussklausel (kein Vertrags-/Validator-/`raw/`-Change, keine §7-Klasse, kein Standalone).
- `_bmad-output/implementation-artifacts/sandbox-3-3/run-sandbox.sh`**neu** (re-executierbar, Muster Story-3.1-Sandbox): Mini-Bundle mit einem Concept + committeter Neuevidenz; alle drei Formen + No-Op durchspielen; Erhaltungs-Invariante als harte Assertion (Diff ⊆ {betroffener Pfad, log}; keine neue Datei; kein Ghost-Diff); Zwei-Run-Identität; Frontmatter-Konformität je mutiertem Concept.
- `wiki/log.md`**append** (append-only, Vertrag §5): Story-3.3-Eintrag (Schärfung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: Key `3-3-…` (`:56`) → `in-progress` (→ `done` bei Story-Abschluss).
- `schema/validator.md`, `schema/wiki-compiler.md`, `raw/…`**read-only** (AD-3). Keine Concept-Inhalts-Mutation (Instruktions-Story; kein neues committetes `raw/`-Material vorhanden — Demonstration per Sandbox).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — §5.9-Pkt.-2-Update-Formen operationell schärfen (je Form Abgrenzungskriterium + Struktur-Erhaltungsregel + Textgenauigkeits-Rahmen; bestehender Regeltext unverändert) · §5.9-Pkt.-5/-6: Struktur-Bezug + P2-Check-Element · §8 Revision 2.8; ohne Change an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/`.
- [x] `sandbox-3-3/run-sandbox.sh` — Update-Run-Demonstrator (Erweitern + Präzisieren + Korrigieren + No-Op), Erhaltungs-Invariante als harte Assertion, Zwei-Run-Identität. **Erweitert um U7 MEHRFACH_TREFFER** (I/O-Matrix-Row, Konsolidierung mehrerer Einheiten auf denselben Pfad) — alle 7 Tests (U1U7) PASS, Exit 0.
- [x] `wiki/log.md` — Story-3.3-Eintrag (Vertrag §5, append-only): Schärfung, Sandbox-Nachweis, Statuswechsel, per-Datei-Validator-Verdikt.
- [x] `sprint-status.yaml` — Key `3-3-…` → in-progress.
**Acceptance Criteria:**
- Given eine neue Erkenntnis zu einem bestehenden Concept, when der Run sie verarbeitet, then erweitert er das bestehende Concept anstelle der Anlage einer neuen Datei (AC-1; FR-6) — Erweitern-Regel in §5.9 Pkt. 2, Sandbox belegt sie (keine neue Datei im Diff).
- Given eine präzisierende Information, when sie eingearbeitet wird, then wird der Text präzisiert oder korrigiert, ohne die Struktur zu zerstören (AC-2; FR-6) — Struktur-Erhaltungsregel in §5.9 Pkt. 2/5 definiert.
- Given eine Aktualisierung, when sie erfolgt, then bleiben Beziehungen und Provenienz soweit weiterhin gültig erhalten (AC-3; FR-6) — Inline-Verweise + Linkform unverändert gültig soweit Beleg Body-Bestand; `sources`-Zuwachs nur um echte neue Belege.
- Given die Instruktion, when geprüft, then bleibt `schema/validator.md`/`schema/wiki-compiler.md`/`raw/` unverändert (AD-3), keine neue §7-Klasse, kein Standalone (D-3), kein Leasing-Scope (AC-4 → 3.5/3.6, AD-17.3-Hinweis in §7) — Validator auf Ist-Bundle SUCCESS (7/7 unverändert).
## Spec Change Log
*(Append-only — hier von step-04 während der Review-Loops befüllt.)*
### Review Loop 1 (Step-04, 2026-08-19) — 3 Subagenten (blind-hunter 9, edge-case-hunter 2+Minors, verification-gap 18)
**Klassifikation (Dedupe → Severity → Route):** High — Verifikations-Lücken des Sandbox-Nachweises (byte-Identität nur Teilmengen-assertiert; Struktur-Ebene/`??`-Sicht nie exerziert; U7 `-le 1`), Form-Abgrenzungs-Überlapp (keine Präzedenz → AD-17h-Gefahr), §5.5-Ankerform (`#S-N` ohne reale Kennung im Rohdokument). Medium — U3-Wortlaut-Mutation der abgelösten Aussage, `last_updated`-Präzisions-Regression, Frontmatter-Prüfung vollständig (Reihenfolge/Duplikat), No-Op-Form-Asymmetrie, „kein neuer normativer Inhalt" vs. Pkt.-5/6-Änderung, at↔AD-17h-Gap-Kreuzreferenz. Low — Diff-Artefakt-Pfade, Spec-Change-Log leer (per Konvention erst jetzt befüllt).
**Ergebnis: kein intent_gap, kein bad_spec** (kein Loopback) — **patch** (auto-fixiert, s. Diff): §5.9 Pkt.-2-Operationelle-Ebene — Präzedenz-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` + Überlapp-Bindung + No-Op als Nicht-Form + at↔AD-17h-Kreuzreferenz; Korrigieren-Struktur-Erhaltung — Original-Wortlaut bleibt als abgelöster Bestand erhalten; Sandbox — U1/U2/U3 byte-Identität auf alle drei Aussagen erweitert, U8 (unbefugter Frontmatter-Key → Struktur-HARD-FAIL) + U9 (ungetrackte neue Datei → `??`-Sicht-HARD-FAIL) neu, U7 `-le 1``-eq 1` + beide raw/-Quellen im konsolidierten log-Eintrag, assert_frontmatter um canoniche Reihenfolge + Duplikat-Prohibition erweitert, reale `S-N`-Anker in allen raw/-Evidenzdateien (§5.5), U3-Wortlaut nicht mehr verändert. **defer** (append-only `deferred-work.md`): `last_updated`-Präzision (sync-sprint-status-General), „Schärferung/Formata"-Typos (frozen Story-3.1-Text), „auch nach Commit"-Überzeichnung + at↔AD-17h-Gap-Kreuzreferenz (Home Story 3.8), U2-Selbsterfüllung + Zuwachs-Sicht-vs-lexikografisch-Rationale (Form-Wahl-Prüfung als Verfahren), Em-Dash/Umlaut-Normalisierung im Sandbox-Evidenztext. **reject:** U6-SHA-Reproduzierbarkeit von log.md (random baseline-SHA ist konstruktionsbedingt), Diff-Artefakt-Pfad-Präfix (nur Verbrauchskontext), Spec-Change-Log „leer" (per Konvention erst während Review-Loops befüllt).
## Design Notes
**Warum Ergänzung, nicht Umbau:** Die Story-3.3-Schärfung ist ein Ausführungs-Sub-Block an Pkt. 2 — sie definiert die offene Frage „Was tue ich konkret bei jedem Form-Fall, und was lasse ich unangetastet?" als deterministische Brücke Regel → Run (AD-17h): dieselbe Evidenz + formale Struktur → identisches Update-Ergebnis. Der frozen Story-3.1-Regeltext bleibt unverändert; die operationellen Regeln werden als Präzisierungsebene darunter angefügt (keine Re-Negotiation).
**Struktur-Definition (das Geschützte):** (1) Frontmatter (Subset + canonische Reihenfolge; zulässig: `sources`-Zuwachs, `at`-Bump; kein neuer Key, keine Entfernung ohne Beleg); (2) bestehende belegte Aussagen mit §5.5-Inline-Verweisen (nicht umgeschrieben ohne dass Präzisieren/Korrigieren greift); (3) Linkzeichen §5.6-Pin (kein Linkumbau bei reinem Body-Update). Das ist die Bedeutung von „ohne die Struktur zu zerstören" (FR-6/AC-2).
**Abgrenzungskriterium (aus der Evidenz):** (a) belegte Aussage existiert im Body nicht → Erweitern; (b) schärft bestehende Formulierung/Abgrenzung ohne Ersatz → Präzisieren; (c) ersetzt eine bestehende als fehlerhaft/überholt → Korrigieren; (d) nichts davon → No-Op. Unschärfe → zugunsten Erweitern/Präzisieren (No-Op = engere Auslegung, bestehende §5.9-Regel).
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. `bash _bmad-output/implementation-artifacts/sandbox-3-3/run-sandbox.sh` — expected: alle Szenarien mit harten Pass/Fail-Assertionen, Erhaltungs-Invariante erzwungen, Exit 0.
2. `grep -n "Struktur-Erhaltungsregel\|operationelle\|Revision 2.8" schema/compiler.md` — liefert die operationellen Regeln + Revisionslog.
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`raw/`.
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (unverändert, keine Inhalts-Mutation).
**Manual checks:**
- §5.9 Pkt. 2 trägt Abgrenzungskriterium + Struktur-Erhaltungsregel + Textgenauigkeits-Rahmen; bestehender Regeltext unverändert; §8-Rev-2.8 mit Abschlussklausel; kein `validator.md`/`wiki-compiler.md`/`raw/`-Diff; §7 ohne neuen Platzhalter (Update-Thema entlassen, AC-4-Leasing an 3.5/3.6); `wiki/log.md`-Eintrag datiert mit Story-3.3-Semantik + Sandbox-Nachweis + Statuswechsel + Verdikt; `sprint-status.yaml` konsistent.
## Suggested Review Order
**Abgrenzungs-Mechanik der Update-Formen (Kern)**
- Einstieg: die neue operationelle Ausführungs-Ebene — Präzedenz-Reihenfolge Korrigieren → Präzisieren → Erweitern → No-Op, deterministische Form-Wahl (AD-17h/A0-19), No-Op als Nicht-Form.
[`compiler.md:270`](../../schema/compiler.md#L270)
- Korrigieren schützt den abgelösten Original-Wortlaut (AD-16-Default, keine stille Löschung).
[`compiler.md:271`](../../schema/compiler.md#L271)
- Präzisieren: Schärfen an der Aussage selbst, kein Satz-Umbau; No-Op-Erhaltungsregel volle Byte-Identität.
[`compiler.md:272`](../../schema/compiler.md#L272)
**Struktur-Erhaltungs-Invariante + P2-Check**
- Die Pfad-Probe verifiziert jetzt zwei Ebenen — Menge + Struktur je berührtem Pfad (FR-6/AC-2).
[`compiler.md:283`](../../schema/compiler.md#L283)
- P2-Check-Block-Element (6): keine unbefugten Keys, keine stille Löschung, Linkform-Pin.
[`compiler.md:287`](../../schema/compiler.md#L287)
- Revisionslog Revision 2.8 mit Abschlussklausel (kein Vertrags-/Validator-/`raw/`-Change).
[`compiler.md:382`](../../schema/compiler.md#L382)
**Re-executierbarer Sandbox-Nachweis**
- U8: unbefugter Frontmatter-Key löst die Struktur-Ebene-HARD-FAIL aus — Negativ-Kontrolle der Kern-Invariante.
[`run-sandbox.sh:365`](./sandbox-3-3/run-sandbox.sh#L365)
- U9: ungetrackte neue Datei wird über die `??`-Sicht erkannt (Duplikat-Kontrolle).
[`run-sandbox.sh:378`](./sandbox-3-3/run-sandbox.sh#L378)
- U7: Konsolidierung mehrerer Einheiten auf einen Pfad — ein log-Eintrag, beide raw/-Quellen verlinkt.
[`run-sandbox.sh:320`](./sandbox-3-3/run-sandbox.sh#L320)
- assert_frontmatter: canoniche Reihenfolge + Duplikat-Prohibition (Vertrag §3.3/§3.4-Subset).
[`run-sandbox.sh:118`](./sandbox-3-3/run-sandbox.sh#L118)
**Provenienz & Status**
- Review-Loop-1-Bullet: Klassifikation, Patches, Defers, Rejects; Spec-Status `in-review`.
[`log.md:4`](../../wiki/log.md#L4)
- Defer-Einträge (5) inkl. Home Story 3.8 für at↔AD-17h-Gap und Normalisierungs-Lücken.
[`deferred-work.md:9`](./deferred-work.md#L9)
- Sprint-Status Key `3-3-…`: Review-Abschluss-Flip auf `done`.
[`sprint-status.yaml:56`](./sprint-status.yaml#L56)
@@ -0,0 +1,142 @@
---
title: 'Wissen aus mehreren Sources synthetisieren — gemischte claim-granulare Provenienz operationalisieren (Story 3.4)'
type: 'feature'
created: '2026-08-19'
status: 'done'
review_loop_iteration: 0
baseline_commit: 9b256fa5adc22fceb1e28a07efbf4504e0004878
context:
- _bmad-output/implementation-artifacts/epic-3-context.md
---
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** §5.9 verankert die Update-Formen einzelner Concepts (Story 3.1/3.3), §4 die `sources`-Liste. Unoperativ bleibt die **Synthese-Dimension (FR-7, AD-4, A0-3)**: wie der Producer mehrere Sources zum selben Thema zu **einer** gemeinsamen Wissensrepräsentation mit **gemischter, claim-granularer Provenienz** zusammenführt — statt getrennter Zusammenfassungen (FR-7). Es fehlt eine deterministische Anleitung für (a) die Erkennung einer Synthese-Einheit (≥ 2 belegende `raw/`-Quellen), (b) die Konsolidierung redundanter Aussagen **ohne** Provenienz-Verlust (Multi-Beleg, §5.5), und (c) den reflektierten Wissensstand (keine Aneinanderreihung von Source-Zusammenfassungen, FR-7 AC-4). Der §7-Synthese-Vorbehalt (`:343`) zeigt auf Story 3.4.
**Approach:** Story 3.4 verankert die Synthese-Mechanik als neue Sektion **§5.10 „Synthese aus mehreren Sources (Story 3.4)"** (nach §5.9, vor §6, Revision 2.9): deterministische Regeln für Synthese-Einheit, gemeinsame Zielrepräsentation (ein Concept-Pfad, `sources`-Liste ≥ 2), claim-granulare gemischte Provenienz mit Multi-Beleg-Konsolidierung (§5.5) und Reflektiertheits-Selbsttest (keine per-Source-Zusammenfassungs-Struktur). Zusätzlich eine **re-executierbare Sandbox** (`sandbox-3-4/run-sandbox.sh`, Muster Story-3.3): demonstriert einen echten Synthese-Run aus 2+ `raw/`-Quellen (Neu-Anlage + Update-Pfad) und assertet die Invarianten hart (sources ≥ 2, Multi-Beleg je konsolidierter Aussage, keine Aneinanderreihung, Erhaltungs-Invariante §5.9-Pkt.-5, Zwei-Run-Identität AD-17h). Nickel die Story-3.3-Defer U2/U7-Empfehlung (Form-Wahl-Klassifikationsprobe) im Sandbox auf. Keine Inhalts-Mutation des realen Bundles (AD-3); kein neues `raw/`-Material; kein Standalone (D-3).
## Boundaries & Constraints
**Always:**
- **Instruktions-Story (D-3):** Verankerung ausschließlich in `schema/compiler.md` §5.10 als deterministischer Text. Kein executable, kein Standalone, keine neue §7-Invaliditätsklasse, kein Change an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3).
- **Synthese ist Querschnitt-Dimension, keine fünfte Update-Form:** Die §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` (Story 3.3) bleibt die **sole** Form-Wahl; Synthese erweitert die Ausführung um gemischte Provenienz + Konsolidierung (bei Update auf ein bestehendes Concept) bzw. die Neu-Anlage mit Multi-Source-`sources` (bei neuer Einheit).
- **Gemischte claim-granulare Provenienz (AD-4a/4b, A0-3):** jede belegte Aussage des Synthese-Concepts bleibt auf `raw/` zurückführbar; Konsequenz von AD-4c — kein bestehendes Concept ist alleinige Provenienz eines anderen; Übernahmen aus bestehenden Concepts tragen den §5.5-Kontext-Marker („übernommen aus `<Concept>` auf Basis von `<source>`, nicht eigenständig belegt").
- **Konsolidierung ohne Provenienz-Verlust (FR-7 AC-3):** redundante Informationen werden zu einer Aussage konsolidiert, die **alle** betroffenen Inline-Belege trägt (Multi-Beleg, §5.5-Serialisierung) — keine stille Beleg-Tilgung (AD-4).
- **Reflektierter Wissensstand (FR-7 AC-4):** keine per-Source-Zusammenfassungs-Blöcke („Quelle A: … / Quelle B: …"-Struktur); der Body trägt integrierte, je Aussage provenance-tags versehene Aussagen — textuell per Selbsttest prüfbar.
- **Determinismus (AD-17h/A0-19):** Ziel-Pfad (via §3.2/§5.7), `sources`-Liste (lexikografisch nach `resource`, LC_ALL=C) und Konsolidierung folgen deterministisch aus der committeten Evidenz; gleicher Git-State + gleiche Eingabemenge → identischer Synthese-Vorgang. `generated.at`-Wanduhr-Gap bleibt offene A0-20-Konvention (Home Story 3.8).
- **Struktur-Erhaltung (FR-6/AC-2, Story 3.3):** die §5.9-Pkt.-2-Struktur-Erhaltungsregeln gelten je berührtem Pfad unverändert (Frontmatter-Subset, keine stille Löschung, §5.6-Pin unverändert, Inline-Verweise gültig soweit Beleg Body-Bestand).
- **Erhaltungs-Invariante (AD-5/FT-6):** der §5.9-Pkt.-5-Diff-Selbsttest gilt für Synthese-Runs unverändert (Pfad-Menge ⊆ Kandidatenliste Neu-Anlage `log.md` Index; keine neue Datei außer echten Ziel-Pfaden).
- `sprint-status.yaml`: Key `3-4-wissen-aus-mehreren-sources-synthetisieren`**in-progress** (bei Implementierung).
**Ask First:** Inhalts-Mutation des realen Bundles (Demo per Sandbox) · AD-7d-Renames · §5.6-Linkform-Änderung · Validator-/Vertrags-/`raw/`-Change · Leasing-Ausweitung (3.5/3.6-Scope) · Synthese als fünfte Update-Form (Abgrenzung ändern) · per-Source-Concepts als zulässige Anlageform.
**Never:** Änderungen an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3) · neue §7-Invaliditätsklasse · Standalone (D-3) · getrennte Zusammenfassungs-Concepts je Quelle (FR-7) · Duplikat-Anlage eines bestehenden Pfads · „Regenerate Everything" (AD-5) · stille Provenienz-Tilgung oder stille Konsolidierung widersprechender Aussagen ohne Ersetzungsbeleg (AD-16) · Embeddings/Vector im Compiler-Kern (AD-13).
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| HAPPY_SYNTHESE_NEUANLAGE | Zwei committete `raw/`-Quellen zum selben Thema (neue Einheit, kein Ziel-Pfad belegt) | **Ein** neues Concept (gemeinsame Repräsentation): `sources`-Liste ≥ 2 (je Quelle ein Eintrag, lexikografisch nach `resource`), claim-granulare gemischte Inline-Provenienz je Aussage, §5.5-Form; kein per-Source-Summary | NO_MATCH → §5.9/§3.2-Pfad (keine Synthese-Dimension) |
| HAPPY_SYNTHESE_KONSOLIDIERUNG | Redundante Aussage in beiden Quellen | **Eine** konsolidierte Aussage mit Multi-Beleg auf alle betroffenen Quellen (§5.5-Semikolon-Form) — keine Provenienz-Dropped | Quellen-EC-1 fehlt → Run-FAIL (Validator), keine Mutation |
| HAPPY_SYNTHESE_UPDATE | Synthese-Einheit trifft bestehenden Concept-Pfad | §5.9-Formwahl normal (Erweitern/Präzisieren/Korrigieren), `sources`-Zuwachs um die zweite Quelle, gemischte Belege; Struktur-Erhaltungsregeln gelten | bestehende belegte Aussage als Anker fehlt → Prüfung ob Erweitern |
| NO_OP_SYNTHESE | Evidenz bereits vollständig im Body | No-Op (Nicht-Form): byte-identität, kein Eintrag, kein `at`-Bump | im Zweifel greift eine Form (engere Auslegung No-Op) |
| ANEINANDERREIHUNG_NEGATIV | Struktur „Quelle A: … / Quelle B: …" | **Instruktions-Selbsttest-FAIL** (Reflektiertheits-Kriterium, NFR-7): textuell benannter Befund, vor Run-Abschluss behoben | kein stiller Vorbeilass (NFR-4) |
| STRUKTUR_ERHALT | Synthese-Update greift in Body mit belegten Aussagen | Struktur unverändert (Subset, Verweise, Linkform); kein unbefugter Key | Verstoß → textuell benannt + Rollback (§5.9 Pkt. 5) |
| SYNTHESE_OVERLAP_FORMCROSS | Eine Einheit trägt zugleich neue + schärfende Aussage | §5.9-Abgrenzungs-Reihenfolge wählt die erste zutreffende Form; Textgenauigkeits-Rahmen der übrigen gelten je Teilbestand | — (AD-17h: deterministisch) |
</frozen-after-approval>
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3):
- Neue Sektion **§5.10** (nach §5.9, vor §6): Synthese-Stimulus (≥ 2 belegende Quellen per §2-Interpretation), ein-Ziel-Repräsentation (§5.7-Routing), `sources`-Liste (≥ 2, lexikografisch nach `resource`, LC_ALL=C — deterministisch, AD-17h), gemischte claim-granulare Provenienz (je Aussage Inline-`raw/`-Verweis §5.5; konsolidierte Aussagen mit Multi-Beleg), Reflektiertheits-Selbsttest (keine per-Source-Zusammenfassungs-Struktur; Muster-Termane wie „nicht eigenständig belegt"), Übernahme aus bestehenden Concepts mit §5.5-Kontext-Marker (AD-4c: nie alleinige Provenienz), §5.6-Pin unverändert, `log.md`-Eintragspflicht („Story 3.1-Update" bei Update; Anlage-Eintrag bei neuem Synthese-Concept), Erhaltungs-Invariante (Diff-Probe §5.9 Pkt. 5 gilt), Determinismus-Vertrag.
- §7 (`:343`): Synthese-Vorbehalt **auflösen** („→ §5.10 verankert (Story 3.4)").
- §8 Revisionslog (`:382`): **Revision 2.9** mit Abschlussklausel.
- `_bmad-output/implementation-artifacts/sandbox-3-4/run-sandbox.sh`**neu** (re-executierbar, Muster Story-3.3-Sandbox): Mini-Bundle mit 2 `raw/`-Quellen zum selben Thema; Szenarien S1S6 (Neu-Anlage mit sources ≥ 2 + gemischten Belegen · Konsolidierung mit Multi-Beleg · Negativ-Kontrolle Aneinanderreihung · Synthese-Update auf bestehendem Pfad · Struktur-/Frontmatter-Invariante · Zwei-Run-Identität AD-17h) + **N1** (NO_OP-Synthese: Evidenz bereits im Body → volle Byte-Identität, kein at-Bump, kein sources-Zusatz, kein log-Eintrag) + **Form-Wahl-Klassifikationsprobe** (Story-3.3-Defer U2/U7: überprüft die §5.9-Abgrenzungs-Reihenfolge an einer gemischten Einheit). Harte Pass/Fail-Assertionen (exit 1), Erhaltungs-Invariante, `??`-Sicht, `assert_frontmatter` (Muster Story-3.3).
- `wiki/log.md`**append** (append-only, Vertrag §5): Story-3.4-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: Key `3-4-…` (`:57`) → `in-progress` (→ `done` bei Story-Abschluss).
- `schema/validator.md`, `schema/wiki-compiler.md`, `raw/…`**read-only** (AD-3). Keine Concept-Inhalts-Mutation (Demonstration per Sandbox).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — §5.10 Synthese-Sektion einfügen (Stimulus, ein-Ziel-Repräsentation, `sources`-Liste, gemischte claim-granulare Provenienz + Multi-Beleg-Konsolidierung, Reflektiertheits-Selbsttest, AD-4c-Marker, §5.6-Pin, log.md, Erhaltungs-Invariante, Determinismus) · §7-Vorbehalt auflösen · §8 Revision 2.9; ohne Change an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/`.
- [x] `sandbox-3-4/run-sandbox.sh` — Synthese-Run-Demonstrator (Neu-Anlage + Update, Konsolidierung, Negativ-Kontrolle Aneinanderreihung, NO_OP-Synthese, Form-Wahl-Probe, Struktur-Invariante, Zwei-Run-Identität); harte Pass/Fail-Assertionen; Exit 0.
- [x] `wiki/log.md` — Story-3.4-Eintrag (append-only): Verankerung, Sandbox-Nachweis, Statuswechsel, per-Datei-Validator-Verdikt.
- [x] `sprint-status.yaml` — Key `3-4-…` → in-progress.
**Acceptance Criteria:**
- Given mehrere Sources zum selben Thema, when der Run synthetisiert, then entsteht eine gemeinsame Wissensrepräsentation statt mehrerer getrennter Zusammenfassungen (AC-1; FR-7) — §5.10-Stimulus + ein-Ziel-Regel, Sandbox belegt (eine Datei, keine per-Source-Summary).
- Given eine Synthese aus mehreren Sources, when das Ergebnis erzeugt wird, then übernimmt es gemischte, claim-granulare Provenienz der beteiligten Sources (AC-2; AD-4, A0-3) — Inline-Verweise §5.5 je Aussage, Konsolidierung mit Multi-Beleg.
- Given redundante Informationen aus mehreren Sources, when sie synthetisiert werden, then werden sie konsolidiert, ohne Provenienz zu verlieren (AC-3; FR-7) — §5.10-Konsolidierungsregel, Sandbox-S2.
- Given das resultierende Concept, when es geprüft wird, then reflektiert es den erkannten Wissensstand — keine bloße Aneinanderreihung (AC-4; FR-7) — Reflektiertheits-Selbsttest, Negativ-Kontrolle S3.
- Given die Instruktion, when geprüft, then bleiben `schema/validator.md`/`schema/wiki-compiler.md`/`raw/` unverändert (AD-3), keine neue §7-Klasse, kein Standalone (D-3), keine fünfte Update-Form (Abgrenzungs-Reihenfolge §5.9 unverändert), kein Leasing-Scope — Validator auf Ist-Bundle SUCCESS (7/7 unverändert).
## Spec Change Log
**Review-Loop-1 (2026-08-19, bmad-code-review, 3 Subagenten — blind-hunter / edge-case-hunter / verification-gap; kein Loopback, `review_loop_iteration` bleibt 0):** Klassifikation — **kein intent_gap, kein bad_spec** (frozen Intent + Approach eingehalten); **patch** (auto-fixiert, siehe unten) + **defer** (4 Einträge → `deferred-work.md` append-only: AD-16/AD-4c-reale-Anwendung-Home-Epic-4, Orphan-Kontrolle-Home-3.8, Umlaut/Em-Dash-Normalisierung-Sandbox-Home-3.8, Frozen-„Nickel"-Typo-Re-Negotiation) + **reject** (noise: diff-Artifakt-Basename, SHA-Reproduzierbarkeit via `$BASE`-ID). **Patches (alle verifiziert):** (1) `schema/compiler.md` §5.10 — Reflektiertheits-Selbsttest-Muster auf Zeilenanfangs-Label erweitert (`^(Quelle|Source) [A-Z][A-Za-z0-9_ -]*:` — mehrbuchstabige/nummerierte Quell-Labels); **Body-Reihenfolge deterministisch** (lexikografisch über Beleg-Anker, LC_ALL=C) + **Konsolidierungs-Kriterium** (Befund-Äquivalenz über dieselbe erkannte Wissenseinheit) explizit; Duplikat-Fall + Orphan-Kontrolle in Pkt. 8; Stil-/Genus-Korrekturen (`sole``einzige`, `der Update``das Update`, `sources`-Pluspunkt→`sources`-Zuwachs); Sandbox-N2/N3-Nachweise in Pkt. 3/4. (2) **Sandbox `run-sandbox.sh` (jetzt 10 Szenarien S1S6 + N1N3 + FW, Exit 0):** `git add -A`-wiki-Staging aus S1/S2/S3/S4/S5/FW entfernt → die Pkt.-8-`??`-Sicht wird **lebendig** geprüft; **S5-Pkt.-8-`??`-Negativkontrolle** neu (ungetrackte Nicht-Ziel-Datei `ghost.md` → Duplikat/Ghost-Diff-HARD-FAIL, nicht vacuous — Story-3.3-U9-Muster); **N1**-Stimulus-Kommentar korrigiert (alpha-v1 = Baseline-Evidenzbasis, nicht Zuwachs); **N2 Disagreement** (AD-16-Default, beide Behauptungen + je Beleg, kein Falsch-Multi-Beleg, `Disagreement:`-log-Eintrag) + **N3 AD-4c-Übernahme-Marker** (Kontext-Marker-Muster, eigenständig belegte Aussagen ohne Marker, übernommene Quelle nicht in `sources`) als neue Szenarien (schließen die Verification-Gap-Kernbefunde „AD-16-Pfad untested"/„AD-4c-Marker untested"); S1/S2-log.md-Assertionen um die **vollständige Multi-Source-Liste** erweitert. (3) `wiki/log.md`: Audit/Sandbox-Zählung auf 10 korrigiert, Scenario-Typo, „zum Audit-Zeitpunkt"-Qualifikation. (4) `sprint-status.yaml last_updated`-Präzision auf `08-19-2026 16:40` fortgeschrieben (HEAD-Format `HH:MM`, siehe Story-3.3-Präzedenzleistung 13:21). (5) `deferred-work.md` — 4 Spec-3.4-Einträge append-only. **Nach-Review-Abschluss (dieser Flip):** Spec `status: 'in-review' → 'done'`; `sprint-status.yaml` Key `3-4-…` `in-progress → done`.
## Design Notes
**Warum §5.10, nicht §5.9-erweiterung:** Synthese ist die **Erzeugungs-/Zusammenführungs-Dimension** (mehrere Quellen → eine Repräsentation) — keine der in §5.9 definierten Einzel-Update-Formen. §5.9 bleibt der Update-Pfad für einzelne bestehende Concepts; §5.10 erweitert die Ausführung um gemischte Provenienz + Konsolidierung (Querschnitt) und die Neu-Anlage mit Multi-Source-`sources`. Das spiegelt den §7-Vorbehalt (`:343`) als eigenständiges Thema.
**Multi-Beleg-Konsolidierung (das Kernstück):** Eine Aussage, die aus mehreren Quellen redundanz-belegt ist, wird zu EINER Aussage konsolidiert, die §5.5-Multi-Beleg trägt (`(raw/a.md#X; raw/b.md#Y)` — Semikolon-Form, voller Pfad je Beleg). Keine Beleg-Tilgung (AD-4). Widersprüche (diskrepanter Inhalt) bleiben gemäß AD-16 erhalten — kein stilles Zusammenführen zu scheinbar eindeutigen Aussagen (NFR-7, FR-8; Epic-4-Interface: Disagreement in `log.md`).
**Reflektiertheits-Selbsttest (FR-7 AC-4):** textuell prüfbar — der Body enthält keine per-Source-Zusammenfassungs-Sektionen (keine Struktur „Quelle A: … / Quelle B: …"); jede Aussage trägt ihren Provenienz-Tag. Der Selbsttest grep-basiert: keine Zeile, die nur einen Quell-Label abschnittsstrukturiert.
**`sources`-Reihenfolge:** deterministisch lexikografisch nach `resource` (LC_ALL=C) — unabhängig von Verarbeitungs- oder Datei-Reihenfolge (AD-17h; konsistent zur §3.2-Pkt.-3b-Lexikografie-Ordnung und zur §5.9-Tie-Break-Logik).
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. `bash _bmad-output/implementation-artifacts/sandbox-3-4/run-sandbox.sh` — expected: alle Szenarien S1S6 + N1 + Form-Wahl-Probe mit harten Pass/Fail-Assertionen, Erhaltungs-Invariante erzwungen, Exit 0.
2. `grep -n "Synthese aus mehreren Sources\|Revision 2.9" schema/compiler.md` — liefert die Synthese-Sektion + Revisionslog-Eintrag.
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`raw/`.
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (unverändert, keine Inhalts-Mutation).
**Manual checks:**
- §5.10 trägt Stimulus, ein-Ziel-Regel, `sources`-Liste, gemischte Provenienz, Multi-Beleg-Konsolidierung, Reflektiertheits-Kriterium, AD-4c-Marker, §5.6-Pin, `log.md`-Regel, Erhaltungs-Invariante, Determinismus; §7-Vorbehalt `:343` auf §5.10 aufgelöst; §8-Rev-2.9 mit Abschlussklausel; kein `validator.md`/`wiki-compiler.md`/`raw/`-Diff; kein neuer Standalone (D-3) / keine neue §7-Klasse; §5.9-Abgrenzungs-Reihenfolge unverändert (Synthese = Querschnitt, keine fünfte Form); `wiki/log.md`-Eintrag datiert mit Story-3.4-Semantik + Sandbox-Nachweis + Statuswechsel + Verdikt; `sprint-status.yaml` konsistent.
## Suggested Review Order
*(Befüllt nach Review-Abschluss durch step-04. Review-Loop 1 abgeschlossen: kein Loopback, 4 Defers; Status → `done`.)*
**Synthese-Mechanik (compiler.md §5.10)**
- Verankerung der Synthese-Dimension — Stimulus, ein-Ziel-Regel, gemischte Provenienz, Reflektiertheit, Erhaltungs-Invariante.
[`compiler.md:290`](../../schema/compiler.md#L290)
- Pkt. 8 — Determinismus-Vertrag: `??`-Duplikat-Kontrolle, Orphan-Fall, `generated.at`-Gap (Home 3.8).
[`compiler.md:301`](../../schema/compiler.md#L301)
**§7/§8-Auflösung**
- Pkt. 3/4 — AD-16-Disagreement (N2) + AD-4c-Übernahme-Marker (N3) als Sandbox-Nachweise verankert.
[`compiler.md:296`](../../schema/compiler.md#L296)
- Revisionslog 2.9 — Abschlussklausel (AD-3, keine neue §7-Klasse, keine fünfte Update-Form) + Review-Loop-1-Notiz.
[`compiler.md:396`](../../schema/compiler.md#L396)
**Sandbox-Nachweis (re-executierbar)**
- 10 Szenarien S1S6 + N1N3 + FW, harte Erhaltungs-Invariante je Probe; Export & Pass-Statistik.
[`run-sandbox.sh:226`](sandbox-3-4/run-sandbox.sh#L226)
- N2 — AD-16-Disagreement (beide Behauptungen + Disagreement-Eintrag); N3 — AD-4c-Marker.
[`run-sandbox.sh:562`](sandbox-3-4/run-sandbox.sh#L562)
- FW — Form-Wahl-Klassifikationsprobe (Story-3.3-Defer U2/U7) + Zwei-Run-Identität (S6).
[`run-sandbox.sh:740`](sandbox-3-4/run-sandbox.sh#L740)
**Provenienz & Status**
- Story-Abschluss + Review-Loop-1-Audit — `done`-Flip, 10/10 PASS, 4 Defers.
[`log.md:4`](../../wiki/log.md#L4)
- Implementierungs-Nachweis — Revision 2.9-Eintrag, Validator-Verdikt 7/7 SUCCESS.
[`log.md:6`](../../wiki/log.md#L6)
- Statuskette `in-progress → done` + `last_updated`-Präzision.
[`sprint-status.yaml:57`](sprint-status.yaml#L57)
@@ -0,0 +1,135 @@
---
title: 'Leasing & Dirty-Tree-Schutz für konkurrierende Producer operationalisieren (Story 3.5)'
type: 'feature'
created: '2026-08-19'
status: 'done'
review_loop_iteration: 0
baseline_commit: fae648e688781d281fa668310e179fd002ba812f
context:
- _bmad-output/implementation-artifacts/epic-3-context.md
---
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** §0/§5.3 verankern die Commit-Boundary = Mutations-Boundary (AD-17f) als einzigen bisherigen Schutz. Es fehlt die **Koordinations-Dimension für konkurrierende Producer** (AD-17a..f, A0-12..A0-16, FR-2/FR-12): wie zwei Producer denselben Concept-Pfad nicht stillschweigend überschreiben, wie eine Lease auf `lease/<area>/<id>`-Branches mit Root-Scope und Merge-Base-Disziplin erworben wird, und wie fremde uncommittete Änderungen (Dirty Tree) geschützt statt als Nebenwirkung gelöscht werden. Der §7-Vorbehalt (`:357`) zeigt unbehoben auf Story 3.5/3.6.
**Approach:** Story 3.5 verankert die Leasing-/Dirty-Tree-Mechanik als neue Sektion **§5.11 „Leasing & Dirty-Tree-Schutz für konkurrierende Producer (Story 3.5)"** (nach §5.10, vor §6, Revision 3.0): deterministische Regeln für Lease-Akquise (Branch-Form `lease/<area>/<id>`, Lockfile, Merge-Base-Disziplin), Root-Scope-Lease (inkl. `log.md`, `index.md`, Root-Dateien), Dirty-Tree-Schutz (Pre-Mutation-Prüfung, Stash/Scratch-Zone, `log.md`-Dokumentation) und compiler-vermittelten Merge als AD-16-Pfad (kein stiller textueller Auto-Merge). Zusätzlich eine **re-executierbare Sandbox** (`sandbox-3-5/run-sandbox.sh`, Muster Story-3.4): demonstriert Lease-Akquise + Dirty-Tree-Schutz + Merge-Verweigerung an echten Git-Szenarien und assertet die Invarianten hart. Keine Inhalts-Mutation des realen Bundles (AD-3); kein neues `raw/`-Material; kein Standalone (D-3).
## Boundaries & Constraints
**Always:**
- **Instruktions-Story (D-3):** Verankerung ausschließlich in `schema/compiler.md` §5.11 als deterministischer Text. Kein executable, kein Standalone, keine neue §7-Invaliditätsklasse, kein Change an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3).
- **Leasing-Modell (AD-17a, A0-12):** Producer arbeiten auf `lease/<area>/<id>`-Branches; die Lease wird gegen einen **eindeutigen Commit-Object-Wert** (Merge-Base-Disziplin) akquiriert; ein **Lockfile** realisiert semantisch identisch in jedem Adapter — die Realisierung ist nicht pro Adapter frei wählbar.
- **Root-Scope-Lease (AD-17b, A0-13):** Die Lease umfasst `wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien — nicht nur den mutierten Concept-Pfad.
- **Kein textueller Auto-Merge (AD-17c, A0-14):** Zwei Branches mit ungleichem Inhalt am selben Concept-Pfad werden nie textuell automatisch gemerged; der Merge ist compiler-vermittelt und durchläuft die AD-16-Klassifikation mit explizitem `log.md`-Eintrag (Interface zu Epic 4).
- **Dirty-Tree-Schutz (AD-17e/f, A0-16):** Vor jeder Mutation wird die Working Copy auf den mutierten Bereich geprüft; fremde uncommittete Änderungen werden geschützt (Stash/Scratch-Zone) und in `log.md` dokumentiert; Mutationen operieren nur auf Directory-/Commit-Ebene — Commit-Boundary = Mutations-Boundary.
- **Determinismus (AD-17h/A0-19):** Lease-Akquise, Lockfile-Inhalte und Merge-Klassifikation folgen deterministisch aus dem committeten Git-State; gleicher Git-State + gleiche Eingabemenge → identische Koordinationsentscheidung.
- **Struktur-Erhaltung (FR-6/AC-2, Story 3.3):** Die §5.9-Pkt.-2-Struktur-Erhaltungsregeln gelten je berührtem Pfad unverändert; Leasing erzwingt keine neuen Concept-Felder (Vertrag §3.1–§3.7-Subset unverändert).
- **Erhaltungs-Invariante (AD-5/FT-6):** Der §5.9-Pkt.-5-Diff-Selbsttest gilt für Leasing-fähige Runs unverändert (Pfad-Menge ⊆ Kandidatenliste Neu-Anlage `log.md` Index).
- `sprint-status.yaml`: Key `3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz`**in-progress** (bei Implementierung).
**Ask First:** Inhalts-Mutation des realen Bundles (Demo per Sandbox) · AD-7d-Renames · §5.6-Linkform-Änderung · Validator-/Vertrags-/`raw/`-Change · Lease-Staleness/Recovery-Scope (3.6) · Lease-Bereich jenseits `wiki/` (AD-17b-Root-Scope) · neuer Concept-Frontmatter-Key für Lease-Metadaten.
**Never:** Änderungen an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3) · neue §7-Invaliditätsklasse · Standalone (D-3) · stiller textueller Auto-Merge (AD-17c) · stille Löschung fremder uncommitteter Änderungen (AD-17e) · Leasing außerhalb von Git-Branches/Lockfile · Embeddings/Vector im Compiler-Kern (AD-13) · Scope-Ausweitung auf Lease-Staleness/Recovery (Story 3.6).
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| LEASE_AKQUISE | Producer bearbeitet Area, keine Lease vorhanden | Arbeits-Branch `lease/<area>/<id>` auf Merge-Base; Lockfile geschrieben; Root-Scope-Lease dokumentiert | Lockfile existiert bereits → Lease-Hold, kein Überschreiben |
| LEASE_KONFLIKT | Zweiter Producer, Lockfile von Producer A vorhanden/Konflikt am selben Pfad | Kein stiller textueller Auto-Merge; compiler-vermittelter AD-16-Pfad mit `log.md`-Eintrag | Unentscheidbar → menschliche Eskalation (AD-17g) |
| DIRTY_TREE_SCHUTZ | Fremde uncommittete Änderung im mutierten Bereich | Schutz (Stash/Scratch-Zone) + `log.md`-Dokumentation; keine Löschung | Screen-Artefakte beim Schutz → textuell benannt |
| UNCOMMITTED_INPUT | `raw/`/`wiki/`-Working-Copy weicht von HEAD ab | Benannter Abbruch „published/committed Input erforderlich" (AD-17a) vor jeder Mutation | Keine Mutation, Bundle konsistent |
| COMMIT_BOUNDARY | Run nach Mutation + Validierung | Commit-Boundary = Mutations-Boundary; Zwischenstände nie veröffentlicht | Validierungs-FAIL → Rollback §5.3 |
</frozen-after-approval>
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3):
- Neue Sektion **§5.11** (nach §5.10, vor §6): Lease-Akquise (Branch-Form `lease/<area>/<id>`, Lockfile, Merge-Base-Disziplin — deterministisch, AD-17h), Root-Scope-Lease (AD-17b, A0-13), Dirty-Tree-Schutz (Pre-Mutation-Prüfung, Stash/Scratch-Zone, `log.md`-Dokumentation, AD-17e/f), compiler-vermittelter Merge als AD-16-Pfad (kein textueller Auto-Merge, AD-17c/A0-14), Commit-Boundary = Mutations-Boundary (§0/§5.3-Verweis unverändert), `log.md`-Eintragspflicht (Lease-Akquise, Schutz-Dokumentation, Merge-Klassifikation), Determinismus-Vertrag.
- §7 (`:357`): Lease-Vorbehalt **auflösen** („→ §5.11 verankert (Story 3.5)").
- §8 Revisionslog: **Revision 3.0** mit Abschlussklausel (AD-3, keine neue §7-Klasse, kein Standalone, kein Vertrags-Change; Lease-Staleness/Recovery bleibt → Story 3.6).
- `_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh`**neu** (re-executierbar, Muster Story-3.4-Sandbox): Mini-Git-Repo mit `wiki/`+`raw/`; Szenarien L1L6 (Lease-Akquise auf `lease/<area>/<id>`-Branch mit Lockfile · Lockfile-Konflikt → Lease-Hold · Root-Scope-Lease inkl. `log.md`/`index.md` · Dirty-Tree-Schutz mit Stash/Scratch-Zone + `log.md`-Dokumentation · kein textueller Auto-Merge bei ungleichem Pfad-Inhalt · Commit-Boundary-Verletzung → HARD-FAIL) + **Negativ-Kontrollen** (fremde uncommittete Änderung wird nie gelöscht; `??`-Sicht auf Ghost-Diffs). Harte Pass/Fail-Assertionen (exit 1), Erhaltungs-Invariante, `assert_frontmatter` (Muster Story-3.4).
- `wiki/log.md`**append** (append-only, Vertrag §5): Story-3.5-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: Key `3-5-…` → in-progress (→ done bei Story-Abschluss).
- `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/…`, `raw/…`**read-only** (AD-3). Keine Concept-Inhalts-Mutation (Demonstration per Sandbox).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — §5.11 Leasing-/Dirty-Tree-Sektion einfügen (Branch-Form, Lockfile, Merge-Base-Disziplin, Root-Scope, Dirty-Tree-Schutz, AD-17c-Merge-Pfad, Commit-Boundary-Verweis, log.md, Determinismus) · §7-Vorbehalt `:357` auflösen · §8 Revision 3.0; ohne Change an `wiki-compiler.md`/`validator.md`/`raw/`.
- [x] `sandbox-3-5/run-sandbox.sh` — Leasing-Run-Demonstrator (Akquise, Konflikt, Root-Scope, Dirty-Tree-Schutz, Merge-Verweigerung, Commit-Boundary); harte Pass/Fail-Assertionen; Exit 0.
- [x] `wiki/log.md` — Story-3.5-Eintrag (append-only): Verankerung, Sandbox-Nachweis, Statuswechsel, per-Datei-Validator-Verdikt.
- [x] `sprint-status.yaml` — Key `3-5-…` → in-progress.
**Acceptance Criteria:**
- Given ein Producer, when er einen Bereich bearbeitet, then arbeitet er auf einem `lease/<area>/<id>`-Branch und akquiriert die Lease gegen einen eindeutigen Commit-Object-Wert (AC-1; AD-17a, A0-12) — §5.11, Sandbox L1.
- Given eine vergebene Lease, when sie aktiv ist, then umfasst sie die Root-Scope inklusive `log.md`, `index.md` und aller Root-Dateien (AC-2; AD-17b, A0-13) — §5.11, Sandbox L3.
- Given eine vorliegende uncommittete Fremdänderung im mutierten Bereich, when ein Producer mutieren will, then schützt er sie (Stash/Scratch-Zone) und dokumentiert den Vorgang in `log.md` (AC-3; AD-17e, A0-16) — §5.11, Sandbox L4, Negativ-Kontrolle „nie gelöscht".
- Given zwei Branches mit ungleichem Inhalt am selben Concept-Pfad, when gemerged werden soll, then erfolgt kein stiller textueller Auto-Merge; die Auflösung ist compiler-vermittelt über AD-16 mit explizitem `log.md`-Eintrag (AC-4; AD-17c, A0-14) — §5.11, Sandbox L5.
- Given die Instruktion, when geprüft, then bleiben `schema/validator.md`/`schema/wiki-compiler.md`/`raw/` unverändert (AD-3), keine neue §7-Klasse, kein Standalone (D-3), kein neuer Frontmatter-Key, Lease-Staleness/Recovery bleibt an Story 3.6 — Validator auf Ist-Bundle SUCCESS.
## Spec Change Log
- **Review-Loop 1 (2026-08-19, step-04; 3 Layer: blind-hunter / edge-case-hunter / verification-gap):** Autofix-`patch`-Findings in `schema/compiler.md` (§5.11) und `sandbox-3-5/run-sandbox.sh`; **kein bad_spec/intent_gap**, `review_loop_iteration` bleibt 0. **Text-Fixes (§5.11):** (1) Pkt.-1-Querverweis „Pkt. 3" → „Pkt. 2" (Root-Scope-Lease; dangling ref); (2) `UNCOMMITTED_INPUT`-Name als I/O-Matrix-Szenario-Deklaration geführt und **explizit mit der §5.9-P2-Element-(1)-`INPUT_UNCOMMITTED`-Prüfung gleichgesetzt** (kein zweiter, separater Abbruch-Pfad — Namens-Kollision aufgelöst); (3) Pkt. 6 um „neueste zuerst" (Vertrag-§5-Datumsgruppe, wie §5.9 Pkt. 4) ergänzt; (4) **Lease-Freigabe (Release)** als deterministischer Pkt.-1-Sub-Bullet + Pkt.-6-Eintragstyp (e) verankert — Lockfiles häufen sich nicht (Staleness-/Recovery-Aspekte der Freigabe verbleiben Story 3.6); (5) **testbares 3.5/3.6-Seam-Kriterium** (3.5 = committed-state-deterministisch / 3.6 = Zeit- bzw. Umgebungs-Zustands-Frage) ergänzt; (6) §7-Enum: AD-17d/A0-15 als **Norm-Rückverweis** gekennzeichnet (Staleness ist nicht Teil der §5.11-Auflösung); (7) §8-Revision 3.1. **Sandbox-Fixes:** (a) log-Helfer: Header-Bullet-**Paar** je Eintrag statt `\n##`-je-Zeile (Datumsgruppen-Format, Vertrag §5; kumulativer Append — neue D2-Szenario „Kumulative Koordinations-Aufzeichnung": 4 Entscheidungen in EINEM log.md, neueste zuerst); (b) L2-Lease-Hold hart über Exit-Status assertet (kein toter Code); (c) L4: `UNCOMMITTED_INPUT`-Abbruch-**Wirkung** hard asserted (HEAD unverändert, nur der fremde alpha-Pfad weicht ab, keine Mutation) + **Restore-Weg** (Scratch→Bundle) demonstriert; `^ M `-porcelain-Form werkzeugunabhängig; (d) L6: `BROKEN_HEAD`/`COMMIT_AFTER_RUN`-Tautologie beseitigt (`BUILD_HEAD`), Frontmatter-Validierungs-FAIL via Subshell (Exit-Propagation), Restore-Assertion korrekt; (e) L5: `log.md`-Klassifikations-Label als Header-Bullet asserted (kein bloßer Substring); (f) L3: `??`-Sicht nicht vacuous — neue legal angelegte `wiki/beta.md` als erlaubte Neu-Anlage durch die Invariante klassifiziert; (g) N1: „Pkt. 8" auf §5.10 Pkt. 8 (Orphan-Nachbarschaft) statt §5.9-Pkt.-8 quasi-finalisiert. **Defers**`deferred-work.md` (holder_id-Quelle; baseline_commit-Resolver; eigener `# Log`-Stand vs. Akkumulator; Revisionsnummer-1.0-Defer). **Nachweis:** Sandbox Exit 0, 21 harte PASS-Assertionen (L1L6, N1, D1, D2); AD-3 unverändert.
## Design Notes
**Warum §5.11, nicht §0-Erweiterung:** Die Commit-Boundary (§0/§5.3) ist der *Schutz bei Einzel-Validierung*; Leasing/Dirty-Tree ist die *Koordination vor/nach der Mutation an Branches* — eine eigenständige Dimension, die die Branch-/Lockfile-/Stash-Semantik beschreibt. §5.9/§5.10 (Update/Synthese) beschreiben die *Inhalts-Mechanik*; §5.11 beschreibt die *Werkzeug-Koordination*, die um jede Mutations-Form herum läuft. Das spiegelt den §7-Vorbehalt (`:357`) als eigenes Thema.
**Grenzfläche zu 3.6:** Story 3.5 deckt Akquise, Konflikt-Hold, Root-Scope und Dirty-Tree-Schutz. Staleness (TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery) bleibt der Story 3.6 vorbehalten — §5.11 verweist auf 3.6 und mutiert deren Mechanik nicht.
**Lease-Schutz ohne neue Fields:** Die Lease lebt in **Git/Datei-Ebene** (Branch-Name, Lockfile) — nicht in Concept-Frontmatter (Vertrag §3.1–§3.7 unverändert, keine neue §7-Klasse). Ein Lockfile realisiert semantisch identisch in jedem Adapter (A0-12); die Satzform präzisiert das Format, damit deterministisch (AD-17h).
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. `bash _bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh` — expected: alle Szenarien L1L6 + Negativ-Kontrollen mit harten Pass/Fail-Assertionen, Erhaltungs-Invariante erzwungen, Exit 0.
2. `grep -n "Leasing & Dirty-Tree-Schutz\|Revision 3.0" schema/compiler.md` — liefert die Leasing-Sektion + Revisionslog-Eintrag.
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`.
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (unverändert, keine Inhalts-Mutation).
**Manual checks:**
- §5.11 trägt Branch-Form `lease/<area>/<id>`, Lockfile, Merge-Base-Disziplin, Root-Scope-Lease, Dirty-Tree-Schutz (Stash/Scratch-Zone + log.md), AD-17c-Merge-Pfad, Commit-Boundary-Verweis, Determinismus; §7-Vorbehalt `:357` auf §5.11 aufgelöst; §8-Rev-3.0 mit Abschlussklausel; kein `validator.md`/`wiki-compiler.md`/`adapters/`/`raw/`-Diff; kein neuer Standalone (D-3) / keine neue §7-Klasse / kein neuer Frontmatter-Key; Lease-Staleness bleibt an 3.6; `wiki/log.md`-Eintrag datiert mit Story-3.5-Semantik + Sandbox-Nachweis + Statuswechsel + Verdikt; `sprint-status.yaml` konsistent.
## Suggested Review Order
**Leasing-Koordinations-Dimension (§5.11)**
- Einstiegspunkt: die verbindliche Verankerung der Koordinations-Dimension — Lease-Akquise, Root-Scope, Dirty-Tree-Schutz, Merge- und Determinismus-Vertrag
[`compiler.md:303`](../../schema/compiler.md#L303)
- Pkt. 1 Lease-Akquise: Branch-Form, Lockfile, Merge-Base-Disziplin, deterministische Lease-Freigabe
[`compiler.md:307`](../../schema/compiler.md#L307)
- Pkt. 2/3 Root-Scope-Lease + Dirty-Tree-Schutz: Stash/Scratch-Zone, UNCOMMITTED_INPUT-Abbruch (nicht-vacuous)
[`compiler.md:312`](../../schema/compiler.md#L312)
- Pkt. 47: compiler-vermittelter Merge (AD-16), Commit-Boundary, log.md-Pflicht, Determinismus-Vertrag & 3.5/3.6-Seam
[`compiler.md:317`](../../schema/compiler.md#L317)
- §8-Revisionslog „Revision 3.0" mit Abschlussklausel (AD-3, keine neue §7-Klasse, kein Standalone)
[`compiler.md:416`](../../schema/compiler.md#L416)
**Nachweis: Sandbox & Story-Protokoll**
- Szenario-Matrix L1L6 + Negativ-Kontrollen als re-executierbarer Beweis der Invarianten
[`run-sandbox.sh:6`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L6)
- L1 Lease-Akquise / L2 Lease-Hold: hart assertierte Akquise- und Fehlerpfade
[`run-sandbox.sh:269`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L269)
- L4 Dirty-Tree-Schutz: Pre-Mutation-Prüfung, Restore-Weg, „nie gelöscht"-Assertion
[`run-sandbox.sh:379`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L379)
- L6 Commit-Boundary: Zwischenstand nie veröffentlicht, Validierungs-FAIL → Rollback
[`run-sandbox.sh:476`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L476)
- Story-3.5-`wiki/log.md`-Eintrag: Verankerung, Sandbox-Nachweis, Statuswechsel, Verdikt (append-only)
[`log.md:3`](../../wiki/log.md#L3)
- 4 Defers (holder_id-Quelle, baseline_commit-Merge-Base, Sandbox-Akkumulator, native `git stash`) — Homes Story 3.6/3.8
[`deferred-work.md:416`](../../_bmad-output/implementation-artifacts/deferred-work.md#L416)
@@ -0,0 +1,151 @@
---
title: 'Story 3.6 — Lease-Staleness & Recovery-Basis absichern'
type: 'feature'
created: '2026-08-19'
status: 'done'
baseline_commit: '895b006f7f2bc951cecc09d7d27d90e28fe2a102'
review_loop_iteration: 0
context:
- '_bmad-output/implementation-artifacts/epic-3-context.md'
---
<!-- Target: 9001300 tokens. Above 1600 = high risk of context rot.
Never over-specify "how" — use boundaries + examples instead.
Cohesive cross-layer stories (DB+BE+UI) stay in ONE file.
IMPORTANT: Remove all HTML comments when filling this template. -->
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** Ein abgebrochener Run hinterlässt eine Lease, die §5.11 (Lease-Hold) akquiriert, doch deren Freigabe (Release) nur nach committetem Run erfolgt. Ohne Staleness-Mechanik blockiert eine verwaiste Lease alle nachfolgenden Runs dauerhaft — Wissen bleibt blockiert (AD-17d/A0-15; §5.11 Pkt. 1 „verbleibt bis zum Staleness-/Recovery-Mechanismus der Story 3.6", §5.11 Pkt. 7-Seam: 3.6 = Zeit-/Umgebungs-Zustands-Frage, §7 verbleibendes 3.x-Thema).
**Approach:** Neue Sektion **§5.12 „Lease-Staleness & Recovery-Basis (Story 3.6)"** (nach §5.11, vor §6): **TTL + Lease-Registrierung im Clone-Root-State** deterministisch einführen — das TTL-Ablauf-Kriterium ist **generationen-basiert** (ohne Wanduhr, AD-17h-konform; die A0-20 zugelassene `at`-Notation bleibt dokumentierend) — plus **Verwaist-Handling** (Übernehmen/Stale-Markieren mit `log.md`-Protokollierung) und die **`raw/`-Recovery-Basis** (Zugriffs-/Consistency-Basis, AD-3 unverändert). Die vier Story-3.5-Defers mit Home 3.6 werden aufgegriffen (holder_id-Ableitung, baseline_commit-Diskrepanz-Regel, kumulative Registrierung über Runs, native `git stash`-Variante + Verwaist-Übungs-Thema). Verwaiste/hängende Leases werden dabei **nie gelöscht** (AD-17e) und `raw/` **nie verändert** (AD-3).
## Boundaries & Constraints
**Always:**
- Nur `schema/compiler.md` (neue §5.12 + §7-Auflösung + §8 Revisionslog-Revision 3.1) mutiert (D-3); `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` read-only (AD-3). Kein Standalone (D-3), keine neue §7-Invaliditätsklasse, kein Vertrags-Change.
- Deterministisch aus dem committeten Git-State ableitbar (AD-17h/A0-19): Registrierungs-Aufbau, Ablauf-Kriterium, Verwaist-Klassifikation, log.md-Texte; **kein Wanduhr-Timestamp im Lockfile- oder Registrierungs-Format** (A0-20-Konvention, §5.11 D1).
- `raw/` ist die Zugriffs- und Consistency-Recovery-Basis (AD-17d/A0-15) und bleibt bei jedem Vorgang unverändert (AD-3).
- Verwaiste/hängende Leases werden **nie still gelöscht** und fremde uncommittete Änderungen **nie** als Seitenwirkung entfernt (AD-17e).
**Ask First:**
- Wanduhr-basierte TTL (Timeout nach Kalenderzeit) statt Generationen-Ablauf — wäre ein deterministischer Bruch, weil wanduhr-`at` von der Laufzeit abhängt; die A0-20-Konvention ließe einen dokumentierenden Zeitstempel zu, ein **streng durchsetzendes** Zeit-TTL wäre Ask-First.
- Konflikt zweier gültiger Leases, die nicht per Generationen-Reihenfolge auflösbar ist (Older-wins außerhalb der definierten Klasse).
**Never:**
- `raw/`-Inhalte verändern (AD-3); uncommittete fremde `wiki/`-Änderungen löschen (AD-17e); textuelle Auto-Merge bei Branch-Konvergenz (AD-17c); Leasing über `lease/<area>/<id>`-Branches hinaus; Zeitstempel/`now`-Wanduhr als Leasing-Steuer-Größe; neuer Frontmatter-Key für Lease-Metadaten (Vertrag §3.1–§3.7, §7); Prädikat-/Format-Erweiterung des Validators; EOF-`# Log`-eigener Stand in der Registry (`wiki/log.md`-Akkumulator ist alleiniger Aufzeichnungs-Ort, Vertrag §5).
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| STALE_ABLAUF | uncommittete Lease nach abgebrochenem Run; nachfolgender Run trifft auf sie | Lease gilt als stale, blockiert nicht; Übernahme oder Stale-Markierung mit log.md-Eintrag | verwaiste Lease nie gelöscht (AD-17e); Abbruch „published/committed Input erforderlich" (AD-17a, §5.11 Pkt. 3) unverändert |
| VERWAIST_UEBERNEHMEN | neuer Run findet verwaiste Lease | übernimmt die Lease gegen die erneute Merge-Base-Prüfung, protokolliert die Übernahme | bei bestehendem Konflikt → AD-16-Pfad / menschliche Eskalation (AD-17g, §5.11 Pkt. 4) |
| VERWAIST_STALE_MARKIEREN | verwaiste Lease, Übernahme nicht sinnvoll | als stale markiert (Registry-Marker) und protokolliert; Blockade aufgehoben | Registrierungs-Invariante (Gen > erzeugend oder gleiche Gen, hält den sichtbar höchsten Reg-Generator) |
| RECOVERY_RAW_BASIS | uncommittete Änderungen aus abgebrochenem Run wiederherstellen | `raw/` (immutable, AD-3) als Zugriffs-/Consistency-Basis; Konformität re-bestätigt | `raw/` unverändert; kein `git diff`-/SHA-256-Beweis gegen `raw/`-Inhaltsebene auf dem realen Ist-Baum (Grenzen beachten: EC-1 auf sandbox-Evidenzwege beschränkt) |
| HOLD_GEGEN_HEAD | Lease existiert; zu mutierender Bereich liegt nicht unter der akquirierten Lease | Lease-Hold gegen HEAD; keine Mutation; kein Abbruch-Text | §5.11 Pkt. 1 (Lease-Hold) unverändert |
| DIRTY_TREE_UNVERWAIST | uncommittete fremde Änderung (Verdacht) ohne verwaiste Lease | §5.11 Pkt. 3 (Schutz/Scratch-Zone, nie gelöscht) greift; keine Staleness-Marke | textuell benannt (NFR-4); Restore-Weg dokumentiert |
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3): neue Sektion **§5.12** „Lease-Staleness & Recovery-Basis (Story 3.6)" (nach §5.11, vor §6; §5.11-Seam-Kriterium S-1 und Pkt.-7-Text bleiben unverändert); §7 (`:376`-Bullet) Staleness/Recovery-Vorbehalt **auflösen** („in §5.12 verankert (Story 3.6)"); §8 Revisionslog **Revision 3.1** mit Abschlussklausel; §8 Normreferenzen AD-17d/A0-15 von reiner Story-Zuordnung auf **§5.12-Anker** angehoben (`AD-17d (Lease-Staleness, §5.12)`); konsistente `§5.12`-Verweise in §5.11-Pkt.-1/Pkt.-7/§7 (nur Wortlaut, kein neuer Inhalt). Anker `§5.12`-Sektion: Z. nach §5.11-Ende (nach Z. 320); §7-Bullet Z. 376; §8-AD-17d Z. 387, A0-15 Z. 389; Revisionslog nach Z. 416.
- `wiki/log.md`**append** (append-only, Vertrag §5): Story-3.6-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
- `_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh`**neu** (re-executierbar, Muster sandbox-3-5, Exit 0): Szenarien STALE-1..STALE-6 (s. Design Notes), harte Pass/Fail-Assertionen, Erhaltungs-Invariante erzwungen, keine Berührung des realen Ist-Baums.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: `3-6-…` `backlog``in-progress`; `last_updated` (Format `MM-DD-YYYY HH:MM`, HEAD-Präzision).
- `_bmad-output/implementation-artifacts/deferred-work.md`**append**: erneut aufgreifen der vier Story-3.5-Defers Home 3.6 (holder_id, baseline_commit, Registry-Akkumulator, git-stash) + ggf. neue Story-3.6-Defers.
- `_bmad-output/implementation-artifacts/epic-3-context.md`, `spec-3-5-…md`**read-only** (continue-context; die epics-/spec-3-5-acme-Kette unverändert).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` -- §5.12 einfügen (§5.11 Pkt. 1/7 + §7 + §8 + Revisionslog-3.1 + Abschlussklausel); Wortlaut nur, kein neuer Prädikat-/Format-Key
- [x] `schema/compiler.md` -- Revision 3.1 in §8 belegen (Anker-Zahlen + Abschlussklausel) und §5.11-`/§7`-Verweise §5.12-wortgleich
- [x] `_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh` -- Szenarien STALE-1..6 + Erhaltungs-Invariante (vertraut auf §5.9-Pkt.-5-Probe-Muster), Exit 0
- [x] `_bmad-output/implementation-artifacts/deferred-work.md` -- vier Story-3.5-Defers Home 3.6 als aufgegriffen markieren + ggf. neue
- [x] `wiki/log.md` -- (Implementierung) Story-3.6-Eintrag, `sprint-status.yaml` → in-progress; Review-Abschluss `done` im Review-Schritt (Workflow-Konvention)
**Acceptance Criteria:**
- Given ein abgebrochener Run, when uncommittete Leases hinterlassen wurden, then gelten sie als stale (TTL plus Lease-Registrierung im Clone-Root-State), blockieren keine nachfolgenden Runs, und `raw/` bleibt Zugriffs-/Consistency-Basis (AD-17d, A0-15; AC-1/AC-2/AC-4).
- Given ein neuer Run, when er eine verwaiste Lease vorfindet, then kann er sie übernehmen oder als stale markieren und protokollieren (AC-3; Alt-Branch nie gelöscht, AD-17e).
- Given ein Story-3.5-Defer mit Home 3.6, when umgesetzt, then ist der Story-3.5-Defer-Bezug nachgezeichnet (holder_id/baseline_commit/Registry-Akkumulator/git-stash; Defer-`### Aufgegriffen`-Block-append).
## Spec Change Log
_Noch leer — wird von step-04 während der Review-Loops befüllt (append-only Konvention)._
- **Review-Loop 1 (2026-08-19, Step-04-Review, 3 Layer, kein Loopback; review_loop_iteration bleibt 0):** Stapel Autofix-Patches — Terminologie-Inversion „jünger→älter" (§5.12 Pkt. 1/3/6 + §8-Revisionslog-3.1-Text + log.md + Spec-Design-Notes; Gen kleiner = älter, ältere/niedrigere Erzeugungs-Generation = stale), Generations-Quellen-Präzedenz deterministisch gepinnt (§5.12 Pkt. 1: committeter `lease-granite-root`-Marker gewinnt, sonst `lease/`-Baumableitung, sonst Startwert `gen = 0`), STALE-1-kern generationen-basiertes TTL gegen dieselbe `id` (HOLD→stale→Akquise), STALE-3-Re-Akquise nach Stale-Markierung, STALE-5-Same-id-A/B (aktiv verweigert / stale-Markierung erlaubt), holder_id-genuine Ableitung (STALE-2), baseline_commit-Merge-Base-Diskrepanz real (Descendant-Commit, `git merge-base` gewinnt), STALE-4-**lesender**-`raw/`-Bind (Zugriffs-/Consistency-Basis, aufgelöste `raw/`-Ressourcen-Referenz), Marker-Idempotenz-Regex, STALE-6-Full-State-Invariante (Registry/Lockfiles/Lease-Branches/log.md SHA-256 byte-identisch), log_bullet-Datumsgruppen-Konvention; **1 neuer Defer** (Branch-Ref-Schicksal nach Übernahme/Stale-Markierung → deferred-work.md). Sandbox re-executiert: Exit 0, 12 harte PASS; AD-3-Readonly + `wiki/`-Scope nur `wiki/log.md` verifiziert; alle Schritte der `## Verification`-Sektion re-ausgeführt.
</frozen-after-approval>
## Design Notes
**Warum §5.12 als eigene Sektion, nicht §5.11-Erweiterung?** §5.11 (3.5) ist strikt committed-state-deterministisch (AD-17h) und hält das Seam-Kriterium S-1 explizit fest — 3.6 verlässt den committeten Zustand (Zeit-/Umgebungs-Zustands-Frage). Das nicht als Punkt an §5.11 anzuhängen, sondern als Eigen-Sektion zu verankern, spiegelt die gekoppelte Abschlussklausel (kein Leasing-Scope, Staleness → Story 3.6) und hält §5.11 als Denk-Basis des Reviews erreichbar.
**Zeit ohne Wanduhr zwingend? (Beste alternative Bestätigung der Voranalyse):** AD-17d nennt als erste Option „TTL + registration of the lease in the clone-root state". Wall-clock-TTL ist deterministisch unhaltbar (AD-17h/A0-19: identische Artifakte bei gleichem Git-State). A0-20 lebt die `at`-Notation als dokumentierendes Konzept (Master-Satz `generated.at`), nicht als steuerndes Leasing-Element; der `at`-Handoff wäre Ask-First. Deshalb: **generationen-basiert** — die Registrierung trägt einen monotonen Run-Generator (ein Highlight-`generation`-Zähler je Clone-Root, aus `lease/`-Baumableitung oder einem Mono-Commit-`lease-granite-root`-Marker), ein Run **älterer (geringerer) Erzeugungs-Generation** gilt als stale (TTL-Ablauf-Äquivalent, Gen kleiner = älter, §5.12 Pkt. 1), ohne Wanduhr.
**Der Story-3.5-Seam (S-1) bleibt unbewegt:** „3.5 = committed-state-deterministisch, 3.6 = Zeit-/Umgebungs-Zustands-Frage (TTL-Ablauf, verwaiste/hängende Leases, Registrierung, `raw/`-Recovery)". §5.12 greift ihn auf, ohne ihn zu ändern — die Review-Layer prüfen die Fuge.
**Defer-Handoff (exakter Pfad):** vier Einträge `deferred-work.md` (Z. 420437, Home Story 3.6): holder_id-Quelle (→ §5.12 Pkt. 2, deterministischer Default), baseline_commit-Merge-Base-Diskrepanz-Regel (→ §5.12 Pkt. 4, Vereinheitlichung), Sandbox-log-Akkumulator (→ §5.12 Pkt. 6, cumulative über Runs; kein eigener `# Log`-Stand), native `git stash`-Variante (→ §5.12 Pkt. 5-Recovery, als Sandbox-Doppel abgebildet). Neu-Defers möglich.
**Sandbox (STALE-1..STALE-6, `bash run-sandbox.sh`, Exit 0):**
- STALE-1: abgebrochener Run erzeugt Lease + Registrierung ohne Release; nachfolgender Run findet sie → stale, kein Blockade-fail
- STALE-2: verwaiste Lease übernehmen (± Merge-Base-Prüfung), Übernahme protokolliert
- STALE-3: verwaiste Lease als stale markieren (Registry-Marker), Blockade aufgehoben, `log.md`-Eintrag
- STALE-4: `raw/`-Recovery-Basis — Ablauf, der `raw/`-Inhalte unveränderlich lässt (Assertion), Konsistenz belegt
- STALE-5: Registrierungs-Invariante (Gen vollständig, höchster-Reg-Invariant) — Verwaiste Kanten nie gelöscht, HOLD-Gegen-HEAD deterministisch
- STALE-6: Determinismus Zwei-Run (AD-17h/A0-19) — gleicher Baum-Input → identische Registrierungs-/Markierungs-Outputs
(Code-/Zahlgenauigkeiten: Die obigen Szenario-Labels sind Fixierung der I/O-Matrix; die Implementierung trägt die harten Assertionen in der Sandbox.)
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. `bash _bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh` — expected: STALE-1..STALE-6 harte PASS/Fail, Erhaltungs-Invariante erzwungen, Exit 0.
2. `grep -n "§5.12\|Revision 3.1" schema/compiler.md` — liefert §5.12-Sektion + Revisionslog-Eintrag; `grep -n "Lease-Staleness & Recovery-Basis" schema/compiler.md` — die §5.12-Überschrift wortgleich (inkl. §5.11-Pkt.-7-Seam-Satz „Lease-Staleness/Recovery … TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery").
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`.
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (unverändert, keine Inhalts-Mutation).
5. Auf den **`wiki/`-Scope begrenzt** (`git status --porcelain -- wiki/`): ausschließlich `wiki/log.md` (dieser Eintrag) — Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt; `sprint-status.yaml`/`deferred-work.md`/`sandbox-3-6/` liegen außerhalb `wiki/` und sind nicht Teil der Diff-Probe (wie §5.9 Pkt. 5-Doku, Story-3.5-Präzedenz).
**Zu beachten (beim step-04-Review):** (a) §5.11 Pkt. 1 „verbleibt bis Story 3.6" und Pkt. 7-Seam S-1 (sowie §7-Bullet) müssen textuell **unverändert** bleiben, wenn §5.12 auf sie verweist (Haltbarkeits-Fuge); (b) `raw/`-Berührung ist auf Sandbox-Evidenzwege beschränkt (kein realer Ist-`raw/`-Beweis nötig); (c) die vorhandene §8-Revisionslog-Umsetzungsnummer ist **Revision 3.0** (Story 3.5); die Spec-`-Change-Log`-Notiz „§8-Revision 3.1" der Story 3.5 (Review-Loop-1-Fix) wurde **nicht real** als eigener Revisionslog-Eintrag übernommen (compiler.md-Revisionslog enthält keine Revision 3.1; grep-verifiziert) — **Revision 3.1 ist für Story 3.6 frei**.
## Suggested Review Order
**§5.12-Leasing-Staleness-Instruktion**
- Einstieg: §5.12-Sektion — Staleness-/Recovery-Dimension, Kern der Story (Registrierung, TTL, Klassifikation).
[`compiler.md:322`](../../schema/compiler.md#L322)
- Registrierung & generationenbasiertes TTL mit gepinnter Quellen-Präzedenz (Marker/Baum/Startwert `gen=0`).
[`compiler.md:326`](../../schema/compiler.md#L326)
- Verwaist-Klassifikation — Übernehmen gg. erneute Merge-Base-Prüfung / Stale-Markieren, nie gelöscht (AD-17e).
[`compiler.md:330`](../../schema/compiler.md#L330)
- baseline_commit-Merge-Base-Diskrepanz-Regel — git-merge-base gewinnt, notierter SHA = Sekundär-Fingerprint.
[`compiler.md:335`](../../schema/compiler.md#L335)
- `raw/`-Recovery-Basis & native `git stash`-Variante — raw/ immutable (AD-3), EC-1-Grenze.
[`compiler.md:337`](../../schema/compiler.md#L337)
- Registry-Invariante & kumulativer Aufbau — Gen-Invariante, wiki/log.md alleiniger Aufzeichnungs-Ort.
[`compiler.md:339`](../../schema/compiler.md#L339)
- log.md-Eintragspflicht & Determinismus-Vertrag — datumsgruppiert, kein Wanduhr-Timestamp steuert.
[`compiler.md:341`](../../schema/compiler.md#L341)
**§7/§8-Nachweis**
- §7-Leasing-Enum-Bullet — Staleness/Recovery „in §5.12 verankert (Story 3.6)", §5.11-Rückverweis unverändert.
[`compiler.md:397`](../../schema/compiler.md#L397)
- §8-Revisionslog Revision 3.1 — Verankerung, Abschlussklausel, AD-17d/A0-15 auf §5.12-Anker.
[`compiler.md:438`](../../schema/compiler.md#L438)
**Sandbox-Nachweis**
- Sandbox STALE-1..STALE-6 — re-executierbar, harte PASS/Fail, Erhaltungs-Invariante §5.9 Pkt. 5 (Exit 0, 12 PASS).
[`run-sandbox.sh:1`](../../_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh#L1)
**Story-Protokoll/Defers**
- log.md — Story-3.6-`done`-Eintrag (Review-Abschluss-Konvention).
[`log.md:4`](../../wiki/log.md#L4)
- sprint-status.yaml — `3-6-…` → done.
[`sprint-status.yaml:59`](../../_bmad-output/implementation-artifacts/sprint-status.yaml#L59)
- deferred-work.md — 4 aufgegriffene Story-3.5-Defers + 1 neuer Story-3.6-Defer (Branch-Ref-Schicksal).
[`deferred-work.md:417`](../../_bmad-output/implementation-artifacts/deferred-work.md#L417)
@@ -0,0 +1,161 @@
---
title: 'Story 3.7 — Reason/Mutate-Trennung & Konsistenz-Endzustand sicherstellen'
type: 'feature'
created: '2026-08-20'
status: 'done'
baseline_commit: '861e65f628001ffe8da05d0fd6834b7bb2689202'
review_loop_iteration: 1
context:
- '_bmad-output/implementation-artifacts/epic-3-context.md'
---
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** AD-6/A0-7 trennen einen Compilation Run **logisch** in Analyse → Änderungsplanung → Mutation → Validierung, doch die Compiler-Instruktion führt die Phasen-Trennung nirgends explizit als **durchsetzbare Disziplin** — die feste Ablaufstruktur in §0 zählt nur Phasen (0)(5), und die Plan-Vorprüfung (P2-Block) ist ein Baustein in §5.9 Pkt. 6, ohne als **Änderungsplanungs-Phase mit Endzustands-Garantie** ausgewiesen zu sein. Folge: Ein teilweise fehlgeschlagener Run könnte Zwischenstände hinterlassen, deren Konsistenz (AD-6: „Der beobachtbare Endzustand muss ein konsistentes Bundle sein") nur implizit über §6-Rollback abgesichert ist. A0-7-AC-2: „**Given** ein Fehler während der Mutation, **When** der Run abbricht, **Then** bleibt der beobachtbare Endzustand des Bundles konsistent".
**Approach:** Neue Sektion **§5.13 „Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)"** (nach §5.12, vor §6): die **logische Phasen-Disziplin** von AD-6 verbindlich in die Instruktion heben — der P2-Vorprüf-Block (§5.9 Pkt. 6) wird als **Änderungsplanung** (Analyse-Ergebnis → konsistenter Plan: Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheits-Liste, Struktur-Erhaltung) institutionalisiert; die §§5/§5.9/§5.10-Mutationsmechanik wird als **Mutationsphase** unter **Veränderungs-Sperre nach Phasenabschluss** (Plan freeze), die §§6/§5.3-Rollback-Maschinerie als **Validierungsphase** mit **Zustands-Restaurations-Invariante** (Post-Rollback-Zustand == Baseline) geschlossen. **Ohne eigene Workflow Engine** (D-3, AD-6): die Trennung ist eine textuelle Ausführungs-Disziplin in einer Session. Endzustands-Konsistenz wird als **beobachtbare, über Git-State überprüfbare Eigenschaft** fixiert — nach einem abgebrochenen/fehlgeschlagenen Run ist der Bundle-Zustand der Baseline (oder der einer abgeschlossenen, valide committeten Mutation).
## Boundaries & Constraints
**Always:**
- Nur `schema/compiler.md` (neue §5.13 + §7-Auflösung + §8 Revisionslog-Revision 3.2) mutiert (D-3); `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` read-only (AD-3). Kein Standalone (D-3), keine eigene Workflow Engine (AD-6), keine neue §7-Invaliditätsklasse, kein Vertrags-Change, kein neuer Prädikat-/Format-Key, kein neuer Frontmatter-Key.
- Deterministisch aus dem committeten Git-State ableitbar (AD-17h/A0-19): Plan-Inhalt, Phasen-Reihenfolge, Rollback-Trigger und Post-Zustand; **keine Wanduhr/`now`-Steuerung** der Phasen-Disziplin (weder Planung noch Rollback hängen von Kalenderzeit ab).
- Endzustands-Konsistenz ist eine **beobachtbare, über Git prüfbare** Eigenschaft: Post-Rollback-Diff leer gg. Baseline bzw. Bundle == valide committeter Zustand des aktuellen Runs (Commit-Boundary = Mutation-Boundary, AD-17f, §0/§5.3).
- Die **vier** AD-6-Phasen decken sich **logisch** mit der §0-Ablaufstruktur (Analyse ≈ §1/§3.2, Änderungsplanung ≈ §5.9 Pkt. 6-P2-Block, Mutation ≈ §§45, Validierung ≈ §6); keine neue $Phasen-Nummerierung im §0-Listentext, §0 bleibt die deterministische Ausführungs-Folge.
**Ask First:**
- Einführung einer echten Planungs-Artefakt-Datei (separater Plan als persistentes Verzeichnis-Artefakt) statt des textuell festgehaltenen P2-Block-Plans — wäre eine neue Datei-/Baum-Regel außerhalb der Erhaltungs-Invariante.
- Änderung des §0-Phasen-Listentextes (Umformulierung der festen Folge) oder zusätzliche Phase — §0 bleibt Takt-Quelle; §5.13 ordnet nur logisch zu.
- Plan-freeze mit maschinellem Vergleich geplanter vs. tatsächlicher Mutationen über den §5.9-Pkt.-5-Diff-Selbsttest hinaus.
**Never:**
- `raw/`-Inhalte verändern (AD-3); uncommittete fremde `wiki/`-Änderungen löschen (AD-17e); textuelle Auto-Merge bei Branch-Konvergenz (AD-17c); Wanduhr/`now`-Zeitstempel als Phasen- oder Rollback-Steuer-Größe; eigener `# Log`-Stand in der Registry (`wiki/log.md`-Akkumulator ist alleiniger Aufzeichnungs-Ort, Vertrag §5); Standalone-Compiler/eigene LLM-Runtime (D-3, AD-11); Änderung an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`.
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| PLAN_BEABSICHTIGT | erkannte Erkenntnis + bestehende Reconcile-Kandidaten; P2-Block-Ausführung | konsistente Änderungsplanung: Input-Zustand ⊇ Zuwachs committet, Ziel-Pfade ⊆ KandidatenNeu-Anlage, Quellen-Existenz (EC-1), Betroffenheits-Liste (§3 Pkt. 2), Struktur-Erhaltung; Plan wird textuell festgehalten | Plan-Defizit benannt (NFR-4); fehlgeschlagene Vorprüfung ⇒ keine Mutation (Kette: Abbruch vor Mutationsphase) |
| PLAN_FREEZE | Mutationsphase beginnt | Änderungsplanung ist abgeschlossen; **Veränderungs-Sperre**: kein Plan-Gegenstand außerhalb der erlaubten Pfad-Menge (Kandidatenliste Neu-Anlage `log.md` `index.md`) wird mutiert | Verletzung = Ghost-Diff (§5.9 Pkt. 5): zurückrollen, textuell benannt |
| MUTATION_ABBRUCH | Fehler während der Mutationsphase | Run bricht ab; beobachtbarer Endzustand: **Zustands-Restaurations-Invariante** — Post-Rollback-Diff gg. Baseline leer / Zustand == Baseline; `raw/` unverändert (AD-3) | §5.3-Pkt.-3/§6-Pkt.-3-Rollback-Mechanik greift; Ghost-Diff-Rollback (§5.9 Pkt. 5) greift; beides deterministisch aus Git-Teilen |
| MUTATION_TEILFOLGE | mehrere geplante Mutationen, nur ein Teil ausgeführt | Rollback stellt Baseline wieder her; kein Teilerfolg wird als fertige Mutation veröffentlicht (Commit-Boundary = Mutation-Boundary) | Textuelle Failure-Benennung (NFR-4); Phasen-Disziplin unverändert |
| VALIDATION_FAIL | Validierung meldet FAIL | Run als gescheitert; §6-Pkt.-3-Rollback; **keine** weiteren Mutationen; Endzustand konsistent | Rollback gemäß §5.3/§6; `raw/` unverändert |
| VALIDATION_SUCCESS | alle `wiki/`-Dateien SUCCESS | Mutationen als Ganzes committen; Konsistenz = valide committeter Zustand; §6-Pkt.-4-Ausführungs-Nachweis | Commit erst nach Diff-Selbsttest ohne Ghost-Diff (§5.9 Pkt. 5); kein Wanduhr-Zeitstempel |
| KEINE_EIGENE_ENGINE | Anforderung „keine separate Workflow Engine" (AD-6) | logische Trennung in einer Session, kein neuer Prozess/Server/MCP | D-3/AD-11 unverändert; nichts Steuerndes wird gebaut |
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3): neue Sektion **§5.13** „Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)" (nach §5.12, vor §6; Fixierung: §5.12-Ende Z. 341, §6-Start Z. 364 — neue Sektion zwischen beide, §5.13 Z. 343362); §0-Phasen-Listentext **unverändert** (Takt-Quelle; §5.13 ordnet nur logisch zu); §5.9-Pkt.-6-P2-Block **Wortlaut-unverändert**, wird als Änderungsplanung referenziert (Rückverweis, keine Doppel-Instruktion); §6-Pkt.-3/§5.3-Rollback **Wortlaut-unverändert**, als Validierungsphase + Zustands-Restaurations-Invariante geschlossen; §7 (`Z. 390ff.`) Reason/Mutate-Vorbehalt **als neuer §7-Bullet** „in §5.13 verankert (Story 3.7)" (AD-6, A0-7, AC-1/2/3/4); §8-Normreferenz-`AD-6` (`Z. 430`) von reiner Story-Zuordnung auf **§5.13-Anker** angehoben. Anker (IST-Zeilen nach Implementierung): §5.13-Sektion Z. **343**; §7-Bullet nach Z. **420** (nach „Relevanzbestimmung"-Bullet); §8-AD-6 in Z. **430**; Revisionslog-Eintrag **Revision 3.2** nach Z. **461**.
- `wiki/log.md`**append** (append-only, Vertrag §5): Story-3.7-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
- `_bmad-output/implementation-artifacts/sandbox-3-7/run-sandbox.sh`**neu** (re-executierbar, Muster sandbox-3-6, Exit 0): Szenarien CONSIST-1..CONSIST-7 (s. Design Notes), harte Pass/Fail-Assertionen, Erhaltungs-/Restaurations-Invariante erzwungen, keine Berührung des realen Ist-Baums.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: `3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell` `backlog``review` (Implementierungs-Flip `in-progress` + Review-Start-Flip `review` im selben Commit; finaler `done`-Flip im Step-05-Status-Sync); `last_updated` (Format `MM-DD-YYYY HH:MM`, HEAD-Präzision).
- `_bmad-output/implementation-artifacts/deferred-work.md`**append** (append-only): ggf. Story-3.7-Defers (z. B. maschineller Plan-vs-Ist-Vergleich über den §5.9-Pkt.-5-Diff-Selbsttest hinaus), nach Prüfung der vorhandenen Defer-Liste (keine bereits auf Story 3.7 gedeuteten Einträge; Stand: keine Home-3.7-Defers vorhanden).
- `_bmad-output/implementation-artifacts/epic-3-context.md`, `spec-3-6-…md`**read-only** (continue-context; Kette unverändert).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — §5.13 einfügen (nach §5.12, vor §6); §0/§5.9-Pkt.-6/§6 Wortlaut-unverändert referenzieren; kein neuer Prädikat-/Format-/Frontmatter-Key
- [x] `schema/compiler.md` — Revision 3.2 in §8 belegen (AD-6/A0-7 auf §5.13-Anker, Abschlussklausel) und §7-Bullet Reason/Mutate in §5.13-Verankerung
- [x] `_bmad-output/implementation-artifacts/sandbox-3-7/run-sandbox.sh` — Szenarien CONSIST-1..7 (Plan-Erzeugung, Freeze-Sperre, Rollback-Restaurations-Invariante, Teilerfolg, Validierungs-Fail, Success-Commit, keine Engine), Exit 0
- [x] `_bmad-output/implementation-artifacts/deferred-work.md` — ggf. Story-3.7-Defers append-only; keine Spuren in bereits vorhandenen Einträgen (Stand: keine Home-3.7-Defers — nichts anzuhängen, wie per README)
- [x] `wiki/log.md` — (Implementierung) Story-3.7-Eintrag, `sprint-status.yaml` → in-progress; Review-Abschluss `done` im Review-Schritt (Workflow-Konvention)
**Acceptance Criteria:**
- Given ein Run, when er Änderungen plant, then erzeugt er zunächst eine konsistente Änderungsplanung (Analyse → Reconcile → Plan Changes → Mutate → Validate), textuell festgehalten (AD-6, A0-7; AC-1).
- Given ein Fehler während der Mutation, when der Run abbricht, then bleibt der beobachtbare Endzustand des Bundles konsistent — Post-Rollback-Diff gg. Baseline leer, `raw/` unverändert (AD-6; AC-2, I/O-Matrix MUTATION_ABBRUCH).
- Given ein Run, when er abgeschlossen ist, then wurden alle geplanten Mutations-Validierungen erfolgreich durchlaufen und die Mutationen als Ganzes committet (A0-7; AC-3, VALIDATION_SUCCESS).
- Given die Architektur-Anforderung, when umgesetzt, then ist die Trennung logisch, ohne eigene Workflow Engine (AD-6; AC-4, KEINE_EIGENE_ENGINE).
## Spec Change Log
- **2026-08-20 (Step-04-Review, Review-Loop 2, bmad-code-review 4 Layer; Nutzer D1/D2/D3 = 1/1/1):** keine Änderung am `<frozen-after-approval>`-Intent (keine intent_gap/bad_spec-Verdikt). Doku-Stellen nachgeführt: Code-Map `sprint-status`-Eintrag `backlog → in-progress`**`backlog → review`** (Impl.-Flip + Review-Start-Flip im selben Commit, D2); SRO-Anker-Text entsprechend. `### Review Findings` (3 Decision / 7 Patch / 3 Defer) angehängt; alle Decision- und Patch-Items nach der Patch-Runde abgehakt. Sandbox-Patch-Nachweise und die 3 Defers siehe `wiki/log.md`-Review-Loop-2-Bullet (2026-08-20) und `deferred-work.md`-Sektion „Deferred from: code review of … (Story 3.7, 2026-08-20)". `review_loop_iteration: 1`.
</frozen-after-approval>
### Review Findings (bmad-code-review, 2026-08-20 — Review-Loop 2, 4 Layer: blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor)
**[Decision]** (offen — Nutzer-Entscheidung erforderlich):
- [x] [Review][Decision] (gelöst: 1) log.md-Protokoll-Widerspruch + fehlender deferred-work-Append — das done-Bullet (log.md:4) behauptet eine „Delokalisierungs-Doku (deferred-work.md append-only: Terminologie-Drift `INPUT_UNCOMMITTED`/`UNCOMMITTED_INPUT` … Misch-Run-Coverage Neu-Anlage+Update → Story 3.8)", in `deferred-work.md` existiert aber **kein** Story-3.7-Eintrag (grep-verifiziert); das in-progress-Bullet (log.md:5) sagt „Keine Story-3.7-Defers" — die beiden Aufzeichnungen widersprechen sich. Optionen: (1) deferred-work-Append nachtragen (done-Bullet wird wahr); (2) done-Bullet umformulieren (keine Delokalisierungs-Doku behaupten, „keine Defers" bleibt wahr). [wiki/log.md:4-5]
- [x] [Review][Decision] (gelöst: 1) sprint-status `backlog → review` im Diff vs. dokumentiertes `backlog → in-progress``git diff 861e65f..HEAD` zeigt Key `3-7-…` = `review`; Code-Map, SRO-Anker-Text, log.md-in-progress-Bullet und §8-Revision-3.2-Klausel dokumentieren alle `backlog → in-progress`. Optionen: (1) `review` behalten (Review-Start-Flip, File-Header-Konvention) + Dokumentation korrigieren; (2) auf `in-progress` setzen (Diff-Getreue) — der Step-04/05-Status-Sync setzt den Endwert ohnehin neu. [sprint-status.yaml:60]
- [x] [Review][Decision] (gelöst: 1) p2_plan-Element-(1)-Scope — Wiedererwägung der dev-intern abgelehnten E1: `p2_plan` prüft unscoped `git status --porcelain` (ganzer Tree), §5.9 Pkt. 6 Element (1) scope-t auf „Working-Copy von `raw/` und `wiki/` gegen HEAD". Das done-Bullet dokumentiert die Ablehnung („Sandbox läuft nur auf sauberen Bäumen") — in den IST-Szenarien ist der Scope-Unterschied wirkungslos; bei einer Sandbox-Erweiterung (untracked-State außerhalb raw/+wiki/) würde die Demo-Funktion abbrechen, wo die Instruktion es zuließe. Optionen: (1) Scope-Fix `git status --porcelain -- raw wiki` (E1 wiedereröffnen); (2) Ablehnung aufrechterhalten (dismiss); (3) Defer an Sandbox-Härtungsrunde. [run-sandbox.sh:253]
**[Patch]** (offen):
- [x] [Review][Patch] (angewendet) CONSIST-1-EC-1-Negativkontrolle nicht isoliert: `rm -f raw/alpha-v2.md` verschmutzt `raw/` (untracked deletion) → Element (1) (uncommitteter Input) feuert zuerst, Element (3) (Quellen-Existenz) wird nie allein geübt — die BEFUND-Zeile benennt EC-1, der Trigger ist aber Element (1). Fix: Löschung committen (`git rm -q` + commit), damit Element (3) am sauberen Baum isoliert feuert [run-sandbox.sh:343-350]
- [x] [Review][Patch] (angewendet) CONSIST-5-`raw/`-SHA-Assertion tautologisch: `raw_sha=$(sha256sum raw/alpha-v1.md …)` wird mit derselben Datei verglichen — immer wahr, die AD-3-Prüfung beweist nichts. Fix: gg. `git show "$BASE:raw/alpha-v1.md"` vergleichen (Muster CONSIST-4) [run-sandbox.sh:481-482]
- [x] [Review][Patch] (angewendet) CONSIST-6-SUCCESS-Demo committiert einen Zustand, der die §5.9-Update-Disziplin verletzt: die Body-Zeile zitiert `(raw/alpha-v2.md#S-3)`, die `sources`-Nachführung fehlt (im Kommentar als „gehört §5.9/§5.10" eingeräumt) — als Referenz-Demo eines VALIDATION_SUCCESS-Runs misleitet. Fix: Demo-Mutation um den `sources`-Eintrag ergänzen, damit der committete Zustand gültig ist [run-sandbox.sh:496-506]
- [x] [Review][Patch] (angewendet: Doku-Block — Voll-Rollback = §6-Pkt.-3-Teilzustand, Sandbox-Abweichung begründet) `rollback()` löscht **alle** untracked `wiki/`-Dateien (`git reset --hard` + `git clean -qfd wiki`), während der Ghost-Diff-Rollback (§5.9 Pkt. 5) nur die betroffenen Pfade wiederherstellt und AD-17e fremde uncommittete Änderungen schützt (Spec-Never: „uncommittete fremde wiki/-Änderungen löschen"). In den IST-Szenarien wirkungslos, aber die demonstrierte Rollback-Mechanik widerspricht der Instruktion, die sie demonstrieren soll. Fix: `git clean` auf Nicht-erlaubte Pfade begrenzen oder die bewusste Sandbox-Abweichung dokumentieren [run-sandbox.sh:299-313]
- [x] [Review][Patch] (angewendet: CONSIST-2 Misch-Run + `+`-Neu-Anlage in `p2_plan` + Absenz-Assertion) Neu-Anlage-Zweig ungetestet: kein Szenario übt den Plan-Freeze-Eintrag „∪ Neu-Anlage-Zielpfade"; der `assert_restored`-Neu-Anlage-Guard (`git cat-file -e "$BASE:wiki/$p"`-Negativzweig) ist toter Code. Fix: CONSIST-2 um einen geplanten Neu-Anlage-Pfad (`p2_plan` unterstützt den Marker `neu`) erweitern, Post-Rollback-Absenz asserten [run-sandbox.sh:166-169]
- [x] [Review][Patch] (angewendet) Tippfehler: „porcelan" (Z. 151), „NFR-4-texuelle Benennung" (Z. 237), „Ferien-Zustand" (Z. 571) [run-sandbox.sh:151,237,571]
- [x] [Review][Patch] (angewendet) §5.13 Pkt. 7: „der Determinismus-Vertrag (AD-17h/A0-19) gilt für … und Post-Zustand" ohne den dokumentierten `generated.at`-Wanduhr-Gap — §5.10 Pkt. 8 hält das Gap ausdrücklich als offene A0-20-Konvention (zwei unabhängige Runs → verschiedene `at` → Post-Zustand weicht ab). Fix: Ausnahme nachtragen („… Post-Zustand — mit dem dokumentierten `generated.at`-Wanduhr-Gap, §5.10 Pkt. 8") [schema/compiler.md:362]
**[Defer]**:
- [x] [Review][Defer] CONSIST-7-Label überschätzt: „identische Plan-/Rollback-/State-Outputs" — im Zwei-Run-Vergleich werden nur Plan-Outputs (`plan_sha`/`plan_state`) verglichen; der Rollback-/State-Nachweis (s7c) läuft einmalig, `log_sha` ist dokumentierte Baseline-Konstante. Die non-vakuum-Kern-Assertion (`plan_sha` byte-identisch) ist real; das Label ist breiter als die Assertion [run-sandbox.sh:516,572] — deferred (Label-Präzisierung; Home: Sandbox-Härtungsrunde)
- [x] [Review][Defer] A0-20-Post-Zustand-Negativkontrolle in der Sandbox fehlt: §5.13 Pkt. 3/7 bindet den Determinismus-Vertrag an den Post-Zustand, der `generated.at`-Wanduhr-Gap macht den Post-Zustand über unabhängige Runs nicht byte-identisch — die Sandbox testet den Gap weder negativ noch positiv [run-sandbox.sh:532-568] — deferred (Home: Story 3.8, A0-20-Home, §5.10-Pkt.-8-Präzedenz)
- [x] [Review][Defer] Pre-existing-Zeitpunktswort-Fuge: §5.9 Pkt. 6 „am **Anfang der Mutationsphase**" (Z. 287) vs. gefrorene I/O-Matrix + §5.13 „Abbruch **vor** der Mutationsphase" — §5.9 ist in Story 3.7 Wortlaut-unverändert (Rückverweis-Vertrag), die Fuge ist hier nicht ohne Ask-First schließbar [schema/compiler.md:287,356] — deferred, pre-existing (Home: spätere Instruktionsrunde)
## Design Notes
**Warum §5.13 als eigene Sektion, nicht §0-Umbau?** §0 (Z. 13) ist die **deterministische Takt-Folge** — (0)..(5) in fester Reihenfolge, das Rückgrat der Instruktion. AD-6 trennt **logisch**, ohne neue Workflow-Engine; die **vier** AD-6-Phasen sind eine **konsolidierende Klassifikation** derselben Ablaufstruktur (Analyse ≈ §1/§3.2, Änderungsplanung ≈ §5.9 Pkt. 6-P2-Block, Mutation ≈ §§45, Validierung ≈ §6). Ein Umbau von §0 allein würde die bestehenden Story-Statements (§5.9/§5.10/§5.11/§5.12: „dieselben Phasen §0: Interpretieren → Reconcile → Synthetisieren → Mutieren → Validieren") brechen. Deshalb: §5.13 referenziert den §0-Wortlaut **unverändert** und bindet die AD-6-Nomenklatur als Ausführungs-Disziplin an die bestehende Struktur — Review-Layer prüfen die Fugen-Identität (keine Um-Nummerierung im §0-Listentext, keine neue Phase).
**Der P2-Block (§5.9 Pkt. 6) ist die natürliche Änderungsplanung.** Bereits vorhanden und reproduzierbar: (1) Input-Zustand (AD-17a), (2) Ziel-Pfade, (3) Quellen-Existenz, (4) Betroffenheits-Liste (§3 Pkt. 2), (5) index.md-V-1, (6) Struktur-Erhaltungs-Check. §5.13 macht diese Vorprüfung zur **Änderungsplanung der AD-6-Kette** und ergänzt zwei institutionalisierte Eigenschaften: den **Plan-Freeze** (Veränderungs-Sperre — nach Phasenabschluss wird kein Pfad außerhalb der erlaubten Menge berührt, Kopplung an die §5.9-Pkt.-5-Diff-Probe) und den **Plan-Nachweis** (der Plan wird als Teil der textuellen Ausführungsdisziplin festgehalten — kein Artefakt-File, keine Erhaltungs-Invarianten-Ausweitung).
**Die Konsistenz-Garantie ist bereits vorhanden — §5.13 macht sie beobachtbar.** §6-Pkt.-3/§5.3-Pkt.-3-Rollback und der Ghost-Diff-Rollback (§5.9 Pkt. 5, „AD-6-Backstop") existieren; die Anforderung „Endzustand konsistent" ist aber bislang eine erzählte Eigenschaft, keine **git-überprüfbare Invariante**. §5.13 definiert die **Zustands-Restaurations-Invariante**: nach Abbruch/FAIL ist `git diff` gegen die Baseline leer (Bundle == Baseline) **oder** das Bundle ist der valide committete Zustand des Runs (Erfolgsfall; Commit-Boundary = Mutation-Boundary, AD-17f). Beide Pfade sind deterministisch und ohne Wanduhr.
**Sandbox (CONSIST-1..CONSIST-7, `bash run-sandbox.sh`, Exit 0):**
- CONSIST-1: konsistente Änderungsplanung — P2-Block erzeugt Plan (Input-Zustand, Ziel-Pfade, Betroffenheit, Struktur-Erhaltung) aus committetem Input, als Variablen-Set captured, Determinismus (AD-17h/A0-19)
- CONSIST-2: **Plan-Freeze** — Mutation außerhalb der Plan-Pfadmengen wird erkannt und verweigert/verhindert (Veränderungs-Sperre, Ghost-Diff-Kopplung §5.9 Pkt. 5)
- CONSIST-3: **Rollback-Restaurations-Invariante** — fehlgeschlagene Mutation rollt auf exakte Baseline zurück (SHA-256 byte-identisch), Bundle-Zustand == Baseline
- CONSIST-4: MUTATION_ABBRUCH — Abbruch nach Teilerfolg hinterlässt keinen Teilerfolg als fertige Mutation (Commit-Boundary), `raw/` unberührt
- CONSIST-5: VALIDATION_FAIL — §6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent
- CONSIST-6: VALIDATION_SUCCESS — Mutationen als Ganzes committet, Diff gg. Plan-Menge gedeckt, §6-Pkt.-4-Nachweis
- CONSIST-7: **Determinismus-Zwei-Run + keine Engine** — gleicher Baum-Input → identische Plan-/Rollback-/State-Outputs; Trennung ohne Workflow-Engine (nur Shell/Git/Datei, D-3)
(Code-/Zahlgenauigkeiten: Szenario-Labels sind Fixierung der I/O-Matrix; die Implementierung trägt die harten Assertionen in der Sandbox.)
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. `bash _bmad-output/implementation-artifacts/sandbox-3-7/run-sandbox.sh` — expected: CONSIST-1..CONSIST-7 harte PASS/Fail, Restaurations-Invariante erzwungen, Exit 0.
2. `grep -n "§5.13\|Revision 3.2" schema/compiler.md` — liefert §5.13-Sektion + Revisionslog-Eintrag; `grep -n "Reason/Mutate-Phasen-Trennung" schema/compiler.md` — die §5.13-Überschrift wortgleich (inkl. §5.12-Seam-Satz in §5.13-Intro).
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`.
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (unverändert, keine Inhalts-Mutation).
5. Auf den **`wiki/`-Scope begrenzt** (`git status --porcelain -- wiki/`): ausschließlich `wiki/log.md` (dieser Eintrag) — Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt; `sprint-status.yaml`/`deferred-work.md`/`sandbox-3-7/` liegen außerhalb `wiki/` und sind nicht Teil der Diff-Probe (wie §5.9 Pkt. 5-Doku, Story-3.6-Präzedenz).
**Zu beachten (beim step-04-Review):** (a) §0-Phasen-Listentext, §5.9-Pkt.-6-P2-Block und §6-Pkt.-3/§5.3-Referenz müssen textuell **unverändert** bleiben — §5.13 verweist auf sie (Fugen-Identität: Um-Nummerierung oder neue Phase => Ask-First); (b) „die **vier** AD-6-Phasen decken sich logisch mit der §0-Struktur" ersetzt **keine** §0-Normierungen — sie ist eine Zuordnungstabelle, kein Parsing-Muss; (c) die vorhandene §8-Revisionslog-Nummer ist **3.1** (Story 3.6); **Revision 3.2 ist für Story 3.7 frei** (grep-verifiziert: keine 3.2 im Revisionslog); (d) kein neuer §7-Bullet ersetzt einen bestehenden — der Reason/Mutate-Bullet wird **ergänzt**, die bestehenden Story-Bullets (2.2/2.3/2.4/2.5/3.2/3.4/3.5/3.6) bleiben unverändert.
## Suggested Review Order
**§5.13-Reason/Mutate-Trennung-Instruktion**
- Einstieg: §5.13-Sektion — logische Phasen-Disziplin (AD-6/A0-7), Kern der Story, §5.12-Seam und §0-Zuordnungstabelle.
[`compiler.md:343`](../../schema/compiler.md#L343)
- Änderungsplanung: P2-Block (§5.9 Pkt. 6) als Phase institutionalisiert, Plan-Freeze (Veränderungs-Sperre, Ghost-Diff-Kopplung §5.9 Pkt. 5).
[`compiler.md:356`](../../schema/compiler.md#L356)
- Zustands-Restaurations-Invariante: Post-Rollback-Diff gg. Baseline leer / Bundle == valide committet; §6-Pkt.-3/§5.3-Rückverweis unverändert.
[`compiler.md:358`](../../schema/compiler.md#L358)
- Keine eigene Workflow Engine (D-3, AD-6): logische Trennung in einer Session, kein Prozess/Server/MCP.
[`compiler.md:362`](../../schema/compiler.md#L362)
**§7/§8-Nachweis**
- §7-Bullet Reason/Mutate-Trennung — „in §5.13 verankert (Story 3.7)", AD-6/A0-7, bestehende Bullets unverändert.
[`compiler.md:420`](../../schema/compiler.md#L420)
- §8-Revisionslog Revision 3.2 — Verankerung, Abschlussklausel, AD-6 auf §5.13-Anker.
[`compiler.md:461`](../../schema/compiler.md#L461)
**Sandbox-Nachweis**
- Sandbox CONSIST-1..CONSIST-7 — re-executierbar, harte PASS/Fail, Zustands-Restaurations-Invariante (Exit 0)
[`run-sandbox.sh:1`](../../_bmad-output/implementation-artifacts/sandbox-3-7/run-sandbox.sh#L1)
**Story-Protokoll/Defers**
- log.md — Story-3.7-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel).
[`log.md:1`](../../wiki/log.md#L1)
- sprint-status.yaml — `3-7-…` = `review` im Diff (Implementierungs-Flip `in-progress` + Review-Start-Flip `review` im selben Commit); das `done`-Transition ist nicht Teil dieses Diffs (Step-05-Status-Sync).
[`sprint-status.yaml:60`](../../_bmad-output/implementation-artifacts/sprint-status.yaml#L60)
- deferred-work.md — ggf. Story-3.7-Defers append-only.
[`deferred-work.md:1`](../../_bmad-output/implementation-artifacts/deferred-work.md#L1)
@@ -0,0 +1,267 @@
---
title: 'Story 3.8 — Determinismus-Vertrag (AD-17h) als Agent-Instruktions-Validator umsetzen'
type: 'feature'
created: '2026-08-20'
status: 'done'
baseline_commit: 'c4cdf4b93e17b3f84dff03a4bd5608251be56268'
review_loop_iteration: 2 # Review-Loop-3 (2026-08-20): abgeschlossene Review-Loops (1, 2); in Loop-1/2 nicht gepflegt (0) — ab Loop-3 gepflegt (P-10)
course_correction: '_bmad-output/planning-artifacts/sprint-change-proposal-2026-08-20.md'
context:
- '_bmad-output/implementation-artifacts/epic-3-context.md'
---
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Re-Open-Delta (Story-3.8-Neu-Verhandlung, 2026-08-20)
**Neu verhandelt durch genehmigtes Sprint-Change-Proposal** — `_bmad-output/planning-artifacts/sprint-change-proposal-2026-08-20.md` (Status: approved; approved_by: ProMods; explizite Chat-Freigabe). Der Plan korrigiert die zyklische Behauptung, Story 3.8 sei abgeschlossen, während Epic 3 seine zugesagten Verträge noch nicht als kohärentes System-Inkrement belegt. Story 3.8 wurde daher aus `review` nach `in-progress` **zurückgenommen** und mit erweitertem Scope wieder eröffnet (Epics-Datei ACs Z. 357370):
1. **Getrennte Worktrees + frische Agent-Kontexte (Q-6/A0-19):** „zwei frische Agent-Kontexte in getrennt aufgebauten Worktrees; eine zweite Ausführung in derselben Session genügt nicht".
2. **Kanonisches Eingabemanifest:** hält Baseline-Commit, geordnete Source-Eingaben und jeden im Bundle sichtbaren Run-/Zeit-/Identitätswert explizit fest.
3. **Run-Receipt außerhalb des Knowledge Bundle:** Candidate-Liste, Reihenfolge, Plan, Entscheidungen und Output-Hashes.
4. **Keine Hartcodes, kein pauschales Maskieren:** weder erwartete Pläne noch Concept-Bodies werden im Test hart codiert; `verified`-Ereignisse werden niemals pauschal aus dem Vergleich maskiert.
Dieser Frozen-Intent wurde **nur in dem Umfang** geändert, den dieses Re-Open-Delta verlangt (document-human respektierend [Tippfehler-Korrektur Review-Loop-3, D-3 — autorisierte Neu-Verhandlung, dokumentiert im Spec Change Log]; Appendix: fehlendes `</frozen-after-approval>`-Schließtag nach dem Spec Change Log ergänzt). Die ACs in `_bmad-output/planning-artifacts/epics.md` (Z. 357370) sind die verbindliche Neu-Verhandlung; abweichende Alt-Formulierungen (namentlich „in einer Session") sind auf den Re-Open-Stand korrigiert.
## Intent
**Problem:** AD-17h/FT-10/A0-19 verlangen: Über denselben Git-State + dasselbe Eingabeset erzeugen **zwei unabhängige Runs denselben Bundle-State**; eine Abweichung ist ein Fehler der AD-16-Klassifikation, kein Rauschen. Das **Enforcement** lebt im MVP (D-3, Q-6) als **Agent-Instruktions-Validator** und muss **mechanisch bestätigt** sein, bevor es tragend wird. Der Ist-Zustand der Instruktion verankert den Vertrag zwar an vielen Stellen (AD-17h-Rückverweise in §3.2/§5.9/§5.10/§5.11/§5.12/§5.13), aber es fehlt die **geschlossene, aus dem committeten Git-State ableitbare Bestätigungs-Mechanik**: Es gibt keinen normativen Ort, der definiert, (a) was genau der „Bundle-State" ist, (b) wie die Zwei-Run-Bestätigung mechanisch abläuft, (c) welche **Ausnahmen** vom byte-identischen Vergleich gelten (namentlich der dokumentierte `generated.at`-Wanduhr-Gap, A0-20) und (d) wie eine festgestellte Abweichung klassifiziert wird (AD-16-Klassifikationsfehler). Mehrere bekannte Lücken sind hierher gewandert (Em-Dash-Normalisierung, Kollaps-Reichweite, Match-Scope, `at`-Gap, Orphan-Politik, Misch-Run-Coverage).
**Approach:** Neue Sektion **§5.14 „Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)"** (nach §5.13, vor §6): definiert (1) den **Bundle-State** als deterministische Projektion des committeten Git-States (vergleichbare Zustandsmenge, `at`-Feld als benannte Ausnahme), (2) die **Zwei-Run-Bestätigungs-Mechanik** als textuell festgehaltenes, re-executierbares Prüfverfahren — im Re-Open-Stand in **zwei getrennten, sauberen Worktrees** mit **frischen Agent-Kontexten** (Q-6/A0-19; eine zweite Ausführung in derselben Session genügt nicht); Vergleichs-Operandum = committeter Baum; identische Plan-/Kandidaten-/Reihenfolge-Outputs; kein neuer Prozess/Server/MCP, D-3 — unter einem **kanonischen Eingabemanifest** (Baseline-Commit, geordnete Source-Eingaben, output-sichtbare Run-/Zeit-/Identitätswerte) und mit einem **Run-Receipt** (Candidate-Liste, Reihenfolge, Plan, Entscheidungen, Output-Hashes) **außerhalb des Knowledge Bundle**; (3) die **Ausnahme-Menge** des Bundle-State-Vergleichs (der `generated.at`-Wanduhr-Gap als dokumentierte A0-20-Konvention; sonst byte-identisch) und (4) die **Abweichungs-Klassifikation** (jede Abweichung außerhalb der Ausnahme = AD-16-Klassifikationsdefekt, textuell benannt, NFR-4). Die Zwei-Run-Bestätigung wird als **Sandbox-Szenario** mechanisch demonstriert (zwei Worktrees, zwei unabhängige Läufe); weder Pläne noch Concept-Bodies werden im Test hart codiert, `verified`-Ereignisse werden niemals pauschal aus dem Vergleich maskiert (nur die benannte `at`-Ausnahme). Die bekannten Lücken (Em-Dash, Kollaps-Reichweite, Match-Scope, Orphan) werden als **geschlossene, deterministische Regeln** in die Instruktion gehoben — keine offenen Fragen verbleiben, die die Zwei-Run-Identität gefährden.
## Boundaries & Constraints
**Always:**
- Nur `schema/compiler.md` (neue §5.14 + §7-Auflösung des Determinismus-Vorbehalts + §8 Revisionslog **Revision 3.3** + gegebenenfalls Kollaps-/Match-Scope-/Orphan-Präzisierungen in §3.2/§5.10) mutiert (D-3). `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` **read-only** (AD-3). Kein Standalone (D-3), keine eigene Workflow-Engine/kein neuer Prozess/Server/MCP (AD-6, AD-11). Keine neue §7-Invaliditätsklasse, kein Vertrags-Change, kein neuer Prädikat-/Format-/Frontmatter-Key.
- Deterministisch aus dem committeten Git-State ableitbar (AD-17h/A0-19): Bundle-State-Vergleich, Zwei-Run-Bestätigungs-Mechanik, Ausnahme-Menge, Abweichungs-Klassifikation. **Keine Wanduhr/`now`-Steuerung** der Bestätigungs-Mechanik.
- Der `generated.at`-Wanduhr-Gap **bleibt als dokumentierte Ausnahme** bestehen (A0-20-Konvention, §5.9 Pkt. 2, §5.10 Pkt. 8, unverändert bindend bis Ask-First-Wechsel). §5.14 **definiert** die Behandlung von `at` im Bundle-State-Vergleich (Ausnahmemenge), ändert aber **nicht** das `generated.at`-Verhalten selbst.
- Bei einer Abweichung zweier Runs: die Abweichung **ist** ein AD-16-Klassifikationsfehler (kein akzeptables Rauschen, FT-10/AD-17h-AC); die textuelle Benennung (NFR-4) ist verpflichtend.
- Die **normalisierenden Lücken-Schließungen** (Em-Dash in der Kollaps-Klasse, Kollaps-Reichweite bei Läufen/führenden/trailenden Separatoren, Wortgrenzen-/Frontmatter-Scope der Stufe-a-Erhebung) werden als **deterministische Regel-Ergänzungen** in §3.2 umgesetzt — **append-only/kein Umbruch** des bestehenden §3.2-Wortlauts; die Orphan-Politik (§5.10 Pkt. 8) wird zu einer deterministischen Reconcile-Orphan-Regel präzisiert.
- Die Bestätigung ist **agent-instruction-basiert (D-3, Q-6)**: **zwei frische Agent-Kontexte in getrennt aufgebauten (sauberen) Worktrees** führen die Instruktion jeweils einmal aus und belegen die Zwei-Run-Identität (Q-6, A0-19) — eine zweite Ausführung in derselben Session genügt nicht; kein dedizierter Validator-Prozess, keine extra Runtime.
**Ask First:**
- **Änderung des `generated.at`-Verhaltens** selbst (z. B. Ableitung `at` aus dem Git-State statt Wanduhr) — §5.14 definiert nur seine Behandlung im Vergleich, der Konventionswechsel wäre Ask-First.
- Einführung einer **eigenständigen, ausführbaren Validator-Datei** (separates Skript/Programm außerhalb der Instruktion als Enforcer) statt der textuellen Agent-Instruktions-Disziplin — wäre ein Realisierungswechsel weg vom D-3/Q-6-Modell.
- Kollaps-Reichweite bei Läufen/führenden/trailenden Separatoren **anders** zu definieren als „Kollaps auf genau ein `-` + Trim" (z. B. Doppel-Bindestrich-Erhaltung) — jede andere Wahl bricht bestehende Sandbox-Erwartungen.
- Umbenennung/Neudefinition der vorhandenen §3.2-Erhebungsstufen (a/b/c) oder deren Reihenfolge.
**Never:**
- `raw/`-Inhalte verändern (AD-3); uncommittete fremde `wiki/`-Änderungen löschen (AD-17e); textueller Auto-Merge bei Branch-Konvergenz (AD-17c); Wanduhr/`now`-Zeitstempel als Bestätigungs-Steuergröße; eigener `# Log`-Stand in der Registry; Standalone-Compiler/eigene LLM-Runtime (D-3, AD-11); Änderung an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`; neuer Frontmatter-Key; neue §7-Invaliditätsklasse; neuer Prädikat-/Format-Key; eigene Workflow-Engine; neuer dedizierter Prozess/Server/MCP als Enforcer.
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| BUNDLE_STATE_DEFINITION | gleicher committeter Git-State + gleiche Eingabemenge | Bundle-State definiert als deterministische Projektion: Datei-Gesamtheit unter `wiki/`/`raw/` gg. Baseline, Plan-/Kandidaten-/Reihenfolge-Outputs; `generated.at` als benannte Ausnahme | textuell begründet (NFR-4), keine Lücke |
| ZWEI_RUN_IDENTISCH | zwei unabhängige Runs, gleicher Git-State + Eingabeset | identische Bundle-States bis auf benannte `at`-Ausnahme; Sandbox-Assertion nicht-vakuum (echte Content-Identität) | PASS; bei Abweichung außerhalb Ausnahme: AD-16-Klassifikationsdefekt |
| ZWEI_RUN_ABWEICHUNG | zwei Runs, gleicher State, verschiedene Ausgabe | Abweichung = Fehler der AD-16-Klassifikation (kein Rauschen); textuell benannt, Run korrigiert/rollt zurück | NFR-4-Benennung; AD-17h-AC |
| AT_GAP_AUSNAHME | zwei unabhängige Runs, gleicher Input | `generated.at` darf abweichen (A0-20-Konvention, §5.9 Pkt. 2) — einzige benannte Ausnahme im Bundle-State-Vergleich; übrige Teile byte-identisch | dokumentiert (kein stiller Ausschluss) |
| NORMALISIERUNG_EM_DASH | Term mit Em-Dash `—` | Em-Dash in die Kollaps-Klasse aufgenommen (`[-–— _]``-`); identische canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen | deterministisch, kein stiller Ausschluss |
| NORMALISIERUNG_KOLLAPS | Term mit Läufen/führenden/trailenden Separatoren | Kollaps-Reichweite definiert: Kollaps auf genau ein `-` + Trim (führende/trailende entfernt), Läufe auf ein `-` | deterministisch, glaubhaft re-executierbar |
| MATCH_SCOPE_STUFE_A | Stufe-a-grep-Treffer mit/ohne Wortgrenze, Frontmatter-Treffer | Match-Semantik fixiert: ganzes Wort (Wortgrenzen), Frontmatter exkludiert (nur Body) | deterministisch, keine falschen Kandidaten |
| ORPHAN_POLITIK | neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer | Orphan bleibt unzugeordnet, wird in `log.md` als verwaist protokolliert, kein Banner, keine stille Vorbearbeitung | deterministische Reconcile-Orphan-Regel (AD-16-Default) |
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3): neue Sektion **§5.14 „Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)"** (nach §5.13, vor §6; Fixierung: §5.13-Ende Z. 362, §6-Start Z. 364 — neue Sektion zwischen beide, §5.14 Z. 365377); §0-Phasen-Listentext **unverändert**; §5.13 Pkt. 7 „Home Story 3.8" — wird auf §5.14-**Anker** umgehängt (die `generated.at`-Gap-Ausnahme ist jetzt in §5.14 definiert statt nur deklariert; Home-Verweis bleibt, wird aber auf die §5.14-Definition verlagert); §3.2 Normalisierungs-Lücken (Em-Dash in Kollaps-Klasse Z. 51; Kollaps-Reichweite; Match-Scope Z. 55) — als Regel-Ergänzungen aufgelöst; §5.10 Pkt. 8 Orphan-Politik Z. 301 — zur deterministischen Reconcile-Orphan-Regel präzisiert; §7 (`Z. 424ff.`) Determinismus-Vorbehalt „Em-Dash-Lücke an Story 3.8 übergeben" — **aufgelöst**, Verankerung in §5.14 benannt; §8-Normreferenz `AD-17h` (`Z. 445`) auf **§5.14-Anker** angehoben (mit der `at`-Ausnahme-Nennung); Revisionslog-Eintrag **Revision 3.3** nach Z. 477. Anker (IST-Zeilen nach Review-Loop-3, 2026-08-20; P-1): §5.14-Sektion Z. 365 (Pkt. 15 = 369/370/375/376/377); §7-Determinismus-Bullet Z. 434; §8-AD-17h Z. 445; Revisionslog **Revision 3.3** Z. 477.
- `wiki/log.md`**append** (append-only, Vertrag §5): Story-3.8-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
- `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh`**neu** (re-executierbar, Muster sandbox-3-7, Exit 0): Szenarien DET-1..DET-8 (s. Design Notes), harte Pass/Fail-Assertionen, Zwei-Run-Identität nicht-vakuum, keine Berührung des realen Ist-Baums.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: `3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato` `backlog``in-progress` (Implementierungs-Flip) → `review` (Review-Start-Flip, Step-04; finaler `done`-Flip im Step-05); `last_updated` (Format `MM-DD-YYYY HH:MM`, HEAD-Präzision).
- `_bmad-output/implementation-artifacts/deferred-work.md`**append** (append-only): ggf. Story-3.8-Defers; die bestehenden „Home: Story 3.8"-Einträge werden **aufgegriffen/geschlossen** — nicht als Nur-Read-markiert, sondern als aufgehobene Homes gekennzeichnet (Status-Eintragung). Stand: mehrere determinismus-bezogene Defer-Homes (Em-Dash, Kollaps, Match-Scope, Orphan, `at`-Gap, Misch-Run-Coverage) werden in §5.14/§3.2/§5.10 geschlossen; verbleibende Homes (falls nicht schließbar) bleiben offen mit verbleibendem Ziel.
- `_bmad-output/implementation-artifacts/epic-3-context.md`, `spec-3-7-…md`**read-only** (continue-context; Kette unverändert).
**Read-only evidence (AD-3):** `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` (inkl. `raw/architecture-spine/…md` AD-17h/Q-6/FT-10/D-3, `raw/epics/epics-2026-08-14.md` A0-19/A0-20 — normativer Rückbezug, keine Mutation). `schema/canonical-terms.md` bleibt append-only-Registry (keine Änderung an Bestandseinträgen; die Em-Dash-/Kollaps-Schließung lebt als Regel in `compiler.md` §3.2, nicht als Registry-Edits — §3.2-Pkt.-1b-Kollaps-Klasse ist die zentrale Normalisierungsregel).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — §5.14 einfügen (nach §5.13, vor §6): Bundle-State-Definition, Zwei-Run-Bestätigungs-Mechanik (agent-Instruktions-basiert, D-3/Q-6, re-executierbare Formeln), `at`-Ausnahme-Menge, Abweichungs-Klassifikation als AD-16-Klassifikationsdefekt; §0/§5.13/§6 Wortlaut-unverändert referenzieren; kein neuer Prädikat-/Format-/Frontmatter-Key
- [x] `schema/compiler.md` — §3.2-Normalisierungs-Lücken schließen (Em-Dash-Klasse, Kollaps-Reichweite, Match-Scope der Stufe a) — als deterministische Regel-Ergänzungen ohne Umbruch des bestehenden §3.2-Wortlauts; Orphan-Politik §5.10 Pkt. 8 zur deterministischen Reconcile-Orphan-Regel präzisieren
- [x] `schema/compiler.md` — Revision 3.3 in §8 belegen (AD-17h auf §5.14-Anker, `at`-Ausnahme-Nennung, Abschlussklausel); §7-Determinismus-Vorbehalt auflösen (Verankerung in §5.14)
- [x] `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh` — Szenarien DET-1..DET-8 (Bundle-State-Definition, Zwei-Run-identisch nicht-vakuum, Zwei-Run-Abweichung klassifiziert, `at`-Gap-Ausnahme, Em-Dash-, Kollaps-, Match-Scope-, Orphan-Szenario), Exit 0
- [x] `_bmad-output/implementation-artifacts/sprint-status.yaml` — Key `3-8-…``in-progress` (Implementierung) → `review` (Review-Start); `_bmad-output/implementation-artifacts/deferred-work.md` — Story-3.8-Defers bzw. aufgegriffene/geschlossene „Home: Story 3.8"-Einträge kennzeichnen
- [x] `wiki/log.md` — (Implementierung) Story-3.8-Eintrag, `sprint-status.yaml` → in-progress (Implementierung) bzw. `review` (Review-Start, Step-04); Review-Abschluss `done` im Review-Schritt (Workflow-Konvention)
- [x] `schema/compiler.md` §5.14 — **Re-Open-Delta:** Zwei-Run-Bestätigung auf **zwei getrennte saubere Worktrees + frische Agent-Kontexte** umgestellt (Q-6/A0-19; „in einer Session" ersetzt); **kanonisches Eingabemanifest** (Baseline-Commit, geordnete Sources, output-sichtbare Run-/Zeit-/Identitätswerte) und **Run-Receipt** (Candidates, Reihenfolge, Plan, Entscheidungen, Output-Hashes) **außerhalb des Bundles** eingefordert; Verbot von **hart codierten erwarteten Plänen/Concept-Bodies** und **pauschaler `verified`-Maskierung** (nur benannte `at`-Ausnahme)
- [x] `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh`**Re-Open-Delta:** DET-2 auf **zwei getrennte Worktrees/independ. Läufe** umgestellt (Manifest + Receipt-Szenario, keine hart codierten erwarteten Bundle-Outputs, verified nicht pauschal maskiert) — Exit 0
**Acceptance Criteria:**
- Given einen Fixieren Git-State + feste Eingabemenge, when zwei unabhängige Runs ausgeführt werden, then produziert §5.14-Wortlaut identische Bundle-States bis auf die benannte `at`-Ausnahme (FT-10, AD-17h, AC-1).
- Given eine Abweichung bei zwei solchen Runs, when sie festgestellt wird, then wird sie als Fehler im AD-16-Klassifikations-Mechanismus behandelt und textuell benannt (nicht als akzeptables Rauschen) (AD-17h, AC-2).
- Given der MVP (D-3), when der Agent-Instruktions-Validator ausgeführt wird, then laufen die beiden Ausführungen in **getrennten sauberen Worktrees und frischen Agent-Kontexten**; eine zweite Ausführung in derselben Session genügt nicht (Q-6, A0-19; AC-3).
- Given das kanonische Eingabemanifest, when ein Run geplant wird, then hält es Baseline-Commit, geordnete Source-Eingaben und jeden im Bundle sichtbaren Run-/Zeit-/Identitätswert explizit fest; der **Run-Receipt** enthält Candidate-Liste, Reihenfolge, Plan, Entscheidungen und Output-Hashes **außerhalb des Knowledge Bundle** (AC-4).
- Given der Zwei-Run-Nachweis, when Pläne und Bundle-State verglichen werden, then dürfen weder erwartete Pläne noch Concept-Bodies **hart codiert** werden; `verified`-Ereignisse werden **niemals pauschal** aus dem Vergleich maskiert (AC-5).
- Given der Validator, when umgesetzt, then hält er keine Embedding-/Vector-Infrastruktur vor (AD-13; FT-3, FT-4; AC-4) und erzeugt keine eigene Runtime/kein neues Werkzeug (D-3, AD-11).
## Spec Change Log
<!-- Append-only. Populated by step-04 during review loops. Do not modify or delete existing entries. -->
### Review-Loop-1 (2026-08-20, Step-04)
**Ergebnis:** keine intent_gap / kein bad_spec; 0 Loopback. Review-Schicht 3 Subagenten (blind-hunter / edge-case-hunter / verification-gap, synchron); Triage: 14 Patch / 0 defer / Rest Reject (ausführliche Reject-Begründung im Story-Protokoll `wiki/log.md`). Alle Patches rein dokumentarischer Natur (Klarstellung/Verweis/Status); Semantik unverändert, keine neue Vertragsentscheidung.
**Patch-Stapel (in dieser Runde angewandt):**
- §3.2-Pkt.-1b (b): paketierte Kollaps-Klasse in der Operativ-Klausel auf die geschlossene `[-–— _]`-Klasse angehoben; Verweis auf den Append-Block als maßgeblich (KEEP: die append-only-Schließung bleibt unverändert; der Operativ-Satz ist jetzt konsistent).
- §3.2-Pkt.-2a: Match-Scope (ganze Wörter über den Body, exklusiv Frontmatter) in die operative Stufe-a-Formel gewirkt — Einzel-Term-Klausel + Mehrfach-Term-Vereinigung (Pkt. 1c) verdeutlicht; Body-Abgrenzung (nach der zweiten `---`-Zeile) bestimmt.
- §3-Pkt.-2-Kandidatenliste (und damit §5.9-Worked Example): Stufe-a-Scope-Verweis ergänzt.
- §5.14-Pkt.-2: Zwei-Run-Abgleich als Vergleich der beiden Run-Ausgänge gegeneinander (nicht nur je Lauf gg. Baseline) verdeutlicht — Beispiel `git diff <Run-A> <Run-B>` ergänzt.
- §5.14-Pkt.-1-Intro: AD-3-Schutzliste um `adapters/` ergänzt.
- §5.10-Pkt.-8: die pre-existing-Orphan-Obligationsformulierung als vorbestehend markiert (Verweis auf die neue deterministische Regel unten; keine Staffelung an Story 3.8/Epi-4 mehr).
- `deferred-work.md`: Bezugs-Eintrag-Nummern der Story-3.2-review-Defers korrigiert (Kollaps Eintrag 3 → 2; Match-Scope Eintrag 2 → 1).
- `wiki/log.md`: „8 `### Aufgegriffen`-Blöcke" → „7 `### Aufgegriffen`-Blöcke + 1 `### Aufgegriffen (teilweise)`".
- `sandbox-3-8/run-sandbox.sh`: DET-2-at-Maskierungszweck als Kommentar klargestellt (belegt die Nicht-at-Bestandteile; at-Varianz demonstriert DET-4); DET-1-Demo-Kommentar (Kandidaten-/Form-Outputs als redaktionell verdichtete Run-Feststellungen).
- `sprint-status.yaml`: Key `3-8-…``review` (Review-Start-Flip; finaler `done`-Flip im Step-05).
- `spec`-Frontmatter: `status: 'in-review'` (Review-Start per Step-04), `review_loop_iteration: 0` unverändert; SRO-Review-Loop-1-Hinweis ergänzt.
**Rejected-Findings (Kurznotation, nicht Teil der Patches):** Stufe-a-Formel-Änderung nicht nötig (die Formel `rg -l`/`grep -rl` + Scope-Klausel bleibt die Instruktionsform; die Wortgrenzen-/Frontmatter-Semantik reist über die Pkt.-2a-Klausel, nicht über ein Flag); DET-2-Plan-Literal (dev-demo, echte Content-Hashes decken; Kandidaten-Ableitung über DET-1/DET-8); DET-8-hardcodierter Verwaist-Befund (Mechanik textuell, Existenz-negativ-Kontrolle hart); §5.14-`raw/`-Input-Verdacht (Pkt.-1a nennt `raw/`-Zuwächse bereits explizit „als Input"); §5.14-„kommt erstellt der Diff" — keine Formel-Verschmelzung; DET-2-maskierte-`at` (Display-Maske, DET-4 behandelt Varianz); Log-Overcount (Patch 9, siehe oben); Umlaut-Aufschlag (behandelt als Patch-7-Fundament); Sprint-Last-Updated-Timing (Konvention); §3.2-„keine offene Frage"(Umlaut bleibt dokumentiert offen, Wortlaut angepasst).
### Re-Open-Implementierung (2026-08-20, Step-03; genehmigtes Sprint-Change-Proposal 2026-08-20)
**Neu-Verhandlung:** `_bmad-output/planning-artifacts/sprint-change-proposal-2026-08-20.md` (approved, ProMods, explizite Chat-Freigabe) nimmt Story 3.8 aus `review` nach `in-progress` zurück und erweitert den Scope (Epics-Datei ACs Z. 357370): getrennte saubere Worktrees + frische Agent-Kontexte (Q-6/A0-19), kanonisches Eingabemanifest, Run-Receipt außerhalb des Bundles, keine hart codierten erwarteten Pläne/Concept-Bodies, keine pauschale `verified`-Maskierung. Dieser Change-Log-Eintrag dokumentiert die Implementierung des Deltas: §5.14-Wortlaut (Zwei-Run-Mechanik auf Worktrees/Manifest/Receipt, Verbot von Hartcodes/pauschaler Maskierung), Sandbox DET-2 auf zwei Worktrees umgestellt. `<frozen-after-approval>`-Intent ausschließlich im Re-Open-Delta-Umfang angepasst; fehlendes Schließtag nach diesem Change Log ergänzt.
### Review-Loop-2 (2026-08-20, Step-04)
**Ergebnis:** keine intent_gap / kein bad_spec; 0 Loopback. Review-Schicht erneut 3 Subagenten (blind-hunter, edge-case-hunter-Retry, verification-gap, synchron). Diesmal ist die Sandbox-DET-2-Mechanik der Review-Objekte — die Befunde betreffen **die Re-Open-Implementierung selbst** (nicht nur Doku). Triage: **10 Patch** (Sandbox-Härtung) / **0 defer** / Rest Reject. Kern-Veranlassung: die drei Re-Open-ACs (Worktrees, Manifest-Verwendung, `at`-Ausnahme als *dokumentierte* Ausnahme statt stillem Ausschluss) wurden von den Subagenten als **nicht wirklich ausgeübt** erkannt — das Manifest war dekorativ (nie konsumiert), die `at`-Maskierung erfolgte **vor** dem Commit (stiller Ausschluss statt dokumentierter Ausnahme), ein `verified[]`-Feld existiert nicht, der Receipt hatte kein Reihenfolge-/Entscheidungs-Feld, und die Subshell ohne `set -e` riskierte false-PASS.
**Patch-Stapel (in dieser Runde angewandt, ausschließlich `sandbox-3-8/run-sandbox.sh`):**
- **DET-2 ganzheitlich gehärtet:** `at` wird jetzt als **reale Wanduhr-Ausgabe committet** (`date -u`) und als eigener `at_cell`-Wert im Receipt festgehalten; die Normalisierung auf `AT` erfolgt **erst beim Vergleich** (Hash-Bildung über die at-normalisierte Content-Projektion, §5.14 Pkt. 2 „ohne die maskierte at-Zeile") — kein Maskieren vor Commit, kein stiller Ausschluss. Nach-Commit-Attestierung: `committed_at == at_now` wird verifiziert. Der Zwei-Run-Abgleich vergleicht **alle deterministischen Receipt-Bestandteile byte-identisch** und **nur die zwei Ausnahme-Cells `at_cell`/`tree` dürfen differieren**; die `at`-Varianz wird in den Läufen real demonstriert (zwei getrennte Worktree-Läufe → unterschiedliche Wanduhr-`at`).
- **Kanonisches Eingabemanifest wird konsumiert/validiert** (nicht dekorativ): Baseline-Commit und geordnete Source-Eingaben werden abschnittsbewusst aus `manifest.yaml` extrahiert und vor jedem Lauf gegen den Worktree-Zustand geprüft („Manifest ohne baseline / Quellen / Identitätswerte" → HARD-FAIL); die Quell-Eingaben-Existenz wird **nach** dem Increment-Input-Commit geprüft (EC-1-Semantik: Quelle zum Verarbeitungszeitpunkt vorliegend).
- **Receipt erweitert** um `order:` (Reihenfolge) und `decisions:` (Ausführungs-Entscheidung) gemäß §5.14 Pkt. 2; Receipt-Gerüst-Nicht-Vakuum (beide Dateien non-empty) vor dem Vergleich.
- **Subshell `set -e`** in `run2_worktree` aktiviert (jeder fehlende `git`/`sed`-Schritt bricht als rc≠0 ab → HARD-FAIL statt false-PASS); Witness-/sha-Leer-Prüfung blieb.
- **`match_stufe_a` literal-sicher:** Term wird regex-escaped (`sed 's|[][\\.*^$+?(){}|]|\\&|g'`) — ein Term wie `a+b`/`x.y` darf nie als Muster umgedeutet werden (§3.2-Pkt.-2a/§5.14 Pkt. 5); Body-Extraktion CRLF-tolerant (`tr -d '\r'`).
- **`cand`-Newline-Normalisierung** (`tr '\n' ','`) — mehrzeilige Candidate-Liste bricht den Receipt-Zeilenblock nicht mehr.
- **`log_bullet` fail-hard:** `mktemp`/`mv`-Fehler werden nicht mehr geschluckt (ein verlorener `log.md`-Eintrag hätte beide Runs identisch=Baseline gelassen → Vakuum-Gleichheit; jetzt HARD-FAIL).
- **`git worktree prune` vor dem Add** — Re-Run-Sicherheit (keine verwaisten worktree-Registrierungen blockieren/nachtragen).
- **`git clean` fail-hard** unter `set -e` (kein `|| true`-Schlucken von Rest-Unternehmungsdateien).
- **Manifest-`output_visible_run_identity`-Klarstellung:** das Manifest führt konkrete **Feldnamen-Werte** (`generated.by`, `generated.at` — die benannte A0-20-Ausnahme), nicht einen Feldnamen-Platzhalter; die Manifest-Validierung verlangt `generated.at` explizit.
**Kein `defer`:** die Befunde waren sämtlich durch die Re-Open-Implementierung verursacht und hier (auto-)fixierbar; keine pre-existing Lücke wurde dabei neu aufgedeckt. **Reject:** einige Findings (z. B. „Teil-Zeitstimmen"/„literal") waren bereits durch die Hash-basierte Abwichen-Erkennung abgedeckt oder überreich.
**Verifikation dieser Runde:* `bash run-sandbox.sh` → 8/8 `PASS` (DET-1..DET-8), Exit 0, zwei Läufe mit real unterschiedlichen `at_cell`-Wanduhrwerten (Ausnahme aktiv), Witness nicht-vakuum (Baseline-SHA ≠ Run-SHA); die Befunde „Manifest wird nie konsumiert", „at-Maske vor Commit", „kein Reihenfolge-/Entscheidungsfeld" sind damit direkt behoben.
### Review-Loop-3 (2026-08-20, Step-04)
**Ergebnis:** keine intent_gap / kein bad_spec; 0 Loopback. Review-Schicht 4 Subagenten (blind-hunter, edge-case-hunter, verification-gap, acceptance-auditor, synchron). Triage: **4 decision-needed / 13 patch / 6 defer / 7 dismissed.** Nutzer-Entscheidungen (alle empfohlene Option 1): **D-1** §5.14-Schließungs-Bullet macht den §7 „Bekannte Determinismus-Lücke"-Alt-Bullet **superseded** (append-only-Superseded-Hinweis am Alt-Bullet, kein Umbruch — §3.2-„kein Umbruch" gewahrt); **D-2** Story-Status-Flip auf **`done`** nach Loop-3-Abschluss — die Change-Proposal-Sequenzierung „3.9 → 3.10 / 3.11 → 3.12 → 3.8 Abschluss → 3.13 Abnahme" (Sprint-Change-Proposal 2026-08-20) betrifft das **Epic-3-Abnahmegate (Story 3.13)**, nicht die Story-3.8-Instruktionsverankerung (die ist mit diesem Loop abgeschlossen; 3.93.12-Verankerungen und die 3.13-Abnahme bleiben offen, epic-3 remains `in-progress` — als Key-Kommentar in `sprint-status.yaml` festgehalten); **D-3** autorisierte Neu-Verhandlung des Frozen-Intents in **geringfügigem** Umfang: Typo „documenthuman" → „document-human" (dieses File, Re-Open-Delta-Zeile; nur der Rechtschreibfehler, kein Intent-Wechsel) — diese Change-Log-Zeile ist ihre Doku; **D-4** Hold-Home = **Story 3.10 (Epic 3)** (Hold-Mechanik: Erhaltung unzugeordneter `raw/`-Evidenz im benannten Hold ohne Wissensmutation, NFR-7), Korrektur-/Erweiterungs-**Klassifikation** = Epic-4-Interface/Story 4.1 — §5.10 Pkt. 8 entsprechend präzisiert.
**Patch-Stapel (in dieser Runde angewandt):**
- **P-1** SRO-/Code-Map-Anker auf IST-Zeilen: `schema/compiler.md` SRO 374→375 (Pkt. 3), 375→376 (Pkt. 4), 376→377 (Pkt. 5), 476→477 (Rev 3.3); Code Map „§5.14 Z. 364381 ca." → 365377, „§7 (Z. 409ff.)" → 424ff, „§8-AD-17h (Z. 430)" → 445, „Revision 3.3 nach Z. 461" → 477; alle 6 Sandbox-SRO-Anker neu berechnet (vorher exakt, durch die Loop-3-Sandbox-Edits verschoben): 238→245, 257→265, 409→442, 267→275, 414→447, 498→531.
- **P-2/P-4** symmetrische Normalisierung: §3.2 Pkt. 2a-Klausel + `match_stufe_a` — Term **und** Body werden **identisch normalisiert** (Lowercasing + Kollaps-Klasse Pkt. 1b) vor dem Vergleich; kein host-abhängiges `-i`-Flag (wurde entfernt); Wortgrenze folgt der Tool-Wortdefinition (rg `-w` / GNU `grep -w`/`\b`); ASCII-Boundary `[^A-Za-z0-9]` im Skript trägt die Tool-Semantik portabel.
- **P-3** §5.14 Pkt. 2: der **Receipt-basierte Hash-/Textvergleich ist die re-executierbare Formel** des Zwei-Run-Abgleichs; die `git diff <Run-A> <Run-B>`-Formel ist deren Austauschbar-Form auf demselben committeten Baum (Sandbox belegt über Receipt-Hashvergleich).
- **P-5** §5.14 Pkt. 2: Sandbox-Fresh-Kontext-Restlücke textuell benannt — die zwei frischen Agent-Kontexte sind **mechanisch simuliert** (Subshell-Läufe in getrennten Worktrees desselben Skripts); Nachweis echter frischer Kontexte erfolgt im **Story-3.13-Abnahmegate** (echte Agent-Läufe).
- **P-6** Manifest-`generated_by` wird **konsumiert**: Manifest erhält `generated_by: wow-compiler/0.1.0`; Verbrauch-Guard (`generated.by`-Identität + non-empty) + Concept-Frontmatter `by: $m_by` (statt hart-codiert) — „nicht dekorativ" ist jetzt materiell erfüllt.
- **P-7** at-Ausnahme **hart assertiert**: bei Wanduhr-Sekundenkollision wird Lauf B im frischen Kontext wiederholt (begrenzt, 5x), bis die `at_cell`-Werte real differieren; sonst `HARD-FAIL` — die aktive Ausnahme (Change-Log-Beleg „zwei Läufe mit real unterschiedlichen at_cell-Werten") ist jetzt eine Eigenschaft jeder Re-Execution. Re-executiert: `BEFUND: at-Ausnahme aktiv (assertiert) — Lauf A at=2026-08-20T15:05:09Z, Lauf B at=2026-08-20T15:05:12Z`.
- **P-8** „vollständig pinbar"/„keine offene Determinismus-Frage" **ge scopet** (§5.14 Pkt. 5 + §7-Determinismus-Bullet): gelten nur für die mit §5.14 geschlossenen Lücken (Em-Dash-Klasse, Kollaps-Reichweite, Match-Scope, Reconcile-Orphan-Regel, `at`-Gap-Ausnahme); bewusst nicht damit geschlossen: Umlaut-vs-Transkription-Divergenz (benannter Defer) und Story-3.9-ACs (geplant, noch nicht verankert — §3.2 maßgeblich bis dahin).
- **P-9** `epics.md` Story-3.8-AC-2: „Determinismusfehler" als **Fehler der AD-16-Klassifikation** (AD-16-Klassifikationsdefekt) präzisiert — die AD-16-Kopplung war bei Story-Aufnahme nicht ausgeschrieben; AC-Wortlaut erhalten, Nachführung als Präzisierungsklausel.
- **P-10** Frontmatter `review_loop_iteration: 0 → 2` (der Counter war in Loop-1/2 nicht gepflegt; ab Loop-3 gepflegt, Wert = Anzahl bereits abgeschlossener Loops).
- **P-11** `epic-3-context.md` Technical Decisions (Z. 39/41/43): als **Zielzustand** markiert (Story 3.9/3.11/3.12 — noch nicht in `schema/compiler.md` verankert; bestehende Verankerungen bleiben maßgeblich) — die Bullets lesen nicht mehr wie Ist-Zustand.
- **P-12** **verifiziert, kein Edit nötig:** die acceptance-auditor-Finding „epics.md-AD-17-Enum inkonsistent" wurde per Grep widerlegt: `epics.md` enthält **kein** Literal „AD-17a..h" als Block; alle Enum-Vorkommen (Z. 6167, 9495, 101, 262263, 321329, 357408) sind mit dem genehmigten Change-Proposal konsistent (AD-17g verbleibt in Epic 4, Z. 101; AD-17d..17f in Story 3.11/3.12-ACs; AD-17h in Story 3.8-ACs).
- **P-13** dieser Change-Log-Eintrag (append-only nach Loop-2-Eintrag, vor `</frozen-after-approval>` — Loop-1/2-Präzedenz: der Change Log liegt frozen-intern, weil er die Frozen-Änderungsdoku des Re-Opens ist; kein frozen-interner Text wurde hier verändert außer dem D-3-Typo).
**Defers (6, → `deferred-work.md` append-only, Block „Deferred from: code review of spec-3-8… (2026-08-20)"):** D-5 Sandbox-Härtungs-Guards (8 Befunde: `set -u`-Scope, ungeschützte `mktemp`/`git init`/Baseline-Commit, `isolate()`, `log_bullet`-Temporaries) — Testharness-Robustheit, keine normative Semantik; D-6 DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert" (dev-demo-Kommentar + echte Content-Hashes tragen die Nicht-Vakuum-Assertion; Home: Story-3.13-Abnahme mit echten Gate-Runs); D-7 `norm()`-Locale-Pinning (`LC_ALL` — C-Locale degradiert `[–—]` zu Byte-Menge; Home: Sandbox-Härtung mit D-5); D-8 Mehrfach-Term-Vereinigung unübend (Home: Story 3.9); D-9 Orphan-Term hand-pinned + feste 2-Datei-Scan (Home: Story 3.9); D-10 Umlaut-vs-Transkription im Match-Pfad unübend (bereits „Aufgegriffen (teilweise)" offen, kein Instruktions-Defekt).
**Dismissed (7):** (1) Blind-Hunter „alle Sandbox-SRO-Anker stale" — **widerlegt** (alle 6 exakt vor den Loop-3-Edits; die Staleness wurde erst durch die eigenen P-6/P-7-Edits erzeugt und per P-1 aufgelöst); (2) Blind-Hunter „§7-Zeilenanker off-by-one (409/430/461)" — bereits im Loop-3-Review-Stand korrekt verortet, Anker waren Implementierungs-Zielwerte (jetzt per P-1 IST); (3) edge-case „Wiederholungs-Schleife unendlicher Loop" — die Schleife ist auf 5 Versuche begrenzt mit HARD-FAIL-Exit; (4) verification-gap „Manifest-`generated.by`-Guard tautologisch" — Guard prüft Manifest-Inhalt gegen Konzepts-Ausgabe (Konsistenz), nicht gegen sich selbst (P-6); (5) acceptance-auditor „AC-5 verletzt durch `plan`-Literal im Receipt" — das `plan`-Feld ist der **abgeleitte** Plan-State (aus Git-State + Stufe-a-Match), kein vorab hart codierter Erwartungswert (D-6-Präzedenz); (6) acceptance-auditor „epics.md AD-17-Enum inkonsistent" — widerlegt (P-12); (7) verification-gap „`last_updated`-Formatbruch" — Format `MM-DD-YYYY HH:MM` beibehalten (Z. 32), Präzedenz-Prüfung bestanden.
**Verifikation dieser Runde:** `bash _bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh`**8/8 PASS (DET-1..DET-8), Exit 0**; at-Ausnahme aktiv (assertiert) mit real unterschiedlichen Wanduhr-`at`-Werten (Lauf A 15:05:09Z vs. Lauf B 15:05:12Z); Witness nicht-vakuum (Baseline-SHA ≠ Run-SHA); Manifest `generated_by` konsumiert (Concept-Frontmatter `by:` trägt Manifest-Wert). AD-3 geprüft (`schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/` unverändert). **Status:** Spec-Frontmatter `status: 'done'`, `review_loop_iteration: 2`; `sprint-status.yaml`-Key `3-8-…`**`done`** (D-2, mit Sequenzierungs-Hinweis), `last_updated` → 08-20-2026 17:04. `wiki/log.md` Loop-3-Abschluss-Bullet ergänzt.
</frozen-after-approval>
### Review Findings (Review-Loop-3, 2026-08-20; bmad-code-review, 4 Layer)
<!-- Append-only Review-Protokoll (Step-04); analog zur Change-Log-Sanction. Bestehender Frozen-Text wird NICHT verändert. Dismissed (7) werden nicht persistiert — Begründungen im Review-Loop-3-Change-Log-Eintrag (P-13). -->
**Decision-Needed (4):**
- [x] [Review][Decision] D-1 §3.2-Alt-Bullet-Selbstwiderspruch — der Pre-Existing-Bullet „Bekannte Determinismus-Lücke … deckt den Em-Dash `—` **nicht** ab … übergeben an Story 3.8" (`schema/compiler.md:53`) steht wortgleich über dem Schließungs-Bullet (`:54`) und ist jetzt faktisch falsch; zwei benachbarte, sich widersprechende normative Aussagen über die Kollaps-Klasse. Kollidiert mit dem Spec-Constraint „ohne Umbruch des bestehenden §3.2-Wortlauts" (append-only). — **Entscheidung (2026-08-20): Option 1** — neuer append-only-Nachtrags-Bullet unter `:54` kennzeichnet den Alt-Bullet ausdrücklich als **superseded** (Alt-Wortlaut bleibt unverändert; Widerspruch aufgelöst).
- [x] [Review][Decision] D-2 Status-Trail bruch — Spec-Frontmatter `status: 'done'` + `wiki/log.md:4` „Step-05 done" + Commit-Titel „Step-05 done" vs. `sprint-status.yaml:61` `review` vs. genehmigtes Change-Proposal („Story 3.8 wird nach 3.93.12 … abgeschlossen; 3.8 Abschluss → 3.13 Abnahme"). Welcher Wert ist maßgeblich, und wer flippt wann? — **Entscheidung (2026-08-20): Option 1**`sprint-status.yaml` auf `done` flippen (Loop-3-Abschluss); Change-Proposal-Sequencing betrifft das Epic-3-Abnahmeverfahren (Gate 3.13), nicht den Story-Zustand; Sequencing-Vorbehalt wird im `wiki/log.md`-Abschluss-Bullet textuell vermerkt.
- [x] [Review][Decision] D-3 „documenthuman respektierend" im Frozen-Block — mangelhafter Phrase (vermutl. „document-human respektierend") in `spec` Z. 24, dem menschlich-eigenen Frozen-Block; nur per menschlicher Renegotiation änderbar. — **Entscheidung (2026-08-20): Option 1** — menschliche Renegotiation hiermit erteilt: Korrektur auf „document-human respektierend"; die Renegotiation wird im Review-Loop-3-Change-Log-Eintrag dokumentiert (D-3).
- [x] [Review][Decision] D-4 Orphan-/Hold-Home-Konflikt — §5.10 Pkt. 8 (`schema/compiler.md:302`) + Log-Einträge benennen „Epic-4-Interface, Story 4.1" als Home der Korrektur-/Erweiterungs-Klassifikation, während die Story-3.10-ACs (epics.md Z. 402) einen Hold „bis Epic 4" in Epic 3 selbst verlangen. Welches Story-AC besitzt die Orphan-/Hold-Klassifikation? — **Entscheidung (2026-08-20): Option 1** — Story 3.10 ist Home des Holds (Mechanik in Epic 3); die Korrektur-/Erweiterungs-*Klassifikation* bleibt Epic-4-Interface (Story 4.1); Pkt.-8-Verweis wird entsprechend präzisert (Patch aus D-4).
**Patches (13):**
- [x] [Review][Patch] P-1 SRO-Anker-Drift `schema/compiler.md` — SRO-Zeilen `:374``375`, `:375``376`, `:376``377` (Pkt. 3/4/5), `:476``477` (Revision 3.3); Code-Map „Revision 3.3 nach Z. 461"→477, „§7 nach Z. 420"→434, „§8-AD-17h Z. 430"→Ist, „§5.14 Z. 364381"→365377 (spec Z. 66/182/183/184/194).
- [x] [Review][Patch] P-2 §3.2-Stufe-a-Boundary-Semantik klären — normative Klausel benennt `rg -w`/`grep -w`/`\b` (ASCII-only Wortklassen), Sandbox nutzt `[^A-Za-z0-9]`-Boundary + `-i`; für deutsche Inhalte (Umlaut/ß) divergieren „Wort"-Definitionen; `-i` nirgends instruiert (spec:79, `schema/compiler.md:56`, run-sandbox.sh:144).
- [x] [Review][Patch] P-3 §5.14 Pkt. 2 Run-A↔Run-B-Formel konkretisieren — die benannte `git diff <Run-A> <Run-B> -- wiki/`-Formel wird von der Sandbox nie ausgeführt (nur Receipt-/Hash-Vergleich; `tree`-Cells als Ausnahme); entweder Sandbox ergänzt die Formel, oder Pkt. 2 benennt den Hash-Textvergleich ausdrücklich als äquivalente re-executierbare Formel (spec:88, §5.14 Pkt. 2 `schema/compiler.md:371`).
- [x] [Review][Patch] P-4 §5.14 Pkt. 2/5 Body-Normalisierung klären — „identisch normalisiert" (§3.2/§5.14) gilt textuell nur für den Term; `match_stufe_a` normalisiert den Body nicht (nur CRLF-Strip), der Em-Dash-Kollaps fehlt im Match-Pfad vollständig (DET-5 testet `norm`, nie den Matcher) (run-sandbox.sh:126-142, 555-571).
- [x] [Review][Patch] P-5 AC-3-Fresh-Kontext-Restlücke dokumentieren — „zwei frische Agent-Kontexte" (Q-6/A0-19) sind in der Sandbox strukturell nur als Subshells simulierbar; die Restlücke soll in spec/Log textuell als „mechanisch simuliert" benannt sein (spec:90, run-sandbox.sh:238-260).
- [x] [Review][Patch] P-6 Manifest-Verbrauchs-Tautologie — Manifest wird aus `$BASE` + harter Source-Liste erzeugt und gegen dieselben Variablen validiert; `generated.by` wird nie konsumiert (hart-codiert `wow-compiler/0.1.0`); „nicht dekorativ" nur formal erfüllt (spec:91, run-sandbox.sh:248-266, 300).
- [x] [Review][Patch] P-7 at-Ausnahme-Demo asserten — bei identischen `at_cell`-Werten (Wanduhr-Sekundenkollision) druckt das Skript nur „BEFUND: at-Grenzfall" und PASST; die aktive Ausnahme wird nicht assertiert; Change-Log-Claim „zwei Läufe mit real unterschiedlichen at_cell-Werten" nicht vom Skript erzwungen (spec Change-Log Z. 127, run-sandbox.sh:394-401).
- [x] [Review][Patch] P-8 §7-Absoluthheits-Claim eingrenzen — „Keine offene Determinismus-Frage verbleibt" (§7 `schema/compiler.md:434`) + §5.14 Pkt. 5 „vollständig pinbar … keine offene Frage" (:377) vs. `deferred-work.md` (Umlaut-vs-Transkription offen) + Story-3.9-ACs (closed term-gewinnung/manifest/Routing-Tabelle existiert noch nicht).
- [x] [Review][Patch] P-9 Terminologie-Drift AC-2 — epics.md Z. 366 „Determinismusfehler … Run gilt als fehlgeschlagen" vs. §5.14 Pkt. 4 „AD-16-Klassifikationsdefekt … korrigiert bzw. rollt zurück" (:376); AD-16-Kopplung in der Neuformulierung ohne Verweis verloren.
- [x] [Review][Patch] P-10 `review_loop_iteration` + Change-Log-Finalität — Counter blieb `0` über zwei Loops (dekorativ); kein Change-Log-Eintrag für den Step-05-Statuswechsel (spec Z. 7, 95-142).
- [x] [Review][Patch] P-11 `epic-3-context.md` geplanter Zustand markieren — Technical Decisions beschreiben Story-3.9/3.11/3.12-Entscheidungen als aktuellen Ist-Zustand, die in `compiler.md` nicht existieren; Ziel-/geplanter-Zustand kennzeichnen.
- [x] [Review][Patch] P-12 epics.md-Epic-Narrativ-AD-17a..h nachziehen — Summary-Tabelle/Epic-Body zitieren weiterhin „(AD-17a..h)"; AD-17g-Entzug (jetzt „vollständig in Epic 4") nur in der AD/A0-Enum sichtbar, nicht im Narrativ.
- [x] [Review][Patch] P-13 Review-Loop-3-Change-Log-Eintrag anhängen — Triage-Ergebnis, Patch-Stapel, Defer-Verweise und die 7 Dismissals mit Begründung (darunter Blind-Hunter „alle Sandbox-SRO-Anker stale" — widerlegt, alle 6 exakt).
**Defer (6):**
- [x] [Review][Defer] D-5 Sandbox-Härtungs-Guards (8 Befunde) [run-sandbox.sh:37,41,94-96,104-107,136-141,243-244] — deferred, Testharness-Robustheit (keine normative Semantik); Re-Run-Risiko dokumentiert, Home: nächste Sandbox-Revision.
- [x] [Review][Defer] D-6 DET-1/DET-2-Plan-/Form-Literale vs. AC-5-„nicht hart codiert" [run-sandbox.sh:189,211-221] — deferred, Loop-1-/Loop-2-Präzedenz (dev-demo-Kommentar Z. 211-214; echte Content-Hashes tragen die Nicht-Vakuum-Assertion); Home: Story-3.13-Abnahme (echte Gate-Runs).
- [x] [Review][Defer] D-7 `norm()`-Locale-Pinning (`LC_ALL`) [run-sandbox.sh:115-122] — deferred, Testharness-Host-Locale-Abhängigkeit; C-Locale degradiert `[–—]` zu Byte-Menge; Home: Sandbox-Härtung (mit D-5).
- [x] [Review][Defer] D-8 Mehrfach-Term-Vereinigung unübend [run-sandbox.sh:555-660] — deferred, §5.14-Pkt.-5-„pinbar"-Verhalten ohne ausführbare Coverage; Home: Story 3.9 (Routing/Term-Gewinnung).
- [x] [Review][Defer] D-9 Orphan-Term hand-gesteuert + feste 2-Datei-Scan [run-sandbox.sh:660-693] — deferred, Mechanik textuell korrekt, Determinismus der Orphan-ermittlung (Term-Ableitung aus `raw/`-Zuwachs, voller Kandidaten-Scan) nicht ausgeübt; Home: Story 3.9.
- [x] [Review][Defer] D-10 Umlaut-vs-Transkription im Match-Pfad unübend [run-sandbox.sh:555-571] — deferred, bereits als „Aufgegriffen (teilweise)" offen (deferred-work.md Z. 541-546, kein Instruktions-Defekt); Home: Sandbox-Vereinheitlichung/folgende Instruktions-Revision.
## Design Notes
**Warum §5.14 als eigene Sektion, nicht §6.5-Erweiterung?** §6.5 ist die „Determinismus- & Selbsttest-Norm" mit **drei** Nachprüf-Kriterien (Vollständige §3-Subset-Konformität, `at`-Normalform, `sources`-Existenz) — die projektions-seitige Content-Prüfung je erzeugtem Concept. Der Determinismus-*Vertrag* (AD-17h/FT-10) ist die **Cross-Run-Eigenschaft** (zwei unabhängige Runs → gleicher Bundle-State), kein Einzel-Concept-Kriterium. §5.14 ist die geschlossene Verankerung dieser Cross-Run-Eigenschaft: (a) **Bundle-State-Definition** (welche Zustandsmenge ist „der Bundle-State" — die committete Baum-Projektion unter `wiki/`/`raw/` zzgl. Plan-/Kandidaten-/Reihenfolge-Outputs; `generated.at` als benannte Ausnahme), (b) **Zwei-Run-Bestätigungs-Mechanik** (agent-Instruktions-basiert; **Re-Open-Stand:** die beiden Ausführungen laufen in **getrennten sauberen Worktrees** mit **frischen Agent-Kontexten** über demselben committeten Git-State und vergleichen die Bundle-States; eine zweite Ausführung in derselben Session genügt nicht — Q-6/A0-19; unter **kanonischem Eingabemanifest** und mit **Run-Receipt außerhalb des Bundles**), (c) **Ausnahme-Menge** (allein `generated.at`-Wanduhr-Gap, dokumentiertes A0-20; Nichts sonst), (d) **Abweichungs-Klassifikation** (jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsfehler; textuell benannt NFR-4; kein stiller Vorbeilass). Damit ist die bisher „offen verankerte" AD-17h-Eigenschaft (viele Rückverweise, kein normativer Ort der Bestätigung) zu einem **instruierten, mechanisch bestätigbaren Verfahren** verdichtet.
**Die Normalisierungs- und Match-Lücken werden deterministisch geschlossen, nicht nur notiert.** Die Kollaps-Klasse in §3.2 Pkt. 1b ist `[-_ ]` (En-Dash , Bindestrich -, Unterstrich _, Leerzeichen) — der Em-Dash `—` fehlt (bekannte Story-3.2-Lücke mit Home Story 3.8). Die Schließung ist eine **Ergänzung der Kollaps-Klasse** um den Em-Dash (`[-–— _]` → `-`) sowie die Festlegung der **Kollaps-Reichweite** (jedes Vorkommen → genau ein `-`; führende/trailende Separatoren werden getrimmt — die bereits in der Sandbox etablierte Semantik) und des **Match-Scope** der Stufe a (Stufe-a-grep-matcht **ganze Wörter** über den Body, exklusive YAML-Frontmatter — `rg -l '<term>' -g '!log.md' wiki/` erhält einen deterministischen Scope; Substring- und Frontmatter-Treffer werden ausgeschlossen). §5.10 Pkt. 8 (Orphan) erhält eine **deterministische Reconcile-Orphan-Regel**: neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer bleibt unzugeordnet, wird in `log.md` als verwaist protokolliert (Datumsgruppe, `<Baseline-Commit>`), kein Banner/keine stille Vorbearbeitung. Diese Schließungen machen die relevanz-/synthese-seitigen Erhebungen über den committeten Zustand vollständig pinbar.
**Die Sandbox (DET-1..DET-8, `bash run-sandbox.sh`, `/tmp`-Baum, Exit 0) demonstriert die Bestätigung mechanisch.**
- DET-1 BUNDLE_STATE_DEFINITION: erzeugt ein Mini-Bundle, definiert/extrahiert die Bundle-State-Projektion (wiki/-Dateien + Plan-/Kandidaten-/Reihenfolge-Outputs) deterministisch
- DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum): **zwei getrennte Worktrees** mit **frischen Agent-Kontexten** (je ein eigener Lauf über demselben committeten Baum) → identische Bundle-States bis auf `at`; unter **kanonischem Eingabemanifest** und mit **Run-Receipt** (Candidates, Reihenfolge, Plan, Entscheidungen, Output-Hashes) **außerhalb des Bundles**; die Assertion vergleicht echte Content-Hashes (nicht nur Vorhandensein); keine hart codierten erwarteten Bundle-Outputs, `verified` nicht pauschal maskiert (nur die benannte `at`-Ausnahme)
- DET-3 ZWEI_RUN_ABWEICHUNG: künstlich divergenter Lauf → wird als AD-16-Klassifikationsdefekt klassifiziert und textuell benannt (kein Rauschen)
- DET-4 AT_GAP_AUSNAHME: zeigt, dass `generated.at`-Wanduhr-Gap die einzige benannte Ausnahme ist (übrige Teile byte-identisch)
- DET-5 EM_DASH: Term mit Em-Dash `—` normalisiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen
- DET-6 KOLLAPS: Läufe (`a--b`), führende (`-x`), trailende (`x-`) → Kollaps auf genau ein `-` + Trim
- DET-7 MATCH_SCOPE: Stufe-a-matcht ganze Wörter über Body, exklusive Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen)
- DET-8 ORPHAN: neu committete Evidenz ohne Ziel-Pfad-Treffer → unzugeordnet, `log.md`-verwaist protokolliert, kein Banner/keine Mutation
(Code-/Zahlgenauigkeiten: Szenario-Labels sind Fixierung der I/O-Matrix; die Implementierung trägt die harten Assertionen in der Sandbox.)
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. `bash _bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh` — expected: DET-1..DET-8 harte PASS/Fail (Re-Open-Stand, gehärtet in Review-Loop-2: DET-2 in zwei getrennten Worktrees mit frischen Kontexten, **kanonisches Eingabemanifest konsumiert/validiert** vor jedem Lauf, **Run-Receipt außerhalb des Bundles** mit `order:`/`decisions:`/`at_cell`, **keine hart codierten Bundle-Outputs**, `verified` nicht pauschal maskiert — **`generated.at` als reale Wanduhr-Ausgabe committet und erst beim Vergleich normalisiert** (dokumentierte Ausnahme, kein stiller Ausschluss), Ausnahme-Cells `at_cell`/`tree` dürfen differieren, übrige Bestandteile byte-identisch), Zwei-Run-Identität nicht-vakuum, Exit 0.
2. `grep -n "§5.14\|Revision 3.3" schema/compiler.md` — liefert §5.14-Sektion + Revisionslog-Eintrag; `grep -n "Determinismus-Vertrag & Agent-Instruktions-Validator" schema/compiler.md` — die §5.14-Überschrift wortgleich (inkl. §5.13-Seam-Satz in §5.14-Intro).
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`. `schema/canonical-terms.md` unverändert (append-only-Registry unangetastet).
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (unverändert, keine Inhalts-Mutation).
5. Auf den **`wiki/`-Scope begrenzt** (`git status --porcelain -- wiki/`): ausschließlich `wiki/log.md` (dieser Eintrag) — Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt; `sprint-status.yaml`/`deferred-work.md`/`sandbox-3-8/` liegen außerhalb `wiki/` und sind nicht Teil der Diff-Probe.
**Zu beachten (beim step-04-Review):** (a) §0-Phasen-Listentext, §5.13-Phasen-Disziplin und §6/§6.5 müssen textuell **unverändert** bleiben — §5.14 verweist auf sie (Fugen-Identität); (b) der `generated.at`-Wanduhr-Gap **bleibt verhaltens-seitig unverändert** (A0-20-Konvention); §5.14 definiert nur seine Behandlung im Bundle-State-Vergleich (Ausnahmemenge) — ein Wechsel des `generated.at`-Verhaltens wäre Ask-First; (c) die vorhandene §8-Revisionslog-Nummer ist **3.2** (Story 3.7); **Revision 3.3 ist für Story 3.8 frei** (grep-verifiziert: keine 3.3 im Revisionslog); (d) kein neuer §7-Bullet ersetzt einen bestehenden — der Determinismus-Bullet wird **ergänzt** (Verankerungs-Verweis), die bestehenden Story-Bullets bleiben unverändert; (e) **Re-Open-Delta (§5.14):** die Zwei-Run-Mechanik nutzt **getrennte saubere Worktrees + frische Agent-Kontexte** (Q-6/A0-19), ein **kanonisches Eingabemanifest** und einen **Run-Receipt außerhalb des Bundles** — keine hart codierten erwarteten Bundle-Outputs, keine pauschale `verified`-Maskierung (nur `at`-Ausnahme); die alte Formulierung „in einer Session" ist entfernt.
## Suggested Review Order
<!-- Klickbare Review-Route (Step-05): jede Zeile = [Beschreibung:Zeilennummer](../../relativer-pfad#LZeile) -- Ctrl+Klick (Cmd+Klick auf macOS) öffnet den Anker. Zuerst die §5.14-Bestätigungs-Mechanik lesen, dann die Lückenschließung, dann Nachweis-Orte, Sandbox-Beweis und Protokoll als Peripherie. -->
**§5.14-Determinismus-Vertrag (Kern, Einstieg)**
- [compiler.md:365](../../schema/compiler.md#L365) — §5.14-Überschrift, Intro mit §5.13-Seam und AD-3-Schutzliste inkl. `adapters/`.
- [compiler.md:369](../../schema/compiler.md#L369) — Pkt. 1 Bundle-State-Definition: committete Baum-Projektion, `generated.at` als benannte Ausnahme (A0-20), FT-10/AD-17h/AC-1.
- [compiler.md:370](../../schema/compiler.md#L370) — Pkt. 2 Zwei-Run-Bestätigungs-Mechanik: zweimal über denselben committeten Git-State, Run-A↔Run-B-Abgleich (Beispiel `git diff`), keine Wanduhr-Steuerung.
- [compiler.md:375](../../schema/compiler.md#L375) — Pkt. 3 Ausnahme-Menge: allein der `generated.at`-Wanduhr-Gap; alle übrigen Bestandteile byte-identisch (kein stiller Ausschluss).
- [compiler.md:376](../../schema/compiler.md#L376) — Pkt. 4 Abweichungs-Klassifikation: jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsdefekt, kein Rauschen (NFR-4, Zustands-Restaurations-Invariante).
- [compiler.md:377](../../schema/compiler.md#L377) — Pkt. 5 Normalisierungs-/Match-/Orphan-Schließung: Verankerung auf §3.2-Pkt.-1b / §5.10-Pkt.-8, Erhebungen vollständig pinbar.
**§3.2/§5.10-Lückenschließung (deterministische Regel-Ergänzungen)**
- [compiler.md:54](../../schema/compiler.md#L54) — §3.2-Pkt.-1b „Geschlossene Determinismus-Lücken": Em-Dash-Kollaps-Klasse `[-–— _]`, Kollaps-Reichweite (ein `-` + Trim), Match-Scope Stufe a (ganze Wörter, Body-exklusiv).
- [compiler.md:56](../../schema/compiler.md#L56) — §3.2-Pkt.-2a operative Stufe-a-Mechanik: `rg -l`/`grep -rl`-Sweep + deterministischer Scope-Filter (zweistufig, tool-portabel), Mehrfach-Term-Vereinigung.
- [compiler.md:302](../../schema/compiler.md#L302) — §5.10-Pkt.-8 Erhaltungs-Invariante & Determinismus-Vertrag: Reconcile-Orphan-Regel (Zuwachs gg. Baseline, datumsgruppierter `log.md`-Eintrag, kein Banner, AD-16-Default).
**§7/§8-Nachweis**
- [compiler.md:477](../../schema/compiler.md#L477) — Revisionslog Revision 3.3: §5.14-Verankerung, §7-Determinismus-Vorbehalt aufgelöst, §8-AD-17h-Normreferenz auf §5.14-Anker, Abschlussklausel (AD-3/D-3/keine neue §7-Klasse).
**Sandbox-Beweis (mechanische Bestätigung)**
- [run-sandbox.sh:245](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L245) — DET-2 Szenario-Start: zwei getrennte Worktrees + frische Kontexte über demselben committeten Baum; Einstieg in die gehärtete Determinismus-Mechanik (Review-Loop-2).
- [run-sandbox.sh:265](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L265) — Kanonisches Eingabemanifest (`manifest_path`), das jetzt VOR jedem Lauf konsumiert/validiert wird (nicht dekorativ; Review-Loop-2-Patch; Review-Loop-3 P-6: `generated_by` wird konsumiert).
- [run-sandbox.sh:442](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L442) — DET-2 RESULT: Receipts byte-identisch bis auf `at_cell`/`tree` (Ausnahme-Cells); att real committet, erst beim Vergleich normalisiert — dokumentierte Ausnahme statt stiller Ausschluss (Review-Loop-3 P-7: at-Ausnahme hart assertiert, Wiederholungs-Schleife bei Sekundenkollision).
- [run-sandbox.sh:275](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L275) — `set -e`-Subshell: jeder fehlende Schritt bricht als rc≠0 ab (kein false-PASS; Review-Loop-2-Patch).
- [run-sandbox.sh:447](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L447) — DET-3: künstlich divergenter Lauf → AD-16-Klassifikationsdefekt, textuell benannt (NFR-4), korrigierte Wiederholung == Referenz.
- [run-sandbox.sh:531](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L531) — DET-4: `generated.at`-Gap als einzige benannte Ausnahme; at-Maskierung beim Vergleich (byte-identische Restteile) + Negativkontrolle.
**Story-Protokoll/Defers (Peripherie, zuletzt)**
- [log.md:4](../../wiki/log.md#L4) — Story-3.8-Eintrag: Verankerung, Sandbox-Nachweis, Erhaltungs-Invariante, Validator-Verdikt; Revision 3.3.
- [deferred-work.md:492](../../_bmad-output/implementation-artifacts/deferred-work.md#L492) — aufgegriffene/geschlossene „Home: Story 3.8"-Defers (7 `### Aufgegriffen` + 1 `### Aufgegriffen (teilweise)`, append-only ab Zeile 492).
- [spec:122](../../_bmad-output/implementation-artifacts/spec-3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato.md#L122) — Spec Change Log Review-Loop-2: 10 Patches der Sandbox-Härtung (at-Ausnahme real ausgeübt, Manifest-Validierung, Receipt order/decisions, `set -e`, literal-sichere Stufe-a).
- [sprint-status.yaml:61](../../_bmad-output/implementation-artifacts/sprint-status.yaml#L61) — Key `3-8-determinismus-…` auf `review` (Review-Start-Flip Step-04); `done`-Transition im Step-05.
File diff suppressed because one or more lines are too long
@@ -29,7 +29,7 @@
# - Dev moves story to 'review', then runs code-review (fresh context, different LLM recommended)
# - Retrospective appends its action items to action_items; the status view surfaces open ones
generated: 08-14-2026 00:00
last_updated: 08-18-2026 22:00
last_updated: 08-21-2026 10:16
project: wow20
project_key: NOKEY
tracking_system: file-system
@@ -50,15 +50,20 @@ development_status:
2-5-progressive-discovery-über-index-md-bereitstellen: done
epic-2-retrospective: done
epic-3: backlog
3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile: backlog
3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip: backlog
3-3-bestehende-concepts-erweitern-präzisieren-korrigieren: backlog
3-4-wissen-aus-mehreren-sources-synthetisieren: backlog
3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz: backlog
3-6-lease-staleness-recovery-basis-absichern: backlog
3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: backlog
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: backlog
epic-3: in-progress
3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile: done
3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip: done
3-3-bestehende-concepts-erweitern-präzisieren-korrigieren: done
3-4-wissen-aus-mehreren-sources-synthetisieren: done
3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz: done
3-6-lease-staleness-recovery-basis-absichern: done
3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: done
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: done # Review-Loop-3-Abschluss 2026-08-20 (bmad-code-review; D-2-Entscheidung). Hinweis: die Abschluss-Reihenfolge „3.9 → 3.10 / 3.11 → 3.12 → 3.8 Abschluss → 3.13 Abnahme" des genehmigten Sprint-Change-Proposal 2026-08-20 betrifft das Epic-3-Abnahmegate (3.13) — die Story-3.8-Instruktionsverankerung ist abgeschlossen (done); die 3.93.12-Verankerungen und die 3.13-Abnahme bleiben offen (epic-3 remains in-progress).
3-9-deterministische-relevanz-und-reconcile-routing-schliessen: done # Review-Loop-3-Abschluss 2026-08-21 (bmad-code-review, 4 Layer; D-3.9-1/2/3 = 1/1/2 — empfohlene Optionen; kein Loopback). Hinweis: der finale `done`-Flip ist der Step-05-Status-Sync nach konvergiertem Review-Loop (Präzedenz Story 3.7/3.8); die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
3-10-inkrementelle-update-und-synthese-erhaltung-absichern: backlog
3-11-root-scope-leasing-atomar-akquirieren: backlog
3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen: backlog
3-13-epic-3-verifikations-und-abnahmegate: backlog
epic-3-retrospective: optional
epic-4: backlog
+96 -11
View File
@@ -88,16 +88,17 @@ Aus den Sources entstehen eigenständige, OKF-0.2-konforme Concepts mit claim-gr
**AD/A0:** AD-1, AD-4, AD-4a, AD-4b, AD-4c, AD-7, AD-7a..7d, AD-8, AD-9, A0-3, A0-4, A0-5, A0-8, A0-9, A0-10, A0-20
### Epic 3: Inkrementelle Kompilation & Synthese
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Die Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten auf `lease/<area>/<id>`-Branches mit Dirty-Tree-Schutz, Root-Scope-Lease und commit-gebundener Mutation (AD-17a..h).
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Die Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten mit atomarer Root-Scope-Lease, Dirty-Tree-Schutz und commit-gebundener Mutation. Klassifikationspflichtige Kollisionen enden bis Epic 4 fail-closed in einem strukturierten Hold; der Determinismus-Vertrag wird unabhängig und mechanisch qualifiziert.
**FRs covered:** FR-4, FR-6, FR-7, FR-12
**NFRs covered:** NFR-7
**AD/A0:** AD-5, AD-6, AD-13, AD-17, AD-17a..17h, A0-6, A0-7, A0-12..A0-18, A0-19, A0-21
**AD/A0:** AD-5, AD-6, AD-13, AD-17a, AD-17b, AD-17c (Fail-closed-Sicherheitsgrenze), AD-17d..17f, AD-17h, A0-6, A0-7, A0-12, A0-13, A0-14 (No-Auto-Merge-Sicherheitsgrenze), A0-15, A0-16, A0-18, A0-19, A0-21 (Incrementality-Teil)
**Shared boundary:** Epic 3 verantwortet bei AD-17c/A0-14 ausschließlich Erkennung und fail-closed Erhaltung; Epic 4 verantwortet Klassifikation und semantische Auflösung. Bei A0-21 verantwortet Epic 3 die Erhaltung unabhängigen Wissens, Story 4.3 die spezifische Human-Curation-Semantik.
### Epic 4: Wissenstreue — Widersprüche & menschliche Kuratierung
Widersprüche werden nie stillschweigend zur scheinbar eindeutigen Aussage zusammengeführt; relevante Disagreements bleiben als explizite Einträge in `log.md` erhalten; menschlich kuratierte Inhalte werden als bestehendes Wissen respektiert und bleiben über OKF-Trust-Metadaten (`verified: human:...`) von ungeprüftem maschinellem Output unterscheidbar; unvollständiges/ungeprüftes Wissen wird ohne künstliche Gewissheit dargestellt.
**FRs covered:** FR-8, FR-13
**NFRs covered:** NFR-7
**AD/A0:** AD-16, AD-16a, AD-16b, AD-15, A0-11, A0-17, A0-20
**AD/A0:** AD-16, AD-16a, AD-16b, AD-15, AD-17c, AD-17g, A0-11, A0-14, A0-17, A0-20, A0-21
### Epic 5: Consumer-Zugriff & Nachvollziehbarkeit
Menschen und beliebige LLM-Agenten (BMAD, Claude Code, Codex, ...) lesen das Knowledge Bundle ohne Wiki-of-Wikis-spezifische Runtime; Änderungen sind über Git-Diffs nachvollziehbar; die kanonischen Compiler-Regeln sind agent-unabhängig mit dünnen Adaptern; es gibt keinen obligatorischen Server, keine Datenbank und keine proprietäre Abhängigkeit.
@@ -257,8 +258,9 @@ So that ein Consumer relevantes Wissen schrittweise findet, ohne das gesamte Wik
## Epic 3: Inkrementelle Kompilation & Synthese
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten auf `lease/<area>/<id>`-Branches mit Dirty-Tree-Schutz, Root-Scope-Lease und commit-gebundener Mutation (AD-17a..h).
**FRs covered:** FR-4, FR-6, FR-7, FR-12 · **NFRs:** NFR-7 · **AD/A0:** AD-5, AD-6, AD-13, AD-17, AD-17a..17h, A0-6, A0-7, A0-12..A0-18, A0-19, A0-21
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten mit atomarer Root-Scope-Lease, Dirty-Tree-Schutz und commit-gebundener Mutation. Klassifikationspflichtige Kollisionen enden bis Epic 4 fail-closed in einem strukturierten Hold; der Determinismus-Vertrag wird unabhängig und mechanisch qualifiziert.
**FRs covered:** FR-4, FR-6, FR-7, FR-12 · **NFRs:** NFR-7 · **AD/A0:** AD-5, AD-6, AD-13, AD-17a, AD-17b, AD-17c (Fail-closed-Sicherheitsgrenze), AD-17d..17f, AD-17h, A0-6, A0-7, A0-12, A0-13, A0-14 (No-Auto-Merge-Sicherheitsgrenze), A0-15, A0-16, A0-18, A0-19, A0-21 (Incrementality-Teil)
**Shared boundary:** Epic 3 verantwortet bei AD-17c/A0-14 ausschließlich Erkennung und fail-closed Erhaltung; Epic 4 verantwortet Klassifikation und semantische Auflösung. Bei A0-21 verantwortet Epic 3 die Erhaltung unabhängigen Wissens, Story 4.3 die spezifische Human-Curation-Semantik.
### Story 3.1: Inkrementellen Datenfluss implementieren (Interpret → Reconcile → Synthesize → Update)
@@ -316,14 +318,14 @@ So dass kein separates Summary pro Quelle entsteht und die gemischte Provenienz
As a Producer/Compiler,
I want auf `lease/<area>/<id>`-Branches mit Root-Scope-Lease und Dirty-Tree-Schutz zu arbeiten,
So dass zwei Producer denselben Concept-Pfad nicht stillschweigend überschreiben und Fremdänderungen nie als Nebenwirkung gelöscht werden (AD-17, AD-17a..f, A0-12..A0-16).
So dass zwei Producer denselben Concept-Pfad nicht stillschweigend überschreiben und Fremdänderungen nie als Nebenwirkung gelöscht werden (AD-17a, AD-17b, AD-17e/f, A0-12, A0-13, A0-16; semantische Kollisionsauflösung in Epic 4).
**Acceptance Criteria:**
**Given** ein Producer, **When** er einen Bereich bearbeitet, **Then** arbeitet er auf einem `lease/<area>/<id>`-Branch und akquiriert die Lease gegen einen eindeutigen Commit-Object-Wert (Merge-Base-Disziplin) (AD-17a, A0-12).
**Given** eine Lease, **When** sie vergeben ist, **Then** umfasst sie die Root-Scope inklusive `log.md`, `index.md` und aller Root-Dateien (AD-17b, A0-13).
**Given** eine vorliegende uncommittete Fremdänderung im zu mutierenden Bereich, **When** der Producer mutieren will, **Then** schützt er sie (Stash/Scratch-Zone) und dokumentiert den Vorgang in `log.md` (AD-17e, A0-16).
**Given** zwei Branches mit Änderungen am selben Concept-Pfad, **When** gemerged werden soll, **Then** erfolgt kein stiller textueller Auto-Merge (AD-17c, A0-14) — Auflösung compiler-vermittelt über AD-16 mit explizitem `log.md`-Eintrag.
**Given** zwei Branches mit ungleichen Änderungen am selben Concept-Pfad, **When** vor Epic 4 ein Merge versucht wird, **Then** erfolgt kein textueller Auto-Merge; der Run endet mit einem strukturierten Kollisions-Hold, der beide Commit-Hashes und den betroffenen Scope erhält. AD-16-Klassifikation und semantische Auflösung folgen in Epic 4 (Sicherheitsgrenze von AD-17c; vollständige Auflösung in Story 4.1/4.2).
**And** Mutationen operieren nur auf Directory-/Commit-Ebene — Commit-Boundary ist die Mutation-Boundary (AD-17f, A0-16).
### Story 3.6: Lease-Staleness & Recovery-Basis absichern
@@ -356,21 +358,104 @@ So dass ein teilweise fehlgeschlagener Run nie ein inkonsistentes Bundle hinterl
As a Compiler,
I want dass derselbe Git-State + dieselbe Eingabemenge bei zwei unabhängigen Runs denselben Bundle-State erzeugt,
So dass die AD-16-Klassifikation deterministisch genug ist (AD-17h, FT-10, A0-19).
So dass Relevanz-, Routing-, Planungs- und Mutationsentscheidungen reproduzierbar sind (AD-17h, FT-10, A0-19).
**Acceptance Criteria:**
**Given** einen Fixed Git-State und eine feste Eingabemenge, **When** zwei unabhängige Runs ausgeführt werden, **Then** produzieren sie identische Bundle-Zustände (FT-10, AD-17h).
**Given** eine Abweichung bei zwei solchen Runs, **When** sie festgestellt wird, **Then** wird sie als Fehler im AD-16-Klassifikations-Mechanismus behandelt (nicht als akzeptables Rauschen) (AD-17h).
**Given** der MVP (D-3), **When** die Determinsmus-Enforcement fehlt, **Then** lebt sie als Agent-Instruktions-Validator und ist vor Last tragenden Anspruch als mechanisch bestätigt (Q-6, A0-19).
**Given** eine Abweichung bei zwei solchen Runs, **When** sie festgestellt wird, **Then** wird sie als Determinismusfehler behandelt und der Run gilt als fehlgeschlagen — nicht als akzeptables Rauschen (AD-17h). *Präzisierung (Review-Loop-3, 2026-08-20): „Determinismusfehler" = **Fehler der AD-16-Klassifikation** (AD-16-Klassifikationsdefekt) im Sinne von §5.14 Pkt. 4 `schema/compiler.md` — der AC-Wortlaut „Determinismusfehler" bleibt erhalten; die Klassifikations-Verknüpfung (AD-16) war bei Story-Aufnahme nicht ausgeschrieben und wird hier nachgeführt, ohne den AC-Sinn zu ändern.*
**Given** der MVP (D-3), **When** der Agent-Instruktions-Validator ausgeführt wird, **Then** laufen die beiden Ausführungen in getrennten sauberen Worktrees und frischen Agent-Kontexten; eine zweite Ausführung in derselben Session genügt nicht (Q-6, A0-19).
**Given** das kanonische Eingabemanifest, **When** ein Run geplant wird, **Then** hält es Baseline-Commit, geordnete Source-Eingaben und jeden im Bundle sichtbaren Run-/Zeit-/Identitätswert explizit fest; der Run-Receipt enthält Candidate-Liste, Reihenfolge, Plan, Entscheidungen und Output-Hashes außerhalb des Knowledge Bundle.
**Given** der Zwei-Run-Nachweis, **When** Pläne und Bundle-State verglichen werden, **Then** dürfen weder erwartete Pläne noch Concept-Bodies im Test hart codiert werden; `verified`-Ereignisse werden niemals pauschal aus dem Vergleich maskiert.
**And** der Validator hält keine Embedding-/Vector-Infrastruktur vor (AD-13; FT-3, FT-4).
### Story 3.9: Deterministische Relevanz- und Reconcile-Routing schließen
As a Compiler,
I want Evidenz deterministisch bestehenden, neuen oder nicht klassifizierbaren Wissenseinheiten zuordnen,
So that keine relevante Information still ignoriert, dupliziert oder falsch verwaist wird (FR-4, FR-5-Interaktion, FR-6, FR-12, AD-5, AD-13, A0-6, A0-18).
**Acceptance Criteria:**
**Given** einen committeten Git-State und ein kanonisches Eingabemanifest, **When** Candidate-Terme gewonnen werden, **Then** folgt die Gewinnung einem geschlossenen, geordneten Algorithmus oder einem expliziten, persistierten Term-Manifest — keine freie Producer-Auswahl.
**Given** semantisch gleiche Schreibweisen mit Groß-/Kleinschreibung, Leerzeichen, Unterstrich, Bindestrich, En-Dash oder Em-Dash, **When** die Stufe-a-Suche läuft, **Then** werden Suchterm und Concept-Body identisch normalisiert und literal-sicher verglichen; `index.md`-Treffer bleiben für die Traversal-Stufe erhalten.
**Given** interpretierte Evidenz, **When** Reconcile abgeschlossen wird, **Then** gilt genau eine Routing-Tabelle: bestehender Match → `UPDATE`; eigenständige neue Wissenseinheit → `CREATE`; nicht klassifizierbare Evidenz → expliziter `ORPHAN/HOLD`; bereits vollständig repräsentierte identische Evidenz → `NO_OP`.
**Given** eine geänderte oder gelöschte bereits committete Datei unter `raw/`, **When** der Run seine Eingaben prüft, **Then** schlägt er vor jeder Mutation fehl; akzeptiert werden nur neu hinzugefügte oder separat versionierte Sources (AD-3).
**Given** ein neues Concept-Ziel, **When** dessen Slug `index` oder `log` beziehungsweise ein anderer reservierter Bundle-Name wäre, **Then** wird das Ziel nicht geschrieben und ein deterministischer Hold verlangt eine disambiguierte Identität.
**And** gleicher Git-State plus gleiches Eingabemanifest erzeugt dieselbe Candidate-Liste, Reihenfolge und Routing-Entscheidung (AD-17h, A0-19), belegt durch positive und negative ausführbare Fixtures.
### Story 3.10: Inkrementelle Update- und Synthese-Erhaltung absichern
As a Compiler,
I want bestehendes Wissen semantisch erweitern und mehrere Sources kohärent synthetisieren,
So that neues Wissen integriert wird, ohne gültiges vorhandenes Wissen oder Provenienz zu verlieren (FR-4, FR-6, FR-7, FR-12, AD-4, AD-5).
**Acceptance Criteria:**
**Given** eine passende neue Erkenntnis zu einem bestehenden Concept, **When** der Run sie verarbeitet, **Then** wird das bestehende Concept in-place erweitert oder präzisiert und kein thematisches Duplikat angelegt.
**Given** explizite aktuelle Evidenz, die eine bestehende Aussage eindeutig korrigiert, ohne dass zwischen weiterhin gültigen Sources ein Widerspruch verbleibt, **When** der Run sie verarbeitet, **Then** wird das bestehende Concept in-place korrigiert und die ersetzte Aussage samt Source-Basis bleibt im Run-Receipt nachvollziehbar; mehrdeutige Fälle gehen in den Hold für Epic 4.
**Given** ein betroffenes Concept mit weiterhin gültigen Aussagen und Provenienz, **When** es aktualisiert wird, **Then** bleiben diese geschützten Bestandteile erhalten; nicht betroffene Concepts bleiben byte-identisch.
**Given** eine neue Source, die eine bestehende Aussage unabhängig bestätigt, **When** synthetisiert wird, **Then** erscheint die Aussage genau einmal und trägt alle beitragenden Evidenzanker; die neue Source ist kein `NO_OP`.
**Given** mehrere Sources mit redundanten und ergänzenden Aussagen, **When** der Run synthetisiert, **Then** entsteht eine gemeinsame Wissensrepräsentation mit claim-granularer gemischter Provenienz — keine Source-A/Source-B-Aneinanderreihung.
**Given** eine bereits vollständig repräsentierte identische Source samt Evidenzanker, **When** sie erneut verarbeitet wird, **Then** ist der Run für dieses Wissen byte-erhaltend (`NO_OP`).
**Given** der Provenienz- und Link-Selbsttest, **When** erwartete Deltas bestimmt werden, **Then** stammen Baseline und Erwartung aus dem aktuellen Run; kein historischer, fest codierter Commit oder globaler Zählwert ist normativ.
**And** klassifikationspflichtige oder widersprüchliche Evidenz wird bis Epic 4 ohne Wissensmutation in einem benannten Hold erhalten; beide Evidenzpfade bleiben im Run-Receipt nachvollziehbar (NFR-7).
### Story 3.11: Root-Scope-Leasing atomar und worktree-übergreifend akquirieren
As a Producer,
I want pro kanonischem Scope genau eine atomare Lease erwerben,
So that verschiedene Run-IDs niemals gleichzeitig Schreibzugriff auf dasselbe Knowledge Bundle erhalten (AD-17a, AD-17b, A0-12, A0-13).
**Acceptance Criteria:**
**Given** den Root-Scope `wiki/`, **When** eine Lease akquiriert wird, **Then** existiert genau ein scope-bezogener Lock im clone-geteilten Zustand; die Run-ID ist Lock-Inhalt und nicht Teil des Exklusivitätsschlüssels.
**Given** zwei Producer mit verschiedenen IDs und Worktrees, **When** beide denselben Root-Scope akquirieren, **Then** ist die Akquise atomar und genau ein Producer erhält die Lease; der andere erhält `LEASE_HOLD`.
**Given** einen abgewiesenen Producer, **When** sein Lauf endet, **Then** verändert er weder `wiki/` noch den bestehenden Lock, erzeugt keinen Compilation Commit und entfernt keine fremde Lease.
**Given** einen realen Zwei-Worktree-/Zwei-Prozess-Test, **When** die Akquise zeitlich überlappt, **Then** beweisen Zwischenzustands-Assertions, dass niemals zwei aktive Root-Leases gleichzeitig existieren.
**And** ungleiche Änderungen am selben Concept-Pfad werden nicht automatisch gemerged, sondern als strukturierter Kollisions-Hold mit beiden Commit-Hashes an Epic 4 übergeben.
### Story 3.12: Lease-Lifecycle und Commit-Abschluss transaktional schließen
As an Operator,
I want Akquise, Dirty-Tree-Schutz, Mutation, Rollback und Freigabe als konsistenten Lifecycle ausführen,
So that SUCCESS und FAIL jeweils einen sauberen, wiederanlaufbaren Zustand hinterlassen (AD-6, AD-17d..17f, A0-7, A0-15, A0-16).
**Acceptance Criteria:**
**Given** einen lebenden Lease-Halter, **When** eine höhere Generation sichtbar wird, **Then** bleibt seine Lease aktiv; Staleness verlangt bestätigten Abbruch oder abgelaufene Liveness sowie atomare Ownership-Prüfung.
**Given** eine nachweislich stale Lease, **When** sie übernommen wird, **Then** gelingt die Übernahme genau einmal, nennt die ersetzte Holder-ID und hinterlässt genau eine aktive Root-Lease.
**Given** getrackte oder ungetrackte fremde Änderungen im Mutationsbereich, **When** der Preflight läuft, **Then** folgt er einer eindeutigen Abort-/Protect-Zustandsmaschine und stellt geschützte Bytes nach dem Run vollständig wieder her.
**Given** einen Fehler nach Mutation oder Staging, **When** Rollback läuft, **Then** restauriert er explizit aus dem bezeichneten Baseline-Commit sowohl Index als auch Worktree; der Post-Rollback-Diff gegen die Baseline ist leer.
**Given** einen erfolgreichen Run, **When** er freigegeben wird, **Then** sind Mutation, zulässiger Log-/Koordinationsnachweis und Release dauerhaft, der aktive Lock ist entfernt, der Worktree ist sauber und der unmittelbar folgende Run besteht den Clean-Input-Guard.
**Given** `wiki/log.md`, **When** ein Run protokolliert wird, **Then** enthält das kanonische Knowledge Log nur vertragskonforme fachliche Änderungen und notwendige Koordinationsereignisse; Build-, Review-, Story- und Sandbox-Historie liegt außerhalb des Knowledge Bundle.
**And** Kill-Point-Tests vor Mutation, nach Mutation, vor Commit und nach Commit beweisen den jeweils konsistenten Endzustand.
### Story 3.13: Epic-3-Verifikations- und Abnahmegate
As a Product Owner/Compiler-Verantwortlicher,
I want Epic 3 durch einen realen, reproduzierbaren Source→Compilation→Wiki-Lauf qualifizieren,
So that seine Fertigstellung durch ausführbare Evidenz statt handgeschriebene Simulationen belegt ist (SM-1, SM-2, FT-6, FT-10).
**Acceptance Criteria:**
**Given** einen sauberen Checkout, **When** das repositoryweite Epic-3-Gate ausgeführt wird, **Then** führt ein Kommando alle Epic-3-Szenarien fail-fast aus; fehlgeschlagene Assertions, übersprungene Szenarien, fehlende Abhängigkeiten und Kindprozessfehler ergeben einen Non-Zero-Exit.
**Given** macOS/BSD- und Linux/GNU-Userland, **When** das Gate dort läuft, **Then** ist es ohne plattformspezifisches `sed -i` reproduzierbar, arbeitet ausschließlich in temporären Repositories und hinterlässt `git status --porcelain` unverändert.
**Given** positive und negative OKF-Fixtures, **When** validiert wird, **Then** wird der vollständige Vertrag aus `schema/validator.md` über alle `wiki/`-Dateien ausgeführt; unter anderem fehlendes `generated.by`, kalenderinvalides `generated.at`, unzulässige Source-Pfade und gebrochene Index-Links verhindern SUCCESS und Commit.
**Given** der autorisierte Validator-Vertrag oder seine ausführbare Aufrufbarkeit weist dabei ein Defizit auf, **When** das Gate es erkennt, **Then** bleibt Story 3.13 offen und benennt den Bedarf für eine separat autorisierte Epic-1-Remediation; Story 3.13 darf die Schema-Semantik nicht still ändern, verantwortet aber die Integration und vollständige Ausführung des bestehenden Vertrags.
**Given** eine repräsentative committete Fixture, **When** ein frischer Agent-Kontext die kanonische Instruktion `schema/compiler.md` ausführt, **Then** darf der Harness nach dem Setup weder erwartete Wiki-Bodies noch Lease-, Log- oder Git-Ausgänge selbst schreiben.
**Given** diese Fixture, **When** der Compilation Run endet, **Then** demonstriert er Update eines bestehenden Concepts, Anlage einer neuen Wissenseinheit, kohärente Multi-Source-Synthese, vollständige Multi-Evidenz-Provenienz, Source-Immutabilität und byte-identische Erhaltung unabhängigen Wissens.
**Given** dasselbe kanonische Eingabemanifest, **When** zwei frische Agent-Kontexte in getrennt aufgebauten Worktrees laufen, **Then** stimmen Run-Receipts und Bundle-State gemäß Story 3.8 überein; eine perturbierte Entscheidung oder Ausgabe wird erkannt.
**Given** nur das erzeugte Knowledge Bundle ohne Planungsartefakte, **When** ein unabhängiger Consumer im Epic-5-Abhängigkeits-Smoke-Test vordefinierte Wissensfragen beantwortet, **Then** benötigt er weder Product Brief, SPEC, Architecture Spine noch Story-Historie zum semantischen Verständnis; solche Referenzen sind höchstens optionale Provenienz/Traceability. Dieser Smoke-Test nimmt die vollständige FR-15/FR-16-Abnahme aus Epic 5 nicht vorweg.
**And** erst nach bestandenem Gate und `done`-Status aller Stories 3.8 bis 3.13 wird Epic 3 auf `done` gesetzt; bis dahin bleibt Epic 3 `in-progress`.
---
## Epic 4: Wissenstreue — Widersprüche & menschliche Kuratierung
Widersprüche werden nie stillschweigend zur scheinbar eindeutigen Aussage zusammengeführt; relevante Disagreements bleiben als explizite Einträge in `log.md` erhalten; menschlich kuratierte Inhalte werden als bestehendes Wissen respektiert und bleiben über OKF-Trust-Metadaten (`verified: human:...`) von ungeprüftem maschinellem Output unterscheidbar; unvollständiges/ungeprüftes Wissen wird ohne künstliche Gewissheit dargestellt.
**FRs covered:** FR-8, FR-13 · **NFRs:** NFR-7 · **AD/A0:** AD-16, AD-16a, AD-16b, AD-15, A0-11, A0-17, A0-20
Epic 4 beginnt nach bestandenem Story-3.13-Abnahmegate und übernimmt die in Epic 3 fail-closed erhaltenen Kollisions-/Widerspruchs-Holds zur semantischen Klassifikation und Auflösung.
**FRs covered:** FR-8, FR-13 · **NFRs:** NFR-7 · **AD/A0:** AD-16, AD-16a, AD-16b, AD-15, AD-17c, AD-17g, A0-11, A0-14, A0-17, A0-20, A0-21
### Story 4.1: Information vor jeder Änderung klassifizieren (NEW/CONFIRMING/CORRECTING/CONTRADICTING/REDUNDANT)
@@ -0,0 +1,102 @@
---
title: Sprint Change Proposal — Epic 3 Completion Remediation
status: approved
created: 2026-08-20
approved_by: ProMods
approval: "Explizite Chat-Freigabe: Ja"
classification: moderate
affected_epic: 3
---
# Sprint Change Proposal: Epic 3 belastbar abschließen
## 1. Issue Summary
Der unabhängige Read-only-Audit nach Story 3.8 zeigte, dass Epic 3 seine bereits zugesagten Anforderungen und Architekturverträge noch nicht als kohärenten System-Inkrement belegt. Betroffen sind insbesondere deterministische Relevanz und Routing, semantische Update-/Synthese-Erhaltung, Root-Scope-Leasing, transaktionaler Run-Abschluss, AD-17h-Reproduzierbarkeit und die ausführbare End-to-End-Abnahme.
Der Trigger ist kein neuer Produktwunsch, sondern eine fehlgeschlagene Verifikation bestehender Zusagen aus FR-4, FR-6, FR-7, FR-12, AD-5/6/13/17 und A0-6/7/12..19. Konkrete Evidenz liegt in `schema/compiler.md`, den Story-3-Sandboxes, `wiki/log.md`, dem Sprint-Status und `epic-3-context.md` vor.
## 2. Impact Analysis
### Epic Impact
- Epic 3 bleibt `in-progress`; Story 3.8 wird aus `review` nach `in-progress` zurückgenommen.
- Stories 3.93.13 werden innerhalb von Epic 3 ergänzt.
- Ein neues Epic ist nicht gerechtfertigt: Alle Arbeiten schließen bereits zugesagte Epic-3-Verträge.
- Epic 4 beginnt erst nach dem Story-3.13-Abnahmegate.
### Scope Boundary zu Epic 4
Epic 3 erkennt klassifikationspflichtige Kollisionen und Widersprüche und hält fail-closed an; dies ist seine Sicherheitsgrenze aus AD-17c/A0-14. Vollständige AD-16-Klassifikation, semantische Kollisionsauflösung (AD-17c/g) und Disagreement-Dokumentation verbleiben in Epic 4. A0-21 ist geteilt: Story 3.10 belegt die Erhaltung unabhängigen Wissens, Story 4.3 das Human-Curation-Verhalten/FT-9. Damit entfällt die bisherige zyklische Behauptung, Epic 3 sei abgeschlossen, obwohl sein Merge-/Resolution-Pfad Epic 4 voraussetzt.
### Artifact Impact
- PRD: keine Änderung.
- Architecture Spine: keine Änderung; die Story-Zuordnung wird mit den bestehenden Entscheidungen konsistent gemacht.
- `epics.md`: Scope-Korrektur, Story-3.8-Präzisierung und Stories 3.93.13.
- `sprint-status.yaml`: Story 3.8 `in-progress`, neue Stories `backlog`.
- `epic-3-context.md`: Story-Liste, technische Grenzen und Abhängigkeiten synchronisiert.
- Code, Compiler-Instruktion, Tests und Knowledge Bundle werden durch diese Planungskorrektur nicht verändert.
## 3. Recommended Approach
**Gewählt: Direct Adjustment innerhalb von Epic 3.**
Rollback bereits abgeschlossener Stories würde die vorhandene Arbeit nicht vereinfachen. Eine PRD-/MVP-Neudefinition ist ebenfalls nicht nötig. Die kleinste kohärente Korrektur ist, die offenen systemischen Verantwortungen in klar geschnittene Remediation-Stories zu überführen, Story 3.8 ehrlich wieder zu öffnen und ein finales ausführbares Abnahmegate einzuführen.
Aufwand: mittel bis hoch. Risiko: mittel bei Umsetzung vor Epic 4; hoch, falls Epic 4 auf dem derzeit unzuverlässigen Lease-/Log-/Compiler-Unterbau beginnt.
## 4. Detailed Change Proposals
### Bestehende Story
- **Story 3.8 — Determinismus-Vertrag:** wieder öffnen; zwei getrennte Worktrees und frische Agent-Kontexte, kanonisches Eingabemanifest, rekonstruierbarer Run-Receipt, keine hart codierten Outputs und keine pauschale Maskierung von Trust-Ereignissen.
### Neue Stories
1. **Story 3.9 — Deterministische Relevanz- und Reconcile-Routing schließen**
Schließt Termgewinnung, symmetrische Normalisierung, literal-sichere Suche, exklusive Routing-Tabelle, Raw-Immutability-Guard und reservierte Zielpfade.
2. **Story 3.10 — Inkrementelle Update- und Synthese-Erhaltung absichern**
Beweist In-place-Update, Erhaltung vorhandenen Wissens, vollständige Multi-Evidenz-Provenienz, echten No-op und kohärente Synthese ohne historische Hardcodings.
3. **Story 3.11 — Root-Scope-Leasing atomar und worktree-übergreifend akquirieren**
Realisiert einen scope-bezogenen atomaren Lock und echte konkurrierende Worktree-Tests für unterschiedliche Run-IDs.
4. **Story 3.12 — Lease-Lifecycle und Commit-Abschluss transaktional schließen**
Schließt Staleness/Liveness, Dirty-Tree-Schutz, Baseline-Rollback, durable Release, Clean-Next-Run und die Grenze des kanonischen Knowledge Logs.
5. **Story 3.13 — Epic-3-Verifikations- und Abnahmegate**
Führt portable fail-fast Verifikation, vollständigen OKF-Validator, einen realen Agent-Instruktionslauf, unabhängige Reproduktion und einen Epic-5-Abhängigkeits-Smoke-Test zusammen.
### Reihenfolge
```text
3.9 → 3.10 ┐
├→ 3.8 Abschluss → 3.13 Abnahme → Epic 4
3.11 → 3.12┘
```
## 5. Implementation Handoff
**Klassifikation:** Moderate Backlog-Reorganisation, anschließend Developer-/Review-Ausführung.
- Product Owner: bestätigt Story-Schnitt und Scope-Grenze — erfolgt am 2026-08-20.
- Developer: implementiert Stories in der angegebenen Abhängigkeitsreihenfolge.
- Reviewer: prüft jede Story gegen ihre ausführbaren Negativ- und Positivbelege; Story 3.13 ist die Epic-Abnahme.
- Epic 3 wird erst `done`, wenn Story 3.8 und Stories 3.93.13 `done` sind und das Story-3.13-Gate erfolgreich ist.
## 6. Checklist Record
- [x] Trigger und konkrete Audit-Evidenz verstanden
- [x] Auswirkungen auf Epic 3, Epic 4 und Epic 5 geprüft
- [x] PRD-/Architecture-Konflikte geprüft; keine Änderung erforderlich
- [x] Direct Adjustment gegen Rollback und MVP-Neuschnitt bewertet
- [x] Story-Schnitt, Reihenfolge und Handoff festgelegt
- [x] Explizite Nutzerfreigabe erhalten
- [x] Sprint-Status und Epic-Kontext synchronisiert
File diff suppressed because one or more lines are too long
+39
View File
@@ -0,0 +1,39 @@
# Kanonischer Schreibweisen-Resolver — canonische Term-Formen (Story 3.2)
> **Status:** abgeleitet (Story 3.2) — committete, **append-only** Registry des kanonischen Schreibweisen-Resolvers der deterministischen Relevanzbestimmung (Term-Ziehverfahren, `schema/compiler.md` §3.2).
> **Normative Grundlage:** `schema/compiler.md` (abgeleitet, Story 3.1/3.2) — §3.2 Relevanzbestimmung (Term-Ziehverfahren, Kanonisierungs-Resolver, dreistufige Erhebung); AD-13 (textuell-deterministische Mittel), AD-17h/A0-19 (Determinismus-Vertrag: gleicher Git-State + gleiche Eingabemenge → gleiche Candidate-Liste in gleicher Reihenfolge), A0-18 (Deterministische Relevanzbestimmung), PRD OQ-3.
> **Ableitungsdatum:** 2026-08-19
## Zweck
Diese Datei ist die einzige committete Registry des **kanonischen Schreibweisen-Resolvers** der Relevanzbestimmung (§3.2 der Compiler-Instruktion). Sie macht die Normalisierung eines gezogenen Terms **deterministisch pinbar**: weil sie selbst Teil des Git-States ist, ist bei gleichem Git-State die Auflösung „Schreibvariante → canonische Form" eindeutig (AD-17h/A0-19). Sie wird ausschließlich **append-only** gepflegt — bestehende Einträge werden nie verändert, neue Einträge werden nur angehängt (Ergänzung einer neu erkannten canonischen Form/Synonymgruppe).
**Geltungsbereich:** Die Registry normalisiert **Fachbegriffe** der Relevanzbestimmung (entscheidungsrelevante Terme nach §3.2-Term-Ziehverfahren). Sie fügt **kein** Frontmatter-Feld, **kein** Schema-Prädikat, **keine** §7-Invaliditätsklasse, keinen Validator-/Vertrags-/`raw/`-Change hinzu (AD-3, D-3).
## Registry-Format
Je Eintrag (eine Zeile in der Tabelle):
- **canonische Form** — der normalisierte Term als Kebab-Case-Slug (Nur-Kleinbuchstaben `[a-z0-9-]`, `-`-Kollaps). Genau **eine** canonische Form je Semantik (A0-18).
- **erlaubte Schreibvarianten** — Schreibweisen, die auf die canonische Form normalisiert werden (bündel-findend per `-`-Kollaps: `[-_ ]``-`, lowercased gemäß §3.2-Normalisierung). **Gespeicherte Form:** canonical-Form- und Variantenzeile tragen ausschließlich die **bereits normalisierte** Form (lowercased, `-`-gekollabst) — die Spalte enthält also nie das Roh-Notat, sondern dessen Normalisierungs-Resultat. `[]` = keine weitere Variante (nur die canonische Form selbst gilt), bzw. noch keine Einträge committet.
- **Semantik** *(optional)* — disambiguierende Kurzangabe, warum dieser Term eine eigene canonische Form trägt (nur bei Bedarf; ergänzt die canonische Form, ist aber nicht Teil der Normalisierungslogik).
**Lookup-Verfahren (deterministische Reihenfolge):** Die Auflösung eines gezogenen Terms folgt exakt dieser Ordnung: (1) **lowercasing**; (2) **Binde-Varianten-Kollaps** `[-_ ]``-` (§3.2 Pkt. 1b); (3) **Lookup der normalisierten Form** in der Registry — Treffer in der Spalte *canonische Form* oder in der Spalte *erlaubte Schreibvarianten* → Ergebnis ist die **canonische Form** desselben Eintrags. Ist die normalisierte Form nicht auffindbar, gilt der Term **wie notiert** (Kollaps-normalisiert; §3.2 Pkt. 1b — kein stiller Ausschluss). Normalisierung und Lookup sind dadurch vollständig deterministisch: gleiche Normalisierungs-Regeln + gleiche Registry (Git-State) → gleiche Auflösung.
**Eindeutigkeits-Invariante:** Jede normalisierte Form kommt in der Registry **genau einmal** vor — als canonische Form **oder** als Variante eines Eintrags, nie in beiden Spalten und nie in zwei Einträgen (auch nicht als Canon eines Eintrags und Variante eines anderen). Wird diese Invariante verletzt, ist der Resolver-Zustand nicht eindeutig auflösbar.
**Append-only-Regel:** Neue Zeilen werden am Ende der Tabelle angehängt; ein bestehender Eintrag wird **nie** editiert. Muss eine Semantik umbenannt werden, wird das in `deferred-work.md` als Mapping-/AD-7d-Nähe-Fall notiert (Ask-First), nicht durch Edits in dieser Registry.
**Konflikt-Verfahren (keine stillen Anhängungen):** Soll ein neuer Eintrag eine normalisierte Form tragen, die bereits in der Registry vorkommt (Canon oder Variante — auch mit abweichender Semantik), wird **nicht** angehängt (das würde die Eindeutigkeits-Invariante verletzen): der Konflikt wird in `deferred-work.md` notiert und dem Nutzer als Ask-First-Frage vorgelegt (Analogie: Umbenennungsregel oben), bis eine Entscheidung eine eindeutige Auflösung erlaubt.
## Registry
| canonische Form | erlaubte Schreibvarianten | Semantik |
|---|---|---|
| — (noch keine Einträge committet) | `[]` | Die Registry ist leer. Mit dem ersten Run, der einen fachlichen Term deterministisch zieht (Story-3.2-Term-Ziehverfahren, §3.2), wird der erste Eintrag hier committet (append-only). |
*Hinweis (Eröffnungs-Zustand):* Für die Story-3.2-Instruktion selbst ist kein Term-Eintrag erforderlich — die Relevanzbestimmung ist auch mit leerer Registry vollständig definiert (gezogene Terme werden wie notiert verwendet, solange keine canonische Auflösung committet ist; §3.2 Pkt. 1 — kein stiller Ausschluss). Die committete, leere Registry ist der deterministisch pinbare Resolver-Zustand.
## Abschlussklausel
Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); keine neue §7-Invaliditätsklasse; kein Standalone (D-3); keine Vertragsänderung. Diese Datei ist ein neues Artefakt unter `schema/`, nebengeordnet zur Compiler-Instruktion — gleiche read-only-Hierarchie (nur der append-only-Ausbau ist vorgesehen).
+199 -15
View File
File diff suppressed because one or more lines are too long
+29
View File
File diff suppressed because one or more lines are too long