8 Commits
Author SHA1 Message Date
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
17 changed files with 5256 additions and 17 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.
@@ -364,3 +391,156 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
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)
@@ -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,572 @@
#!/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 unabhängige Läufe über demselben
# committeten Baum -> identische Bundle-States bis auf at; die Assertion
# vergleicht echte Content-Hashes (nicht nur Vorhandensein); der Witness
# (wiki/alpha.md) 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
for file in "${@:2}"; do
# Body = alles nach dem zweiten "---" (Frontmatter exkludiert, deterministisch)
body=$(awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}' "$file")
# Wortgrenzen-Match: ganze Wörter — Substring-/Frontmatter-Treffer liefern nichts.
# Term-Normalisierung (§3.2 Pkt. 1b) ist lowercasing — der Match ist daher case-insensitiv.
if printf '%s' "$body" | grep -Eiq "(^|[^A-Za-z0-9])${term}([^A-Za-z0-9]|$)"; then
basename "$file" .md
fi
done
}
# log.md-Eintrag (datumsgruppiert, neueste zuerst; Vertrag §5). Deterministisch fester Tag.
log_bullet() { # $1 = Bullet-Text (eine Zeile)
local day="2026-08-20"
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
}
# =====================================================================================
# 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) — §5.14 Pkt. 2
# =====================================================================================
runlabel "DET-2: ZWEI_RUN_IDENTISCH (nicht-vakuum) — zwei unabhängige Läufe über demselben committeten Baum -> identische Bundle-States bis auf at; echte Content-Hashes (Witness weicht byte-weise von Baseline ab)"
run2() { # $1 = Run-Name ; führt die Instruktion über dem committeten Baum aus
isolate "$1"
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-/Kandidaten-Output (§5.14 Pkt. 2: identische Plan-/Kandidaten-/Reihenfolge-Outputs)
local plan
plan="cand=alpha;form=update;graphic=aktualisieren;baseline=$BASE"
# Mutations-Commit als ein commitierter Run-Ausgangszustand (unterschiedliches at je Run:
# A0-20-Konvention — der at-Wert ist Wanduhr, NICHT Teil des deterministischen Vergleichs)
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: RUNAT
---
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
# at-Maskierung für den deterministischen Vergleich (§5.14 Pkt. 3): DET-2 demonstriert
# die Identität der NICHT-at-Bestandteile (Plan-, log-, index-, alpha-Content + TREE) —
# dafür wird die at-Zeile vor dem Hash auf ein festes Token normalisiert (die at-Varianz
# selbst ist die benannte Ausnahme, A0-20; ihre Behandlung demonstriert DET-4 positiv und
# negativ). Die Ausnahme ist dokumentiert, kein stiller Ausschluss; alle übrigen
# Bestandteile gehen byte-identisch in den Hash ein.
sed -i "s/^ at: .*/ at: AT/" wiki/alpha.md
log_bullet "- Determinismus-Bestätigung (Zwei-Run, §5.14 Pkt. 2): $plan; Baseline $BASE"
git add -A && git commit -qm "Run: alpha-Update ($1)"
# --- Bundle-State projizieren (deterministisch): echte Content-Hashes (nicht-vakuum) ---
echo "PLAN=$plan"
echo "SHA_ALPHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)"
echo "ABS_BASELINE=$(git show "$BASE:wiki/alpha.md" | 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)"
echo "TREE=$(git rev-parse 'HEAD:')"
}
echo "--- Lauf A (unabhängig) ---"
SA=$(run2 run2a)
echo "$SA"
echo "--- Lauf B (unabhängig, über demselben committeten Baum) ---"
SBV=$(run2 run2b)
echo "$SBV"
# Zwei unabhängige Läufe -> identische Bundle-States bis auf at
[ "$(echo "$SA" | grep '^PLAN=')" = "$(echo "$SBV" | grep '^PLAN=')" ] || { echo "HARD-FAIL (DET-2): Plan-/Kandidaten-Outputs weichen ab (AD-17h/A0-19, §5.14 Pkt. 2)" >&2; exit 1; }
[ "$(echo "$SA" | grep '^SHA_ALPHA=')" = "$(echo "$SBV" | grep '^SHA_ALPHA=')" ] || { echo "HARD-FAIL (DET-2): alpha-content-Hash weicht zwischen Runs ab (Byte-Differenz ausserhalb at, §5.14 Pkt. 3)" >&2; exit 1; }
[ "$(echo "$SA" | grep '^SHA_LOG=')" = "$(echo "$SBV" | grep '^SHA_LOG=')" ] || { echo "HARD-FAIL (DET-2): log.md-Hash weicht ab" >&2; exit 1; }
[ "$(echo "$SA" | grep '^SHA_INDEX=')" = "$(echo "$SBV" | grep '^SHA_INDEX=')" ] || { echo "HARD-FAIL (DET-2): index.md-Hash weicht ab" >&2; exit 1; }
[ "$(echo "$SA" | grep '^TREE=')" = "$(echo "$SBV" | grep '^TREE=')" ] || { echo "HARD-FAIL (DET-2): Bundle-Baum nach at-Maskierung weicht ab (Soll: byte-identisch, §5.14 Pkt. 2)" >&2; exit 1; }
# Nicht-vakuum-Witness: der produzierte Content weicht byte-weise von der Baseline ab
ABS_A=$(echo "$SA" | grep '^ABS_BASELINE=' | cut -d= -f2)
SHA_A=$(echo "$SA" | grep '^SHA_ALPHA=' | cut -d= -f2)
[ -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; }
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 "RESULT: PASS — DET-2: ZWEI_RUN_IDENTISCH — zwei unabhängige Läufe über demselben committeten Baum produzieren identische Bundle-States (Plan-, log-, index-, alpha-Content-Hashes und Bundle-TREE byte-identisch, jeweils bis auf die benannte at-Ausnahme, die für den Vergleich maskiert ist); Vergleich nicht-vakuum (Witness vs. Baseline, echte Content-Hashes, §5.14 Pkt. 2, FT-10/AD-17h AC-1)"
# =====================================================================================
# 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)"
@@ -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,162 @@
---
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: 0
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-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 (Vergleichs-Operandum = committeter Baum; identische Plan-/Kandidaten-/Reihenfolge-Outputs; kein neuer Prozess/Server/MCP, D-3), (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; 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)**: ein Agent führt die Instruktion aus und bestätigt in einer Session die Zwei-Run-Identität — 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. 364381 ca.); §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. 409ff.`) Determinismus-Vorbehalt „Em-Dash-Lücke an Story 3.8 übergeben" — **aufgelöst**, Verankerung in §5.14 benannt; §8-Normreferenz `AD-17h` (`Z. 430`) auf **§5.14-Anker** angehoben (mit der `at`-Ausnahme-Nennung); Revisionslog-Eintrag **Revision 3.3** nach Z. 461. Anker (IST-Zeilen nach Implementierung): §5.14-Sektion Z. ≈364; §7-Determinismus-Bullet nach Z. 420; §8-AD-17h in Z. 430; Revisionslog **Revision 3.3** nach Z. 461.
- `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)
**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 die Determinismus-Enforcement fehlt, then lebt sie als Agent-Instruktions-Validator (§5.14) und ist als mechanisch durch die Sandbox bestätigt (Q-6, A0-19; AC-3).
- 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).
## 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; der Produzent führt die Instruktion zweimal über demselben committeten Git-State aus und vergleicht die Bundle-States; re-executierbare Formeln), (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 unabhängige Läufe über demselben committeten Baum → identische Bundle-States bis auf `at`; die Assertion vergleicht echte Content-Hashes (nicht nur Vorhandensein)
- 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, 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.
## 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:374](../../schema/compiler.md#L374) — Pkt. 3 Ausnahme-Menge: allein der `generated.at`-Wanduhr-Gap; alle übrigen Bestandteile byte-identisch (kein stiller Ausschluss).
- [compiler.md:375](../../schema/compiler.md#L375) — Pkt. 4 Abweichungs-Klassifikation: jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsdefekt, kein Rauschen (NFR-4, Zustands-Restaurations-Invariante).
- [compiler.md:376](../../schema/compiler.md#L376) — 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:476](../../schema/compiler.md#L476) — 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:152](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L152) — DET-1..DET-8-Szenarien (Bundle-State, Zwei-Run-identisch nicht-vakuum, Zwei-Run-Abweichung, `at`-Gap, Em-Dash, Kollaps, Match-Scope, Orphan); Läufe Exit 0 mit 8 harten PASS-Assertionen.
**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).
- [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.
@@ -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-19-2026 13:14
last_updated: 08-20-2026 09:05
project: wow20
project_key: NOKEY
tracking_system: file-system
@@ -53,12 +53,12 @@ development_status:
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: 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
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: review
epic-3-retrospective: optional
epic-4: backlog
File diff suppressed because one or more lines are too long
+111 -10
View File
@@ -38,7 +38,7 @@ Bestätigung (Story 1.4): schema/validator.md (mechanische Prüfung, kein L
1. Vor der Anlage prüfen, ob die erkannte Wissenseinheit **bereits als Concept** im Bundle existiert (deterministisch: Dateikollision über den relativen OKF-Pfad, AD-7a).
2. **Update-Routing (statt Kollision-Hold; Story 3.1):** Existiert bereits ein Concept mit dem Ziel-Pfad, wird **nicht** stumm überschrieben und **kein** Duplikat angelegt — die erkannte Wissenseinheit wird als **Update-Kandidat** im **bestehenden Concept-Pfad** aktualisiert (Erweitern/Präzisieren/Korrigieren gemäß §5.9; FR-6). Die Mutationsmechanik für Updates spezifiziert §5.9; der Kollisions-Hold-Schutzprinzip („nicht stumm überschreiben") bleibt als Grundsatz der Erhaltung erhalten (AD-16-Default: bestehende Provenienz/Inhalte werden nie ohne Beleg entfernt). Der **Neu-Anlage-Pfad** dieser Instruktion bleibt für Wissenseinheiten, deren Ziel-Pfad **nicht** belegt ist (§5.1/§5.7).
**Kandidatenliste (betroffen-Bestimmung):** Vor jeder Mutation erhebt der Producer die Menge der betroffenen Concepts als **nachvollziehbare Kandidatenliste** (relative OKF-Pfade ohne `.md`) mit **textuell-deterministischen Mitteln** (AD-13): (a) Term-/Konzept-Überschneidung zwischen der neuen Evidenz und den bestehenden Concept-Bodies via `grep`/`ripgrep` über `wiki/` (z. B. `rg -l '<term>' -g '!log.md' wiki/` bzw. GNU-grep-Form `grep -rl '<term>' --exclude=log.md wiki/`); (b) `index.md`-Traversal (Bundleroot- und Area-`index.md`-Dateien, gewurzelte Erreichbarkeit Root → Area → Concept, §5.8) der dadurch betroffenen Bereiche; (c) Link-Following aus bereits betroffenen Concepts (§5.6-Pin) auf weitere Concept-Pfade — **mit besuchter Menge** (ein bereits besuchter Concept-Pfad wird nicht erneut besetzt; keine Schleife bei zyklischen Links). Die Erhebung folgt der feinkörnigen, verbindlichen Mechanik in **§3.2 Relevanzbestimmung (Story 3.2)** — Term-Ziehverfahren (deterministisch aus der neuen Evidenz abgeleitet, kanonischer Schreibweisen-Resolver `schema/canonical-terms.md`, mehrere Terme je Einheit), dreistufige Erhebung mit `log.md`-Exklusion (die Candidate-Liste ist auf Concept-Pfade definiert, `log.md` ist kein Kandidat) und Determinismus-Vertrag (AD-17h/A0-19) — **Erhebung nach §3.2**. Die Kandidatenliste wird textuell festgehalten (Pre-Run-Reconcile-Check-Block, §5.9 Pkt. 6). Keine Embeddings/Vector-Suche (AD-13); §5.9 bindet die Erhebung an die in §3.2 genannten deterministischen Mittel.
**Kandidatenliste (betroffen-Bestimmung):** Vor jeder Mutation erhebt der Producer die Menge der betroffenen Concepts als **nachvollziehbare Kandidatenliste** (relative OKF-Pfade ohne `.md`) mit **textuell-deterministischen Mitteln** (AD-13): (a) Term-/Konzept-Überschneidung zwischen der neuen Evidenz und den bestehenden Concept-Bodies via `grep`/`ripgrep` über `wiki/` (z. B. `rg -l '<term>' -g '!log.md' wiki/` bzw. GNU-grep-Form `grep -rl '<term>' --exclude=log.md wiki/`); (b) `index.md`-Traversal (Bundleroot- und Area-`index.md`-Dateien, gewurzelte Erreichbarkeit Root → Area → Concept, §5.8) der dadurch betroffenen Bereiche; (c) Link-Following aus bereits betroffenen Concepts (§5.6-Pin) auf weitere Concept-Pfade — **mit besuchter Menge** (ein bereits besuchter Concept-Pfad wird nicht erneut besetzt; keine Schleife bei zyklischen Links). Die Erhebung folgt der feinkörnigen, verbindlichen Mechanik in **§3.2 Relevanzbestimmung (Story 3.2)** — Term-Ziehverfahren (deterministisch aus der neuen Evidenz abgeleitet, kanonischer Schreibweisen-Resolver `schema/canonical-terms.md`, mehrere Terme je Einheit), dreistufige Erhebung mit `log.md`-Exklusion (die Candidate-Liste ist auf Concept-Pfade definiert, `log.md` ist kein Kandidat) und Determinismus-Vertrag (AD-17h/A0-19) — **Erhebung nach §3.2**. Die Kandidatenliste wird textuell festgehalten (Pre-Run-Reconcile-Check-Block, §5.9 Pkt. 6). Keine Embeddings/Vector-Suche (AD-13); §5.9 bindet die Erhebung an die in §3.2 genannten deterministischen Mittel; **der Stufe-a-Match-Scope (ganze Wörter über den Body, exklusive YAML-Frontmatter, §3.2-Pkt.-1b-Schließung/§5.14 Pkt. 5) gilt für die Stufe-a-Form hier unverändert**.
Löst eine erkannte Wissenseinheit auf **keinen** bestehenden Concept-Pfad auf (kein Update-Kandidat), wird sie als **neue** Einheit über den Neu-Anlage-Pfad (§5.1/§5.7, §3-Pkt.-1/-2-Kollisionsprüfung ist damit erstbestanden) behandelt.
3. Der Run prüft zusätzlich, ob `wiki/index.md` als Bundleroot existiert (V-1-Vorbedingung des Validators); fehlt sie, darf kein Concept erzeugt oder aktualisiert werden (Run-FAIL, Vertrag §2).
@@ -48,11 +48,12 @@ Diese Sektion ist der **einzige Instruktions-Ort** der feinkörnigen, **textuell
1. **Term-Ziehverfahren (deterministisch):** Die Candidate-Terme werden deterministisch aus der neuen Evidenz (committete `raw/`-Dateien, §1 Pkt. 1/2) abgeleitet — nicht freie LLM-Auswahl:
- **(a) Bedeutungstragende Token-Folgen:** Der Producer benennt die bedeutungstragenden Fachbegriffe der Wissenseinheit gemäß der §2-Interpretation (fachliche Signifikanz; kein Stoppwort-Abgleich nötig, aber auch kein freies Urteil). Der Umfang „bedeutungstragend" ist die Auswahl derjenigen Begriffe, die das erkannte Thema identifizieren — als Token-Folgen über eine sprachliche Einheit hinweg zulässig (z. B. `quanten-protocol-schlüssel`).
- **(b) Normalisierung über den kanonischen Schreibweisen-Resolver:** Jeder gezogene Term wird durchgängig normalisiert: (i) lowercasing; (ii) Binde-Varianten-Kollaps `[-_ ]``-` (En-Dash ``, Bindestrich `-`, Unterstrich `_`, Leerzeichen — jedes Vorkommen wird in einen einzelnen Bindestrich kollabiert); (iii) Auflösung über die committete, append-only Registry **`schema/canonical-terms.md`** (ein Eintrag = canonische Form + erlaubte Schreibvarianten; der Resolver ist damit Bestandteil des Git-States und die Auflösung pinbar). **Genau eine canonische Form je Semantik** (A0-18). **Kein stiller Ausschluss:** ist eine Variante nicht in der Registry auflösbar, wird der Term **wie notiert** verwendet (Kollaps-normalisiert) — niemals still verworfen.
- **(b) Normalisierung über den kanonischen Schreibweisen-Resolver:** Jeder gezogene Term wird durchgängig normalisiert: (i) lowercasing; (ii) Binde-Varianten-Kollaps `[-_]``-` (En-Dash ``, Em-Dash `—`, Bindestrich `-`, Unterstrich `_`, Leerzeichen — jedes Vorkommen wird in einen einzelnen Bindestrich kollabiert; die Kollaps-Reichweite und die übrigen Schließungen definiert der Append-Block weiter unten — **maßgeblich für den Kollaps ist die geschlossene Kollaps-Klasse `[-–— _]`**); (iii) Auflösung über die committete, append-only Registry **`schema/canonical-terms.md`** (ein Eintrag = canonische Form + erlaubte Schreibvarianten; der Resolver ist damit Bestandteil des Git-States und die Auflösung pinbar). **Genau eine canonische Form je Semantik** (A0-18). **Kein stiller Ausschluss:** ist eine Variante nicht in der Registry auflösbar, wird der Term **wie notiert** verwendet (Kollaps-normalisiert) — niemals still verworfen.
- **(c) Mehrere Terme je Einheit erlaubt:** Eine Wissenseinheit kann mehrere bedeutungstragende Terme tragen; die Candidate-Liste ist dann die **Vereinigung** der Treffer über alle Terme, bereinigt über die besuchte Menge (Pkt. 3c — ein Pfad nur einmal).
- **Bekannte Determinismus-Lücke (aufgezeichnet, nicht still hinzugefügt):** Die Kollaps-Klasse `[-_ ]` deckt den Em-Dash `—` **nicht** ab (nur En-Dash ``). Em-Dash-Varianten fallen damit nicht unter den Kollaps — eine erkannte Synonym-Lücke, die **Story 3.8** als offene Determinismus-Frage übergeben ist (s. `deferred-work.md`; nicht stillschweigend in §3.2 ergänzt).
- **Geschlossene Determinismus-Lücken (Story 3.8; deterministische Regel-Ergänzungen, §5.14):** (a) **Em-Dash in der Kollaps-Klasse** — die Kollaps-Klasse ist um den Em-Dash `—` **ergänzt** (`[-–— _]``-`): En-Dash ``, Em-Dash `—`, Bindestrich `-`, Unterstrich `_` und Leerzeichen kollabieren identisch auf **genau einen** Bindestrich — eine Schreibvariante mit Em-Dash (z. B. `wissen — relevanz`) erhält damit dieselbe canonische Form wie ihre En-Dash-/Bindestrich-/Unterstrich-/Leerzeichen-Variante (kein stiller Ausschluss, keine unterschiedliche canonische Form je Separator; §5.14-Abweichungs-Klassifikation). (b) **Kollaps-Reichweite (deterministisch):** jedes Separator-Vorkommen wird auf **genau ein `-`** kollabiert; Läufe (`a--b`) kollabieren auf ein `-` (`a-b`); führende (`-x``x`) und trailende (`x-``x`) Separatoren werden getrimmt. (c) **Match-Scope der Stufe a (Pkt. 2a):** Stufe a matcht **ganze Wörter** über den **Body** des Concepts, **exklusive YAML-Frontmatter** — Substring-Treffer und Frontmatter-Treffer (z. B. in `sources[].resource`/`generated.by`) liefern **keine** Kandidaten (deterministischer Stufe-a-Scope, §5.14; fixiert das §3.2-Pkt.-2a-grep ohne Wortgrenzen-/Frontmatter-Klausel). Die Schließung erhebt **keinen** neuen Prädikat-/Format-/Frontmatter-Key und ändert **keinen** bestehenden §3.2-Wortlaut (append-only-Regel-Ergänzung; AD-3, D-3).
2. **Term-übergreifende Erhebung über `wiki/` (drei Stufen):** Der Producer erhebt die betroffenen Concept-Pfade in **drei textuell-deterministischen Stufen** (a → b → c). Ab der Workspace-Root:
- **(a) Stufe a — grep/ripgrep über `wiki/`:** `rg -l '<term>' -g '!log.md' wiki/` (rgs native Glob-Exklusions-Syntax — `--exclude` ist kein rg-Flag; das GNU-grep-Äquivalent ist `grep -rl '<term>' --exclude=log.md wiki/`, §5.6-Scan-Scope-Konvention). `<term>` = jeder gezogene Term aus Pkt. 1 nach Normalisierung. Beide Formen exkludieren `log.md` **strukturell** (unabhängig von dessen Inhalt) — die Candidate-Liste bleibt auf Concept-Pfade definiert.
- **(a) Stufe a — grep/ripgrep über `wiki/`:** `rg -l '<term>' -g '!log.md' wiki/` (rgs native Glob-Exklusions-Syntax — `--exclude` ist kein rg-Flag; das GNU-grep-Äquivalent ist `grep -rl '<term>' --exclude=log.md wiki/`, §5.6-Scan-Scope-Konvention). `<term>` = jeder gezogene Term aus Pkt. 1 nach Normalisierung. Beide Formen exkludieren `log.md` **strukturell** (unabhängig von dessen Inhalt) — die Candidate-Liste bleibt auf Concept-Pfade definiert. **Match-Scope (Pkt.-1b-Schließung, §5.14 Pkt. 5):** beide Formen erfüllen den Stufe-a-Match-Scope der geschlossenen Determinismus-Lücke — **ganze Wörter** über den **Body** (exklusive YAML-Frontmatter); Substring- und Frontmatter-Treffer (z. B. in `sources[].resource`/`generated.by`) liefern **keine** Kandidaten. Konsequent als **zweistufige, tool-portable Mechanik**: (1) **Sweep** mit der Formel je `<term>` (Substring-/dateiweite Suche — die Formel ist ein trichiger Erhebungsschritt, keine Scope-Filterung); (2) **deterministischer Scope-Filter** über die so gefundenen Pfade: der Producer prüft je Pfad, ob `<term>` als **ganzes Wort** im **Body** (Zeilen **nach der zweiten `---`-Zeile** — dem schließenden Frontmatter-Limit; die Frontmatter-Zeilen davor sind **nie Treffer-Ziel**) vorkommt — tool-portabel (z. B. rg `-w` über den Body bzw. GNU `grep -w`/`\b` nach `awk`-Frontmatter-Strip) — und verwirft Substring- und Frontmatter-only-Treffer deterministisch. `<term>` ist je Erhebung ein **einzelner** normalisierter Term (Pkt. 1); werden **mehrere Terme** je Einheit gezogen (Pkt. 1c), wird die Formel je Term ausgeführt und die Candidate-Liste ist die **Vereinigung** der Treffer, bereinigt über die besuchte Menge (Pkt. 3c) und in Zuwachs-Sicht-Ordnung (Pkt. 3b).
- **(b) Stufe b — `index.md`-Traversal:** Für die in Stufe a getroffenen Bereiche (und die Bundleroot) folgt der Producer der gewurzelten Erreichbarkeit Root → Area → Concept (§5.8): trifft ein Term nur `wiki/index.md` oder eine Area-`index.md` (nicht einen Concept-Body), so sind alle **darunter gewurzelten Concept-Pfade** Treffer der Stufe b (TRAVERSAL_REACH_ONLY). Fehlende Bundleroot → Run-FAIL (V-1, §3 Pkt. 3, besteht fort).
- **(c) Stufe c — Link-Following mit besuchter Menge:** Aus bereits als betroffen erhobenen Concepts folgt der Producer die Concept-Links (§5.6-Pin) auf weitere Concept-Pfade — file-relativ auflösen (§5.7 Pkt. 4), **jeder bereits besuchte Concept-Pfad wird nicht erneut besucht** (besuchte Menge): Zyklen (A → B → A) enden, die Candidate-Liste bleibt endlich (LINK_FOLLOWING_ZYKLUS).
3. **Candidate-Liste (Ausgabe) + Determinismus-Vertrag:**
@@ -267,6 +268,11 @@ Diese Sektion ist der **einzige Instruktions-Ort** der Update-Mutationsmechanik
- **No-Op-Kandidat (Review-Loop-2-Präzisierung):** Erhebt die Kandidatenerhebung einen Pfad, dessen Body die neue Evidenz **bereits vollständig enthält** (keiner der drei Update-Formen greift — keine neue belegte Aussage, keine Schärferung, keine Ersetzung), ist der Kandidat ein **No-Op**: **keine** Body-Mutation, **kein** `generated.at`-Bump, **kein** `sources`-Zusatz, **kein** `log.md`-Eintrag — der Pfad bleibt byte-identisch (Erhaltungs-Invariante, Pkt. 5). Die Entscheidung „keiner der drei Formata greift" ist selbst textuell deterministisch: sie trifft nur, wenn die neue Evidenz keine Aussage trägt, die im Body nicht bereits als belegte Aussage vorhanden ist (Term-/Stellen-Abgleich mit §5.5-Inline-Verweisen); im Zweifel (neue belegte Aussage auch nur in abgewandelter Form) greift Erweitern/Präzisieren — der No-Op ist die *engere* Auslegung.
- **Mehrfach-Treffer-Konsolidierung (ein Run, mehrere Einheiten auf denselben Concept-Pfad):** Ergeben mehrere neue Wissenseinheiten desselben Runs auf **demselben** bestehenden Concept-Pfad ein Update, werden ihre Mutationen in **einem einzigen Update dieses Pfads** konsolidiert (eine Body-Änderung, **ein** `log.md`-Eintrag „Story 3.1-Update", ein konsolidierter `sources`-Zuwachs, **ein** `generated.at`-Zeitstempel — der einmalige `at` des Runs). Die Reihenfolge der Einheiten wird dabei textual-deterministisch aus der Verarbeitung der Evidenz abgeleitet: REIHENFOLGE = **Auftritt der Einheiten in der Zuwachs-Sicht** (Datei-Reihenfolge des R-1-Zuwachsbefunds, dann Stellen-Reihenfolge innerhalb der Datei); nur bei Einheiten desselben Ortes (identischer Dateipfad + identische Stelle) greift die lexicografische Ordnung als Tie-Break (Review-Loop-2-Präzisierung: die Reihenfolge ist für *alle* Einheiten definiert, nicht nur für vollständig unabhängige). Die AD-16-Default-Erhaltung bleibt für jede betroffene Stelle gewahrt.
- **Commit-Boundary = Mutations-Boundary (AD-17f, §0):** Auch im Update-Pfad werden **Zwischenstände nie als fertige Mutation veröffentlicht** — die Mutationen der Update-Kandidaten werden als Ganzes committet, und zwar erst, nachdem der Diff-Selbsttest (Pkt. 5) ohne Ghost-Diff abgeschlossen ist. Ein Ghost-Diff ist dann als **Abbruch-Vorlauf „korrigierter Teil-Run"** im `log.md`-Eintrag des betroffenen Runs gekoppelt (siehe Disagreement-/Run-Notiz Pkt. 4), damit die Ursache textuell nachvollziehbar bleibt.
- **Operationelle Ausführungs-Ebene je Update-Form (Präzisierungsebene, Story 3.3 / Revision 2.8; Präzisierung des frozen Story-3.1-Regeltexts, keine Re-Negotiation — die operationellen Regeln sind die deterministische Brücke Regel → Run, AD-17h/A0-19):** Die folgenden Ausführungs-Regeln präzisieren, **was der Producer bei jedem Form-Fall konkret tut und was er unangetastet lässt**. Je Update-Form gelten (a) das **Abgrenzungskriterium** (Welche Form greift wann? — ausschließlich aus der committeten Evidenz abgeleitet), (b) die **Struktur-Erhaltungsregel** (was bleibt unverändert — die „Struktur" aus FR-6/AC-2: Frontmatter-Subset, bestehende belegte Aussagen mit §5.5-Inline-Verweisen, §5.6-Pin) und (c) der **Textgenauigkeits-Rahmen** (wie geschärft wird). **No-Op ist keine eigene Form, sondern die Nicht-Form** (keiner der drei Form-Bullets greift) mit eigenem Abgrenzungskriterium und der Erhaltungsregel „volle Byte-Identität". **Abgrenzungs-Reihenfolge (Prozess, AD-17h):** Der Producer prüft die Abgrenzungskriterien **in fester Reihenfolge Korrigieren → Präzisieren → Erweitern → No-Op** (strikt pro Evidenz-Einheit) und wählt die **erste zutreffende** Form: (1) ersetzt die Evidenz eine bestehende belegte Aussage als fehlerhaft/überholt → **Korrigieren**; (2) sonst schärft sie eine bestehende Formulierung/Abgrenzung (ersetzte Wortlautfolge → Ersetzungsbeleg nötig) → **Präzisieren**; (3) sonst trägt sie eine neue belegte Aussage, die noch nicht existiert → **Erweitern**; (4) sonst → **No-Op** (engere Auslegung). Damit ist jeder Einheit genau eine Form zugeordnet (kein Form-Überlapp, gleiche Evidenz → gleiche Form). Gleicher Git-State + gleiche Eingabemenge → identischer Update-Vorgang (AD-17h); der `generated.at`-Wanduhr-Gap (gleiches Eingabeset, unabhängige Runs, verschiedene `at`) bleibt offene A0-20-Konvention mit Home **Story 3.8** (oben, `generated.at`-Konvention) — unverändert bindend.
- **Korrigieren — Abgrenzungskriterium:** greift genau dann, wenn die Evidenz eine bestehende Aussage als **fehlerhaft/überholt ersetzt** (ersetzte Wortlautfolge liegt vor → Ersetzungsbeleg nötig). **Struktur-Erhaltungsregel (Korrigieren):** die ersetzte Aussage wird **nicht still gelöscht**, sondern explizit durch die ersetzende Aussage **abgelöst**; die Ersetzung trägt den **Ersetzungsbeleg** (§5.5, AD-16-Default Erhaltung); die Ablösung erfolgt an der Position der ersetzten Aussage — dabei bleibt die Aussage **in ihrer ursprünglichen Wortlautfolge als abgelöster Bestand** erhalten (die Beleg-Kennung bleibt gültig, nur die Aussage-Fortschreibung wechselt), der Rest des Bodys bleibt unverändert. **Textgenauigkeits-Rahmen (Korrigieren):** nur die fehlerhafte/überholte Aussage wird ersetzt; keine flankierende Umschreibung.
- **Präzisieren — Abgrenzungskriterium:** greift genau dann, wenn die Evidenz eine **bestehende Formulierung/Abgrenzung schärft** (präzisierende Information an derselben Aussage, ohne Ersatz). **Struktur-Erhaltungsregel (Präzisieren):** die geschärfte Aussage bleibt an ihrer **Position**, das Satzgefüge wird **nicht umgebaut**, umgebende Aussagen, §5.5-Inline-Verweise anderer Aussagen und die §5.6-Linkform bleiben unverändert; der Beleg wird bei der Aussage neu/nachgeführt (§5.5). **Textgenauigkeits-Rahmen (Präzisieren):** Schärfen = Formulierung/Abgrenzung **an der Aussage selbst**; **kein Satz-Umbau** (keine Neustrukturierung des Absatzes), **kein Neuschreiben ohne Ersetzungsbeleg** — jede ersetzte Formulierung braucht die neue/fortgeführte `raw/`-Evidenz als Inline-Verweis.
- **Erweitern — Abgrenzungskriterium:** greift dann, wenn die neue committete Evidenz eine **neue belegte Aussage** trägt, die im bestehenden Body **nicht** existiert (Term-/Stellen-Abgleich gegen die §5.5-Inline-Verweise; kein bestehender Anker). **Struktur-Erhaltungsregel (Erweitern):** die neue Aussage wird als **eigener Absatz angefügt** (Body-Ende); bestehende belegte Aussagen bleiben **byte-identisch**, keine Umschreibung bestehender Absätze; Frontmatter nur `sources`-**Zuwachs um den echten neuen Beleg** + ein `generated.at`-Bump. **Textgenauigkeits-Rahmen (Erweitern):** die neue Aussage wird eigenständig formuliert, trägt den §5.5-Inline-Beleg mit existierender Stellen-Kennung; kein Satz-Umbau des Bestands. *(Überlappt eine Einheit mehrere Formen — z. B. sie trägt zugleich eine neue belegte Aussage und eine Schärfung einer bestehenden —, ordnet der Producer die Einheit der in der Abgrenzungs-Reihenfolge ersten zutreffenden Form zu (deterministisch, AD-17h); die Textgenauigkeits-Rahmen der übrigen Formen gelten für die jeweiligen Teilbestandteile unverändert.)*
- **No-Op — Abgrenzungskriterium (Nicht-Form, engere Auslegung, bestehende Regel oben):** greift nur, wenn **keine** der drei Formen greift (die Evidenz ist bereits vollständig im Body); im Zweifel trifft eine der drei Formen — die Entscheidung ist textuell deterministisch (Term-/Stellen-Abgleich). **Erhaltungsregel (No-Op):** volle Byte-Identität (keine Mutation, kein `at`-Bump, kein `sources`-Zusatz, kein `log.md`-Eintrag).
3. **Index-/Link-Form unverändert (§5.6-Pin):** Ein reines Body-Update ändert die Concept-Identität nicht → der bestehende Index-Link (Bundleroot oder Area-`index.md`) bleibt unverändert gültig; **kein neuer Link** bei reinem Body-Update (kein Index-`index.md`-Zusatz). Die **`log.md`-Eintragspflicht** (Pkt. 4) bleibt davon **unberührt**: *jedes* Update — auch ein reines Body-Update — MUSS seinen `log.md`-Eintrag „Story 3.1-Update" führen; nur der **Index-**Link bleibt unverändert. Wird durch das Update eine Concept-Kategorie (Root vs. Area) oder die Identität berührt, ist das **Ask-First** (AD-7d-Renames/Redirects; nicht Teil von Story 3.1). Neue zulässige Concept-Links (Beziehungsschicht, §5.6) werden nur gesetzt, wenn die neue Erkenntnis eine echte, inhaltsbegründete Beziehung rechtfertigt.
4. **`log.md`-Eintragspflicht (Vertrag §5):** Jedes Update wird als datumsgruppierter Eintrag in `wiki/log.md` dokumentiert (Header = ISO-Datum `YYYY-MM-DD`, neueste zuerst), verknüpft mit dem mutierten Concept-Pfad und den genutzten `raw/`-Quellen. Für Updates wird der Eintrag als „**Story 3.1-Update**" markiert (disambiguierbar von Anlage-Einträgen). Konflikt-/Erhaltungsfälle (Pkt. 2 „Korrigieren" ohne Ersetzungsevidenz) werden explizit als Disagreement-Eintrag mit dem mutierten Concept-Pfad geführt (AD-16b, Vertrag §5) — der Epic-4-Interface-Fall bleibt dokumentiert, ohne Korrektur-Klassifikation hier.
5. **Erhaltungs-Invariante (Kern) + deterministischer Diff-Selbsttest (AD-17h, FT-6/FR-12):** Ein Compilation Run darf nur die Concepts **neu anlegen oder verändern**, die durch den erkannten Erkenntnis-Zuwachs tatsächlich betroffen sind. **Nicht betroffene Concept-Pfade bleiben byte-identisch unverändert** — es gibt **nie** „Regenerate Everything" (AD-5, A0-6). Die Inkrementalität ist als **re-executierbarer Diff-Selbsttest** mechanisch kontrollierbar: Nach jedem Run prüft der Producer ab der Workspace-Root
@@ -276,11 +282,99 @@ Diese Sektion ist der **einzige Instruktions-Ort** der Update-Mutationsmechanik
```
(erste Zeile: modifizierte/gelöschte Pfade gegen die Baseline; zweite Zeile: zusätzlich ungetrackte neue Pfade `??` und staged-Änderungen — die Duplikat-Kontrolle „keine neue Datei" braucht die `??`-Sicht, die `git diff` allein nicht liefert; `<Baseline-Commit>` siehe Pkt. 6 R-1)
dass die geänderten Dateien eine Teilmenge von **(Kandidatenliste Neu-Anlage-Zielpfade `log.md` nachgeführte `index.md`)** — mit der Kandidatenliste aus §3 Pkt. 2 (betroffene *bestehende* Concepts, relative OKF-Pfade ohne `.md`) **plus** den Zielpfaden aller im selben Run neu angelegten Concepts (§5.1/§5.7 — Misch-Runs) plus `log.md` und jede nachgeführte `index.md` als zusätzliche zulässige Einträge. **Probe-Zeitpunkt (D-1-Präzisierung):** die Probe läuft **vor dem Commit**, am Ende des P2-Check-Blocks (Pkt. 6) — nach dem Commit wäre `git diff` leer und die Probe vacuous (Commit-Boundary = Mutations-Boundary, AD-17f: der Run committiert erst, nachdem die Probe ohne Ghost-Diff abgeschlossen ist). **Pfad-Normalisierung vor dem Teilmenge-Vergleich:** die Proben-Ausgabe trägt `wiki/`-Präfix und `.md`-Endung; die Kandidaten-/Ziel-Pfade sind definiert als relative OKF-Pfade *ohne* `.md` — für den Vergleich werden von jedem Proben-Eintrag `wiki/`-Präfix und `.md`-Suffix gestrippt (deterministischer Normalisierungsschritt). Jede Abweichung (**Ghost-Diff** auf einem nicht betroffenen Pfad) ist ein Instruktions-Verstoß (FT-6): Der Producer behebt sie textuell benannt (NFR-4) und **rollt den Ghost-Diff zurück, bevor der Run als gültig vermerkt wird** — der beobachtbare Endzustand des Bundles bleibt damit konsistent (AD-6-Backstop). **Rollback-Mechanik:** der Ghost-Diff betrifft nur Pfade *außerhalb* der erlaubten Menge; der Producer stellt die betroffenen Pfade aus dem Baseline-Zustand wieder her (für modifizierte Pfade: `git checkout -- <wiki/pfad>`; für ungetrackte neue Dateien: Datei löschen; für Index-Änderungen: `git checkout -- wiki/index.md` bzw. die Area-`index.md`) — der Teilzustand-Rollback des §5.3 Pkt. 3 / §6 Pkt. 3 greift daneben unverändert. Für Story 3.1 selbst (Instruktions-Story ohne Inhalts-Mutation) ist der Nachweis: `git diff --name-only -- wiki/` liefert ausschließlich `wiki/log.md` — keine Concept-Datei, keine `index.md` (die aggressive `git diff --stat -- wiki/`-Default-Effektiv-ausgabe zeigt denselben Pfad, `wiki/log.md | 1 +`). *(Anmerkung: `schema/compiler.md` und `deferred-work.md` liegen **außerhalb** `wiki/` und sind daher kein Teil dieser Diff-Probe — sie gehören zum Instruktions-, nicht zum Bundle-Baum; der Ghost-Diff-Begriff dieser Sektion gilt nur für `wiki/`.)*
Die Probe verifiziert dabei **zwei Ebenen** (Struktur-Erhaltungs-Bezug, Story 3.3): **(1) die Pfad-Mengen-Ebene** (obiger Teilmenge-Vergleich — welche Pfade der Run berührt hat) und **(2) die Struktur-Ebene je berührtem Pfad** — die Struktur-Erhaltung „ohne die Struktur zu zerstören" (FR-6/AC-2) wird pro betroffenem Concept-Pfad über die Pkt.-2-Regeln geprüft (Frontmatter-Subset-Konformität: nur `sources`-Zuwachs um echte neue Belege + `generated.at`-Bump, kein neuer Key, keine Entfernung ohne Beleg; bestehende belegte Aussagen nicht umgeschrieben ohne dass Präzisieren/Korrigieren greift; §5.5-Inline-Verweise weiterhin gültig soweit Beleg Body-Bestand; §5.6-Linkform unverändert, keine neuen Links außer bei echten Beziehungen — Verstöße sind textuell zu benennen (NFR-4) und werden vor dem Commit behoben, sonst gilt der Pfad als Ghost-Diff mit Rollback gemäß dieser Pkt.-5-Konsequenz).
6. **Run-Vorphase-Bausteine (Defer R-1 + P2, in den Update-Pfad eingearbeitet):** Beide sind **keine** neuen Prozesse — wiederverwendbare, reproduzierbare textuelle Check-Blöcke innerhalb der Instruktions-Ausführung (D-3):
- **Change-Detection (Defer R-1, Input-Zuwachserkennung):** Vor der Interpretation bestimmt der Producer, **welche `raw/`-Dateien Zuwachs** sind (neu/modifiziert). Deterministische Mittel: `git diff --name-only <Baseline-Commit> -- raw/` auf das `raw/`-Verzeichnis und/oder der **SHA-256-Record aus `raw/**/source.md`** (Provenienz-Sidecar, §1 Pkt. 4). Als `<Baseline-Commit>` dient der **letzte committete Zustand des Workspace** (deterministisch: der HEAD der vorherigen Mutations-Boundary, AD-17f). **Auflösungs-Regel (D-2-Präzisierung, Review-Loop 2):** der Producer **notiert den `<Baseline-Commit>` (vollen SHA) im `wiki/log.md`-Eintrag des Runs** — damit ist er deterministisch auflösbar ohne Domain-State-Annahme an Git (AD-14: Git liefert Historie, nicht Domain-State; der log.md-Eintrag ist der State-Referenz-Punkt, keine Git-Historie-Interpretation). **Diskrepanz-Regel:** widersprechen sich `git diff`-Befund und SHA-256-Record für dieselbe Datei, **gewinnt der `git diff`-Befund** (Commit-Boundary = Mutations-Boundary, AD-17f); der SHA-256-Record bleibt Sekundär-Fingerprint. Ist der SHA-256-Record unlesbar/fehlend, wird die Datei dennoch als Zuwachs **nicht doppelt** verarbeitet (textueller Hinweis) und gegen den `git diff`-Befund abgeglichen (kein Doppel-Verdikt). **Fallback:** existiert keine vorherige Mutations-Boundary (frischer Workspace ohne Lauf-Historie), gilt **alle `raw/`-Dateien als Zuwachs**. Die Diff-Probe in Pkt. 5 läuft gegen **dasselbe** `<Baseline-Commit>` (Review-Loop-2-Korrektur des Rev-2.4.1-Claims: die Pkt.-5-Probe trägt das Baseline-Commit-Argument explizit).
- **Pre-Run-Reconcile-Check-Block (Defer P2):** Vor jeder Mutation durchläuft der Producer den gebündelten Vorprüf-Block und hält ihn textuell fest: (1) **Input-Zustand** (AD-17a, I/O-Matrix `INPUT_UNCOMMITTED`; Review-Loop-2-D-3): Working-Copy von `raw/` und `wiki/` gegen HEAD prüfen — bei Abweichung (uncommitteder Zustand) bricht der Run mit dem **textuell benannten Abbruch „published/committed Input erforderlich"** ab, **vor** Interpretation und vor jeder Mutation (keine Mutation gegen Zwischenstände); (2) Ziel-Pfade (Ausgangs-Kandidatenliste, §3 Pkt. 2); (3) Quellen-Existenz (EC-1 via Validator-Punkt: jede referenzierte `raw/`-Datei existiert als Datei); (4) Betroffenheits-Kandidatenliste (§3 Pkt. 2, textuell-deterministisch); (5) `wiki/index.md`-V-1-Vorbedingung (fehlende Bundleroot → Run-FAIL, §3 Pkt. 3, Vertrag §2). Dieser Block ist der in `deferred-work.md` als P2 notierte Pre-Plan-Schritt und wird durch Story 3.1 als Teil des inkrementellen Runs ausgeführt. **Zeitpunkt (Review-Loop-2-Präzisierung):** der Block wird **nach Reconcile (2) und vor Mutieren (4)**, am **Anfang der Mutationsphase**, einmal pro Run durchgeführt — die Kandidatenliste (Elemente 2/4) existiert zu diesem Zeitpunkt bereits; die **Diff-Probe (Pkt. 5) läuft am Ende desselben Blocks, nach den Mutationen, vor dem Commit**. Die Kandidatenliste, die hier festgehalten wird, ist **die** Kandidatenliste, gegen die der Diff-Selbsttest (Pkt. 5) prüft (keine zweite, davon abweichende Erhebung nach der Mutation).
- **Pre-Run-Reconcile-Check-Block (Defer P2):** Vor jeder Mutation durchläuft der Producer den gebündelten Vorprüf-Block und hält ihn textuell fest: (1) **Input-Zustand** (AD-17a, I/O-Matrix `INPUT_UNCOMMITTED`; Review-Loop-2-D-3): Working-Copy von `raw/` und `wiki/` gegen HEAD prüfen — bei Abweichung (uncommitteder Zustand) bricht der Run mit dem **textuell benannten Abbruch „published/committed Input erforderlich"** ab, **vor** Interpretation und vor jeder Mutation (keine Mutation gegen Zwischenstände); (2) Ziel-Pfade (Ausgangs-Kandidatenliste, §3 Pkt. 2); (3) Quellen-Existenz (EC-1 via Validator-Punkt: jede referenzierte `raw/`-Datei existiert als Datei); (4) Betroffenheits-Kandidatenliste (§3 Pkt. 2, textuell-deterministisch); (5) `wiki/index.md`-V-1-Vorbedingung (fehlende Bundleroot → Run-FAIL, §3 Pkt. 3, Vertrag §2); (6) **Struktur-Erhaltungs-Check (Story-3.3-Ergänzung als zusätzliches textuelles Element):** vor dem Commit prüft der Producer an den betroffenen Concept-Pfaden, dass keinerlei unbefugte Keys im Frontmatter entstanden sind (Vertrag §3.3/§3.4-Subset, §6.5-Kriterium-1), **keine stille Löschung** bestehender belegter Aussagen/Provenienz erfolgt ist (AD-16, Pkt. 2 „Korrigieren"-Form) und **keine Links** verändert oder ohne echte Beziehung neu gesetzt wurden (§5.6-Pin, Pkt. 3); Verstöße sind textuell benannt (NFR-4) und vor dem Commit zu beheben; die Kandidatenliste bleibt **die** Liste gegen die der Diff-Selbsttest (Pkt. 5) prüft (keine zweite Erhebung nach der Mutation). Dieser Block ist der in `deferred-work.md` als P2 notierte Pre-Plan-Schritt und wird durch Story 3.1 als Teil des inkrementellen Runs ausgeführt. **Zeitpunkt (Review-Loop-2-Präzisierung):** der Block wird **nach Reconcile (2) und vor Mutieren (4)**, am **Anfang der Mutationsphase**, einmal pro Run durchgeführt — die Kandidatenliste (Elemente 2/4) existiert zu diesem Zeitpunkt bereits; die **Diff-Probe (Pkt. 5) läuft am Ende desselben Blocks, nach den Mutationen, vor dem Commit**.
7. **Worked Example (an die reale Ist-Lage gebunden, re-executiert; Review-Loop-2-Korrektur):** Ein Run mit neuer committeter Evidenz `raw/epics/epics-2026-08-14.md#FR-12` (Zuwachs über die bisher verarbeitete Stelle hinaus; die Kennung FR-12 existiert real in der Datei) trifft über `rg -l 'FR-12' -g '!log.md' wiki/` das bestehende Root-Concept `wiki/knowledge-kompilation-inkrementell.md` (Term-/Konzept-Überschneidung — re-executierter Befund: der Grep-Ausgabe-Pfad ist `knowledge-kompilation-inkrementell`, `wiki/log.md` exkludiert gemäß §3.2-Pkt.-2a; das Rev-2.4.1-Beispiel `wissensarchitektur/source-material.md` enthielt den Term **nicht** und ist damit korrigiert). Der Run aktualisiert diesen Pfad (Body-Erweiterung mit neuem §5.5-Inline-Beleg `(raw/epics/epics-2026-08-14.md#FR-12)`, `sources`-Ergänzung um diesen `resource` — sofern nicht bereits deklariert, sonst No-Op gemäß Pkt. 2, `generated.at` = aktueller Run-Zeitstempel, `log.md`-Eintrag „Story 3.1-Update"). Die Diff-Probe (Pkt. 5, `<Baseline-Commit>`-Form) zeigt ausschließlich `log` und `knowledge-kompilation-inkrementell` (betroffen, normalisiert) — kein Ghost-Diff, keine `index.md` im Diff (der Index-Link bleibt unverändert).
## 5.10 Synthese aus mehreren Sources (Story 3.4)
Diese Sektion ist der **Instruktions-Ort der Synthese-Dimension** (AD-4, FR-7): wie der Producer mehrere `raw/`-Quellen desselben Themas zu **einer** gemeinsamen Wissensrepräsentation mit **gemischter, claim-granularer Provenienz** zusammenführt — statt getrennter Zusammenfassungen je Quelle (FR-7). Sie ist eine weitere Spezifikations-Ebene der Mutationsphase §5 (nach §5.9, vor §6), geschlossen auf dem §7-Vorbehalt (Story 3.4). Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone (D-3). Synthese ist eine **Querschnitt-Dimension, keine fünfte Update-Form**: die §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` bleibt die **einzige** Form-Wahl (§5.9 Pkt. 2); Synthese erweitert die Ausführung um gemischte Provenienz + Konsolidierung (bei Update auf ein bestehendes Concept) bzw. um die Neu-Anlage mit Multi-Source-`sources` (bei neuer Einheit). Eine Synthese ist ein Compilation-Vorgang wie die Anlage/das Update: sie unterliegt denselben Phasen (§0: Interpretieren → Reconcile → Synthetisieren → Mutieren → Validieren), derselben Commit-Boundary = Mutations-Boundary (AD-17f, §0/§5.3) und derselben Validator-Erfolgsbedingung (§6).
1. **Synthese-Stimulus (Input der Synthese-Dimension):** Der Stimulus ist eine **Synthese-Einheit**: mindestens **zwei belegende `raw/`-Quellen** (§2-Interpretation) zum **selben Thema** (dieselbe erkannte Wissenseinheit, dieselbe Semantik — gemessen über Term-/Konzept-Überschneidung nach §3.2 bzw. die §2-Pkt.-2-Mehrquellen-Regel: eine Wissenseinheit kann aus mehreren Abschnitten/Dokumenten stammen). Deterministische Erkennung: die neue committete Evidenz (§1 Pkt. 1, AD-17a) enthält ≥ 2 `raw/`-Dateien, deren abgegrenzte Einheiten auf **dieselbe** Zielrepräsentation auflösen. Eine einzelne Quelle bleibt auf dem §5.9-/§3.2-Pfad (Anlage mit `sources`-Zuwachs bzw. Update) — **keine** Synthese-Dimension. Der Stimulus verarbeitet ausschließlich **published/committed** Input (R-1-Change-Detection, §5.9 Pkt. 6).
2. **Ein-Ziel-Repräsentation (gemeinsame Wissensrepräsentation, FR-7 AC-1):** Mehrere Quellen zum selben Thema erzeugen **ein einziges** Synthese-Concept über **einen** Ziel-Pfad (§5.7-Routing; bei neuem Thema über §5.1/§5.7-Neu-Anlage, bei bestehendem Pfad über das §5.9-Update). **Getrennte Zusammenfassungs-Concepts je Quelle sind verboten** (FR-7) — es entsteht nie eine per-Source-Concept-Menge. Die `sources`-Liste des Synthese-Concepts trägt **≥ 2 Einträge** (je belegende Quelle ein Eintrag), **deterministisch lexikografisch nach `resource` sortiert (LC_ALL=C)** — unabhängig von Verarbeitungs- oder Datei-Reihenfolge (AD-17h/A0-19; konsistent zur §3.2-Pkt.-3b-Lexikografie-Ordnung und zur §5.9-Tie-Break-Logik). Bei Update auf ein bestehendes Concept wächst `sources` um die zweite/weitere Quelle (Frontmatter-Regel §5.9 Pkt. 2 „`sources` nur um echte neue Belege ergänzen"; bestehende Einträge bleiben unverändert).
3. **Gemischte claim-granulare Provenienz (AD-4a/4b, A0-3):** Jede belegte Aussage des Synthese-Concepts bleibt auf `raw/` zurückführbar — der Body trägt **je Aussage einen Inline-`raw/`-Verweis in §5.5-Form** (`(raw/<datei.md>#<stellen-kennung>)` bzw. Komma-Form; voller Pfad je Beleg). **Konsolidierung ohne Provenienz-Verlust (FR-7 AC-3, AD-4):** redundante Informationen aus mehreren Quellen — dieselbe Aussage, von mehreren Quellen unabhängig belegt — werden zu **einer** Aussage konsolidiert, die **alle** betroffenen Belege trägt (**Multi-Beleg**, §5.5-Pkt.-1-Semikolon-Form: `(raw/a.md#X; raw/b.md#Y)` — voller Pfad je Beleg). **Keine stille Beleg-Tilgung** (AD-4): eine konsolidierte Aussage listet jeden ursprünglichen Beleg. **AD-16-Default:** widersprüchliche Aussagen (diskrepanter Inhalt) werden **nicht** still zu scheinbar eindeutigen Aussagen zusammengeführt, sondern bleiben Bestand und werden als Disagreement in `log.md` explizit abgelegt (Pkt. 7; Epic-4-Interface, Story 4.1 — keine Korrektur-Klassifikation hier vorweggenommen; Sandbox-Szenario N2 demonstriert beide Behauptungen + Disagreement-Eintrag). **Body-Reihenfolge deterministisch (AD-17h/A0-19):** die Positionierung der konsolidierten Aussagen im Body folgt der **lexikografischen Ordnung (LC_ALL=C) ihrer Beleg-Anker** (`raw/<datei.md>#<stellen-kennung>`, voller Pfad), nicht einer Verarbeitungs- oder Datei-Reihenfolge — gleicher Git-State + gleiche Eingabemenge ⇒ identische Aussagen-Reihenfolge. Das Konsolidierungs-Erkennungs-Kriterium („dieselbe Aussage") ist die sachlich äquivalente inhaltliche Formulierung über **dieselbe erkannte Wissenseinheit** (Befund-Äquivalenz, nicht Wort-Identität; abgegrenzt über §2-Interpretation/dieselbe Semantik) — sie ist pro Befund eindeutig aus dem Quell-Text der Einheit begründbar.
4. **Übernahme aus bestehenden Concepts (AD-4c — nie alleinige Provenienz):** Übernimmt das Synthese-Concept Formulierungen aus bestehenden Concepts (Kontext-/Synthese-Umformulierung, nicht eigenständig gegen `raw/` belegt), trägt die übernommene Aussage den **§5.5-Kontext-Marker** (Pkt. 2, Muster „übernommen aus `<Concept>` auf Basis von `<source>`, nicht eigenständig belegt") — **kein bestehendes Concept ist alleinige Provenienz eines anderen**. Die Quelle des übernommenen Concepts bleibt damit textuell rückverfolgbar; die Direktübernahme aus `raw/` (ohne Zwischen-Concept) verwendet das Muster „übernommen aus `<source>` (rohe Quelle), nicht eigenständig belegt". **Sandbox-Szenario N3** demonstriert den Kontext-Marker („übernommen aus `gamma` auf Basis von `raw/gamma-v1.md#S-1`, nicht eigenständig belegt") und prüft, dass die Quelle des übernommenen Concepts **nicht** als eigene `sources`-Quelle eingetragen wird (AD-4c).
5. **Reflektierter Wissensstand (FR-7 AC-4, NFR-7):** Der Body trägt **integrierte, je Aussage provenance-tags versehene Aussagen****keine per-Source-Zusammenfassungs-Struktur** (keine Blöcke „Quelle A: … / Quelle B: …"). **Reflektiertheits-Selbsttest (textuell deterministisch, grepbasiert):** es existiert **keine** Zeile, die einen Quell-Label abschnittsstrukturiert — das Muster ist ein Zeilenanfangs-Label „`Quelle <Label>:`"/„`Source <Label>:`" (einbuchstabig, mehrbuchstabig oder mit Ziffer/Unterstrich suffigiert), grepbasiert `grep -nE '^(Quelle|Source) [A-Z][A-Za-z0-9_ -]*:' <Synthese-Concept>` liefert leer; jede Aussage trägt ihren Provenienz-Tag (§5.5). Ein Befund (Aneinanderreihung) ist ein **Instruktions-Selbsttest-FAIL** (NFR-7, kein stiller Vorbeilass — NFR-4): der Producer stellt den Body vor Run-Abschluss so um, dass die Integration erfüllt ist.
6. **§5.6-Pin unverändert:** Synthese fügt **keine neue Linkform** und **keine** neuen `index.md`-Links ohne echte, inhaltsbegründete Beziehung hinzu — §5.6-Pin (genau-eine-Linkform, file-relativ mit `.md`-Endung) und §5.9 Pkt. 3 (kein neuer Index-Link bei reinem Body-Update) gelten unverändert. **Form-Wahl-Klassifikationsprobe (Defer U2/U7, Story 3.3):** eine Synthese-Einheit, die zugleich neue + schärfende + ersetzende Anteile trägt (Form-Überlapp), wird per §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` **strikt pro Evidenz-Einheit** der **ersten zutreffenden Form** zugeordnet (deterministisch, AD-17h); die Textgenauigkeits-Rahmen der übrigen Formen gelten für die jeweiligen Teilbestandteile unverändert.
7. **`log.md`-Eintragspflicht (Vertrag §5):** Jede Synthese wird als datumsgruppierter Eintrag in `wiki/log.md` dokumentiert. Ein Synthese-**Update** auf ein bestehendes Concept führt den Eintrag „**Story 3.1-Update**" (Disagreement-Fälle als Disagreement-Eintrag mit dem mutierten Concept-Pfad, AD-16b); eine Synthese-**Neu-Anlage** eines neuen Synthese-Concepts führt einen **Anlage-Eintrag** (disambiguierbar von Update-Einträgen). Der Eintrag verknüpft das erzeugte Concept mit der **vollständigen Multi-Source-`sources`-Liste** und hält den `<Baseline-Commit>` (R-1, §5.9 Pkt. 6) fest.
8. **Erhaltungs-Invariante & Determinismus-Vertrag (AD-17h/A0-19):** Der **§5.9-Pkt.-5-Diff-Selbsttest gilt für Synthese-Runs unverändert**: die mutierten/neu angelegten Pfade sind eine Teilmenge von (Kandidatenliste Neu-Anlage-Zielpfade `log.md` nachgeführte `index.md`) — **keine neue Datei außer echten Ziel-Pfaden** (Duplikat-Kontrolle via `??`-Sicht, `git status --porcelain -- wiki/`). Gleicher Git-State + gleiche Eingabemenge → identischer Synthese-Vorgang (Ziel-Pfad via §3.2/§5.7, `sources`-Liste lexikografisch nach `resource` LC_ALL=C, Konsolidierung und Form-Zuordnung wie Pkt. 3/6); der `generated.at`-Wanduhr-Gap (gleiches Eingabeset, unabhängige Runs, verschiedene `at`) bleibt offene A0-20-Konvention mit Home **Story 3.8** (§5.9 Pkt. 2, `generated.at`-Konvention) — unverändert bindend. **Duplikat-Fall (Synchronisation zwischen Concepts und Evidenzbasis):** ein `raw/`-Quellpfad erscheint **nur einmal** in einer `sources`-Liste — ein bestehender Eintrag derselben `resource` wird **nicht** doppelt angelegt (die Neuanlage eines Synthese-Concepts setzt voraus, dass kein bestehendes Concept dieselbe Quelle bereits mit derselben Stellen-Kennung als Beleg nutzt; für Update-Fälle gilt das §5.9-Pkt.-2-Prinzip „bestehende Einträge bleiben unverändert" — der Zuwachs einer Quelle, die bereits existiert, wird niemals doppelt eingetragen). **Unzugeordnete/verwaiste Evidenz (Orphan-Kontrolle, pre-existing — dokumentiert den Vertrag, wie er vor dieser Story bestand):** neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer bleibt **unzugeordnet** (weder still getilgt noch hintenherum als eigenes Concept angelegt) und wird in `log.md` als verwaist protokolliert — kein Banner/keine stille Vorbearbeitung; eine Präzisierung der Reconcile-Orphan-Politik leistete die Verankerung in §5.13-Phase 5 / **die deterministische Reconcile-Orphan-Regel unten (Story 3.8; §5.14 Pkt. 5)** — eine Staffelung an Epic-4 (Story 4.1) bleibt für die Verwaist-Klassifikation offen. **Deterministische Reconcile-Orphan-Regel (Story 3.8; §5.14-Präzisierung):** der Verwaist-Befund — neu committete `raw/`-Evidenz ohne jeglichen Ziel-Pfad-Treffer (kein Update-Kandidat nach §3.2, keine Synthese-Zuordnung nach Pkt. 17, kein §5.7-Neu-Anlage-Ziel-Pfad) — wird deterministisch aus dem committeten Git-State erhoben (Zuwachs gg. `<Baseline-Commit>`, §5.9 Pkt. 6 R-1) und als **datumsgruppierter `log.md`-Eintrag** verwaist protokolliert (Header `YYYY-MM-DD`, neueste zuerst — Vertrag-§5-Datumsgruppe; Quell-Pfad + `<Baseline-Commit>`); **kein Banner**, **keine stille Vorbearbeitung**, **keine** eigenständige Concept-Anlage aus verwaister Evidenz (AD-16-Default: Erhaltung — die Evidenz bleibt in `raw/` unangetastet, AD-3; keine Korrektur-/Erweiterungs-Klassifikation hier vorweggenommen — Epic-4-Interface, Story 4.1). Gleicher Git-State + gleiche Eingabemenge ⇒ identischer Verwaist-Befund und identischer `log.md`-Eintrag (AD-17h/A0-19, §5.14).
## 5.11 Leasing & Dirty-Tree-Schutz für konkurrierende Producer (Story 3.5)
Diese Sektion ist der **einzige Instruktions-Ort der Koordinations-Dimension für konkurrierende Producer** (D-3, Story 3.5; 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. Sie ist eine weitere Spezifikations-Ebene der Mutationsphase §5 (nach §5.10, vor §6) und **schließt den §7-Vorbehalt** dieser Koordinations-Dimension (Story 3.5). Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone (D-3). 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, **kein neuer Frontmatter-Key für Lease-Metadaten**; ein solcher wäre Ask-First). Leasing ist eine **Querschnitt-Dimension, keine neue Mutations-Form**: die §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` bleibt die **einzige** Form-Wahl; die §5.10-Synthese-Mechanik bleibt unberührt. Eine Lease-geschützte Mutation ist ein Compilation-Vorgang wie die Anlage/das Update: sie unterliegt denselben Phasen (§0: Interpretieren → Reconcile → Synthetisieren → Mutieren → Validieren), derselben Commit-Boundary = Mutations-Boundary (AD-17f, §0/§5.3) und derselben Validator-Erfolgsbedingung (§6).
1. **Lease-Akquise (AD-17a, A0-12) — deterministisch (AD-17h):** Ein Producer, der einen Bereich (Area oder Root-Scope gemäß Pkt. 2) bearbeiten will, akquiriert die Lease **bevor** er mutiert, gegen einen **eindeutigen Commit-Object-Wert** (Merge-Base-Disziplin):
- **Arbeits-Branch-Form:** Der Producer arbeitet auf einem Branch der Form **`lease/<area>/<id>`** (z. B. `lease/alpha/run-a`; für Root-Scope-Bereiche `<area>` = `wiki`, z. B. `lease/wiki/run-a`). `<area>` ist der Bereichs-Pfad (deterministisch nach §5.7), `<id>` ein vom Producer vergebener, kollisionsfreier Run-Identifier. Der Branch wird **von der Merge-Base aus** erstellt (`git checkout -b lease/<area>/<id>`, Abzweigung von der letzten Mutations-Boundary, §5.9 Pkt. 6 R-1); der Producer arbeitet **ausschließlich** auf diesem Branch und committet dort (Commit-Boundary = Mutations-Boundary, Pkt. 5).
- **Lockfile:** Eine Lease wird durch ein **Lockfile** realisiert, das **semantisch identisch in jedem Adapter** ist (A0-12 — die Realisierung ist **nicht** pro Adapter frei wählbar). Deterministisches Format (AD-17h): das Lockfile liegt unter `lease/<area>/<id>.lock` (außerhalb `wiki/`/`raw/`, im Workspace), enthält den **Feld-Satz** `area`, `id`, `producer`, `baseline_commit` (voller SHA) und — wenn bereits vergeben — `holder_id`, und ist ein **committeter** git-Tracking- oder ein deterministisch benannter ungetrackter Marker (die Semantik „eine Lease je `lease/<area>/<id>`-Branch" ist maßgeblich; die exakte Datei-Ablage ist in jedem Adapter identisch zu halten). **Lease-Hold:** Existiert das Lockfile schon (die Lease ist vergeben), **überschreibt der Producer sie nicht** — er hält die Lease (Lease-Hold) und es erfolgt **keine Mutation** am betroffenen Pfad durch den zweiten Producer; die Koordination geht in den AD-16-Pfad (Pkt. 4) über.
- **Merge-Base-Disziplin (eindeutiger Commit-Object-Wert):** Die Lease wird gegen den **Merge-Base-Commit** akquiriert — den eindeutigen Commit-Object-Wert, von dem beide Producer (der haltende und der neue) ausgehen (deterministisch über `git merge-base` bzw. den notierten `<Baseline-Commit>` aus §5.9 Pkt. 6). Gegen denselben Git-State akquirieren zwei unabhängige Producer **deterministisch dieselbe** Koordinationsentscheidung (gleiche Merge-Base → gleiche Lease-Lage, AD-17h/A0-19). Der akquirierte `<Baseline-Commit>` wird im `log.md`-Eintrag notiert (Pkt. 6, D-2).
- **Lease-Freigabe (Release, deterministisch):** Nach erfolgreichem Run, sobald die Mutation als Ganzes committet ist (Pkt. 5, Commit-Boundary), gibt der Producer die Lease **deterministisch** frei: das Lockfile `lease/<area>/<id>.lock` wird entfernt bzw. als freigegeben markiert und der Abschluss als `log.md`-Eintrag dokumentiert (Pkt. 6). Eine Lease, deren Inhaber den Run **ohne** Freigabe beendet (abgebrochen/verwaist), **verbleibt bis zum Staleness-/Recovery-Mechanismus der Story 3.6** — §5.11 prüft stets nur den **committeten** Zustand (AD-17h/A0-19) und wertet bei einer existierenden, nicht freigegebenen Lease als **Lease-Hold** (Pkt. 1). Staleness-TTL, verwaiste Leases und die `raw/`-Recovery sind Story-3.6-Thema.
2. **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. Ein Producer, der einen Concept-Pfad mutiert, hält damit zugleich die Koordinations-Rechte an der Bundle-Integrität (Bundleroot `index.md`, `log.md`, Root-Concepts), weil jede Mutation die Pfad-Menge von `log.md`/`index.md` (Eintragspflicht bzw. Index-Regel) berühren kann. **Kein Bereich jenseits `wiki/`** ist Lease-Gegenstand (AD-17b-Root-Scope; `raw/` bleibt immutable, AD-3; jenseits-`wiki/`-Lease wäre Ask-First).
3. **Dirty-Tree-Schutz (AD-17e/f, A0-16):** **Vor jeder Mutation** prüft der Producer die Working Copy **auf den zu mutierenden Bereich** (Pre-Mutation-Prüfung; deterministisch: `git status --porcelain -- <Mutationsbereich>` gegen HEAD — der mutierte Bereich ist die Pfad-Menge des Runs, Kandidatenliste Neu-Anlage-Zielpfade `log.md` `index.md`, §5.9 Pkt. 5):
- **Fremde uncommittete Änderungen werden geschützt, nie gelöscht (AD-17e):** Liegen im mutierten Bereich **fremde uncommittete Änderungen** vor (Dirty Tree — Änderungen, die **nicht** vom laufenden Producer stammen und **nicht** committet sind), **löscht der Producer sie nicht** und übergeht sie nicht still. Er schützt sie in eine **Scratch-Zone/Stash** (deterministisch: `git stash push -- <Pfade>` mit Stash-/Verzeichnis-Konvention oder Kopie in eine benannte Scratch-Zone außerhalb `wiki/`, z. B. `scratch/<run-id>/<pfad>.stash`) und **dokumentiert den Vorgang in `log.md`** (Pkt. 6). **Screen-Artefakte beim Schutz** (Stash-/Kopier-Artefakte, die im Bundle-Baum auftauchen würden) werden **textuell benannt** (NFR-4) und liegen außerhalb der erlaubten Diff-Menge (§5.9 Pkt. 5) — sie werden nie als Concept verwechselt. Nach erfolgreichem Run sind die geschützten Änderungen für den ursprünglichen Producer zurückzuspielen (Restore; der Weg ist im `log.md`-Eintrag dokumentiert).
- **UNCOMMITTED_INPUT-Abbruch (AD-17a, I/O-Matrix-Szenario `UNCOMMITTED_INPUT` der Story-3.5-Matrix):** Weicht die **Working-Copy von `raw/` oder `wiki/`** vom committeten HEAD in einem Umfang ab, der als Input des Runs gelten würde (uncommittete `raw/`-Zuwächse oder uncommittete `wiki/`-Abweichungen außerhalb der eigenen, bereits geleasten Mutation), bricht der Run mit dem textuell benannten Abbruch „**published/committed Input erforderlich**" ab — **vor** Interpretation und vor jeder Mutation (keine Mutation gegen Zwischenstände). Dies ist **dieselbe Pre-Run-Prüfung** wie das §5.9-P2-Element (1) (`INPUT_UNCOMMITTED`, Story-3.1-Matrix): dieselbe Bedingung, derselbe Abbruch-Wortlaut — **kein zweiter, separater Abbruch-Pfad**. Keine Mutation, Bundle konsistent.
- **Mutationen operieren nur auf Directory-/Commit-Ebene — Commit-Boundary = Mutations-Boundary (Pkt. 5).**
4. **Kein textueller Auto-Merge — compiler-vermittelter Merge (AD-17c, A0-14):** Zwei Branches mit **ungleichem Inhalt am selben Concept-Pfad** werden **nie textuell automatisch gemerged** (kein stiller textueller Auto-Merge, AD-17c — verboten, Never). Der Merge ist **compiler-vermittelt** und durchläuft die **AD-16-Klassifikation** mit explizitem `log.md`-Eintrag (Pkt. 6; Interface zu Epic 4): die beiden Inhaltsvarianten desselben Pfads werden als **Konflikt** behandelt — der haltende Producer klassifiziert sie gemäß AD-16 (Default: **Erhaltung** — beide Behauptungen bleiben, Disagreement-Eintrag in `log.md`; keine stille Konsolidierung, keine stille Löschung). **Lease-Konflikt (I/O-Matrix `LEASE_KONFLIKT`):** Ein zweiter Producer mit Lockfile-Konflikt am selben Pfad erzeugt **keinen stillen textuellen Auto-Merge**; die Auflösung geht in den compiler-vermittelten AD-16-Pfad (Pkt. 4) mit `log.md`-Eintrag. **Unentscheidbar → menschliche Eskalation (AD-17g):** Ist die AD-16-Klassifikation unentscheidbar, eskaliert der Producer menschlich (textuell benannt, NFR-4) — er nimmt **keine** Auto-Entscheidung vor.
5. **Commit-Boundary = Mutations-Boundary (§0/§5.3-Verweis unverändert):** Auch im Leasing-Pfad gilt: **Zwischenstände werden nie als fertige Mutation veröffentlicht** (AD-17f). Der Producer committet die Mutationen als Ganzes auf dem `lease/<area>/<id>`-Branch, und zwar erst, nachdem der Diff-Selbsttest (§5.9 Pkt. 5) ohne Ghost-Diff abgeschlossen ist. Ein Ghost-Diff (auch ein durch den Dirty-Tree-Schutz erzeugter Screen-Artefakt im Bundle-Baum) ist ein textuell benannter Instruktions-Verstoß (NFR-4) und wird **vor** der Run-Gültigkeit zurückgerollt. Die Pkt.-5-Erhaltungs-Invariante gilt für Leasing-fähige Runs **unverändert** (Pfad-Menge ⊆ Kandidatenliste Neu-Anlage `log.md` Index; AD-5/FT-6).
6. **`log.md`-Eintragspflicht (Vertrag §5):** Jede Lease-geschützte Koordinationsentscheidung wird als datumsgruppierter Eintrag in `wiki/log.md` dokumentiert (Header = ISO-Datum `YYYY-MM-DD`, **neueste zuerst** — Vertrag-§5-Datumsgruppe, wie §5.9 Pkt. 4): **(a) Lease-Akquise** (Branch `lease/<area>/<id>`, `<Baseline-Commit>`, Lockfile-Referenz — deterministisch auflösbar, D-2), **(b) Dirty-Tree-Schutz** (geschützte fremde uncommittete Änderung, Scratching-Ziel, Restore-Weg — „nie gelöscht", AD-17e), **(c) Merge-Klassifikation** (AD-16-Klassifikation bei ungleichem Pfad-Inhalt, Disagreement-/Konflikt-Vermerk) und **(d) Eskalation** (menschliche Eskalation bei Unentscheidbarkeit, AD-17g).
7. **Determinismus-Vertrag (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** (gleiche Merge-Base → gleiche Lockfile-Lage; gleiche Pfad-Inhalte → gleiche AD-16-Klassifikation). Der `generated.at`-Wanduhr-Gap (gleiches Eingabeset, unabhängige Runs, verschiedene `at`) bleibt offene A0-20-Konvention mit Home **Story 3.8** (§5.9 Pkt. 2, `generated.at`-Konvention) — unverändert bindend. **Lease-Staleness/Recovery** (TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery; AD-17d, A0-15) bleibt **Story 3.6** vorbehalten: diese Sektion verweist darauf und mutiert deren Mechanik nicht. (Testbares Seam-Kriterium der Koordinations-Dimension, das 3.5 von 3.6 trennt: **3.5** ist eine auf den **committeten Git-State** bezogene Determinismus-Frage — allein aus Merge-Base + Lockfile + Pfad-Inhalten entscheidbar (AD-17h/A0-19); **3.6** ist eine **Zeit- bzw. Umgebungs-Zustands-Frage**, die den committeten Zustand verlässt — TTL-Ablauf, verwaiste/hängende Leases, Registrierung, `raw/`-Recovery.)
## 5.12 Lease-Staleness & Recovery-Basis (Story 3.6)
Der §5.11-Lease-Hold (`lease/<area>/<id>`-Branches, Lockfile, Merge-Base-Disziplin) wertet stets nur den **committeten Git-State** (AD-17h/A0-19). Diese Sektion ist der **einzige Instruktions-Ort der Staleness-/Recovery-Dimension** (D-3, Story 3.6; AD-17d, A0-15; §5.11-Seam-Kriterium S-1 und Pkt.-7-Text bleiben unverändert): sie definiert, wie eine nach einem **abgebrochenen Run ohne Freigabe** hinterlassene Lease **nicht dauerhaft blockiert** (TTL plus Lease-Registrierung im Clone-Root-State), wie sie **übernommen oder als stale markiert** wird, und dass `raw/` die **Zugriffs-/Consistency-Basis** bleibt (AD-3). Sie ist die Ausformulierung der **Zeit-/Umgebungs-Zustands-Frage** des §5.11-Pkt.-7-Seam-Kriteriums („3.5 = committeter-Git-State-Determinismus; 3.6 = Zeit-/Umgebungs-Zustands-Frage — TTL-Ablauf, verwaiste/hängende Leases, Registrierung, `raw/`-Recovery") und greift §5.11 Pkt. 1 wortgleich auf („verbleibt bis zum Staleness-/Recovery-Mechanismus der Story 3.6"). Der §7-Vorbehalt der koordinations-Dimension ist damit **in dieser Sektion verankert (Story 3.6)**. Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone (D-3). Verwaiste/hängende Leases werden **nie still gelöscht** (AD-17e) und `raw/` bleibt bei jedem Vorgang unverändert (AD-3).
1. **Lease-Registrierung & generationenbasiertes TTL (AD-17d, A0-15) — deterministisch (AD-17h):** Die Registrierung der Leases erfolgt dauerhaft im **Clone-Root-State** — nicht in `wiki/`/`raw/`/Concept-Frontmatter (Vertrag §3.1–§3.7 unverändert) und **ohne Wanduhr-Timestamp im Lockfile- oder Registrierungs-Format** (A0-20-Konvention, §5.11 D1). Ein Run, der eine Lease akquiriert (Pkt. 1), **registriert** sie zur Erzeugungs-Generation `gen` — einem **monotonen Run-Generator (Highlight-`generation`-Zähler je Clone-Root)**, der **deterministisch aus dem committeten Git-State ableitbar** ist: entweder als **Baumableitung aus dem `lease/`-Baum** (höchster, committet sichtbarer Stände-Generator der akquirierten/release-freien Leases) oder als gepinnter **Mono-Commit-`lease-granite-root`-Marker** (ein einziger committeter Markerwert im Clone-Root, der den aktuellen Generationsstand trägt). Die **Quellen-Präzedenz ist deterministisch gepinnt**: liegt **beides** vor (Baumableitung und Marker), **gewinnt der committete `lease-granite-root`-Marker** (expliziter Pinn garantiert Eindeutigkeit); liegt **nur die Baumableitung** vor, dient sie als Generator; liegt **keins** vor (keine committete Lease im Clone-Root-Baum), gilt der **definierte Startwert `gen = 0`** (zählt keine Lease als älter — die erste Akquise startet als Erzeugungs-Generation 1, Pkt. 1). Das **TTL-Ablauf-Kriterium ist generationen-basiert**: eine Lease, deren Erzeugungs-Generation **älter** ist (niedrigere Gen-Zahl als der aktuell höchste sichtbare Reg-Generator des ablaufenden Runs — Gen kleiner = älter), gilt als **stale** (TTL-Ablauf-Äquivalent) — sie **blockiert keinen nachfolgenden Run** (I/O-Matrix `STALE_ABLAUF`). Die A0-20-zugelassene `at`-Notation (dokumentierender Zeitstempel, §5.9 Pkt. 2) bleibt **dokumentierend** — sie ist **nie** eine streng durchsetzende TTL-Steuergröße (ein wanduhr-durchsetzendes Zeit-TTL wäre Ask-First).
2. **holder_id-Ableitung (deterministischer Default; Defer „holder_id-Quelle" Story 3.5 aufgegriffen):** Das Lockfile-Feld `holder_id` (§5.11 Pkt. 1, „eindeutige Producer-/Run-Kennung") wird **deterministisch** als `holder_id := <producer>-<id>` abgeleitet — `<producer>` aus dem Lockfile-Feld `producer`, `<id>` der Run-Identifier aus der Branch-Form `lease/<area>/<id>` (§5.11 Pkt. 1). Die Ableitung ist **reproduzierbar aus dem committeten Git-State** (AD-17h/A0-19): gleicher Producer + gleicher Branch-Suffix → identische `holder_id`, ohne Wanduhr. Eindeutigkeit gilt je **aktiver** Lease (ein vorhandenes Lockfile blockiert ohnehin jede zweite Akquise, §5.11 Pkt. 1 Lease-Hold); ein Producer, der eine längere, kollisionsfreie Kennung wünscht, wählt `<id>` entsprechend — die Ableitbarkeits-/Lesbarkeits-Regel bleibt unverändert.
3. **Verwaist-Klassifikation, Übernahme & Stale-Markierung (AD-17e, AD-17g):** Findet ein neuer Run eine **verwaiste/hängende Lease** (Registrierung ohne Freigabe/Release oder mit älterer Erzeugungs-Generation, Pkt. 1), wird sie **klassifiziert** statt blockiert:
- **VERWAIST_UEBERNEHMEN:** Der neue Run kann die Lease **übernehmen** — gegen die **erneute Merge-Base-Prüfung** (§5.11 Pkt. 1 + die Pkt.-4-Diskrepanz-Regel dieser Sektion, I/O-Matrix `VERWAIST_UEBERNEHMEN`). Die Übernahme wird als `log.md`-Eintrag protokolliert (Pkt. 6). Besteht ein **Konflikt** (die verwaiste Lease hält einen Bereich/`log.md`-Eintrag, der vom Übernehmer anderweitig mutiert würde), geht die Koordination in den **AD-16-Pfad** (Erhaltung; §5.11 Pkt. 4) bzw. bei Unentscheidbarkeit in die **menschliche Eskalation** (AD-17g).
- **VERWAIST_STALE_MARKIEREN:** Ist die Übernahme nicht sinnvoll, markiert der Run die Lease als **stale** — über einen **Registry-Marker** (Registrierungs-Eintrag der aktuellen Generation, Pkt. 1/5) — und hebt damit die Blockade auf; der Vorgang wird als `log.md`-Eintrag protokolliert (Pkt. 6, I/O-Matrix `VERWAIST_STALE_MARKIEREN`).
- **Nie gelöscht (AD-17e):** Die verwaiste Lease selbst (Lockfile, Registrierung, Marker) wird **nie still gelöscht** — sie wird übernommen oder als stale markiert; fremde uncommittete Änderungen werden **nie** als Seitenwirkung entfernt (§5.11 Pkt. 3).
4. **baseline_commit-Merge-Base-Diskrepanz-Regel (Vereinheitlichung; Defer „baseline_commit-Diskrepanz" Story 3.5 aufgegriffen):** §5.11 Pkt. 1 nennt zwei Quellen für den akquirierten Baseline-Commit („deterministisch über `git merge-base` bzw. den notierten `<Baseline-Commit>` aus §5.9 Pkt. 6"). Die **Diskrepanz-Regel** bündelt sie deterministisch (AD-17h): Widersprechen sich der `git merge-base`-Laufzeitbefund und der **notierte** `<Baseline-Commit>` (letzte notierte Mutations-Boundary, §5.9 Pkt. 6 D-2) derselben Lease, **gewinnt der git-abgeleitete Merge-Base-Wert** (Commit-Boundary-Prinzip); der notierte SHA bleibt **Sekundär-Fingerprint**. **Fallback:** Ohne vorherige Mutations-Boundary gelten alle `raw/`-Dateien als Zuwachs (§5.9 Pkt. 6 R-1) und die Merge-Base ist der älteste committete Fundamentpunkt (deterministisch auflösbar, AD-14). Dieselbe Regel gilt bei einer **Lease-Übernahme** (Pkt. 3): die Übernahme validiert die angestrebte neue Merge-Base gegen diese Auflösung, bevor Mutationsrechte übergehen.
5. **`raw/`-Recovery-Basis & native `git stash`-Variante (AD-3, AD-17d/A0-15):** `raw/` ist die **Zugriffs- und Consistency-Recovery-Basis** — die unveränderte, committete Evidenzbasis, auf die ein Run nach einem abgebrochenen Lauf seine Zugriffs-/Konsistenzannahmen bezieht (I/O-Matrix `RECOVERY_RAW_BASIS`). `raw/`-Inhalte werden **bei keinem Vorgang verändert** (AD-3) — auch nicht bei Staleness-/Übernahme-/Stale-Markierungs-Schritten; ein möglicher Real-Baum-Nachweis gegen die `raw/`-Inhaltsebene ist dabei auf **Sandbox-Evidenzwege beschränkt** (kein `git diff`-/SHA-256-Beweis gegen `raw/`-Inhaltsebene auf dem realen Ist-Baum, EC-1-Grenze). Für den **Dirty-Tree-Schutz** (§5.11 Pkt. 3) sind beide textuell zulässigen Sicherungswege nutzbar: die **Kopie in eine benannte Scratch-Zone** (bestehender Pfad) und die **native `git stash`-Variante** (`git stash push -- <Pfade>`, Stash-/Verzeichnis-Konvention) — beide deterministisch im Ergebnis (fremde uncommittete Änderung bleibt byte-identisch geschützt, nie gelöscht, Restore dokumentiert; Defer „native `git stash`-Variante" Story 3.5 aufgegriffen, als Sandbox-Doppel abgebildet).
6. **Registrierungs-Invariante & kumulativer Registry-Aufbau über Runs (Vertrag §5):** Die Registrierung hält je Clone-Root die **Invariante: Gen > erzeugend oder gleiche Gen** — der Registry-Stand regrediert nie auf eine **ältere** Generation als die erzeugende, und er hält stets den **sichtbar höchsten Reg-Generator** (I/O-Matrix `VERWAIST_STALE_MARKIEREN`-Error-Handling, Sandbox STALE-5). Der Registry-Aufbau ist **kumulativ über Runs** (mehrere aufeinanderfolgende Producer schreiben dieselbe Registrierung fort, analog zum `log.md`-Akkumulator; Defer „Sandbox-log-Akkumulator" Story 3.5 aufgegriffen): es existiert **kein eigener `# Log`-Stand** der Registry — `wiki/log.md` ist der **alleinige Aufzeichnungs-Ort** (Vertrag §5), die Registry-Marker selbst sind Git-/Datei-Ebene ohne Eintrags-Body.
7. **`log.md`-Eintragspflicht & Determinismus-Vertrag (Vertrag §5; AD-17h/A0-19):** Jede Staleness-/Recovery-Koordinationsentscheidung wird als datumsgruppierter `wiki/log.md`-Eintrag dokumentiert (Header = ISO-Datum `YYYY-MM-DD`, **neueste zuerst** — Vertrag-§5-Datumsgruppe, wie §5.9 Pkt. 4/§5.11 Pkt. 6): **(a) Registrierung/TTL** (Erzeugungs-Generation, Registry-Marker-Referenz, `<Baseline-Commit>` — deterministisch auflösbar, D-2), **(b) Lease-Übernahme** (verwaiste Lease, angestrebte Merge-Base, neuer holder_id), **(c) Stale-Markierung** (verwaiste Lease → Marker der aktuellen Generation, Blockade aufgehoben) und **(d) Recovery-Basis-Nutzung** (bei Rückgriff auf `raw/` bzw. Stash-Restore, Pfad-Referenz). **Determinismus-Vertrag (AD-17h/A0-19):** Registrierungs-Aufbau, TTL-Ablauf-Kriterium (Generationen), Verwaist-Klassifikation und `log.md`-Texte folgen **deterministisch aus dem committeten Git-State** (gleicher Git-State + gleiche Eingabemenge → identische Staleness-/Recovery-Entscheidung); **kein Wanduhr-Timestamp steuert** einen dieser Vorgänge (A0-20-Konvention unverändert).
## 5.13 Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)
Diese Sektion ist der **einzige Instruktions-Ort der Reason/Mutate-Phasen-Disziplin** (D-3, Story 3.7; AD-6, A0-7): sie bindet die **logische Phasen-Trennung** von AD-6 (Analyse → Änderungsplanung → Mutation → Validierung) als **durchsetzbare Ausführungs-Disziplin** an die bestehende, **unverändert** verbindliche §0-Ablaufstruktur. Der §7-Vorbehalt dieser Trennung ist damit **in dieser Sektion verankert (Story 3.7)**. Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone (D-3) — und sie baut **keine eigene Workflow-Engine** (AD-6, A0-7-AC-4): die Trennung ist eine **textuelle Ausführungs-Disziplin in einer Session** (Analyse → Änderungsplanung → Mutation → Validierung laufen nacheinander in dem einen vorhandenen agentischen Run; kein neuer Prozess/Server/MCP). Die Endzustands-Konsistenz wird als **beobachtbare, über Git prüfbare Eigenschaft** fixiert (A0-7-AC-2): nach einem abgebrochenen/fehlgeschlagenen Run ist der beobachtbare Bundle-Endzustand der Baseline **oder** der einer abgeschlossenen, valide committeten Mutation — **deterministisch und ohne Wanduhr** (AD-17h/A0-19, A0-20: weder Planung noch Rollback hängen von Kalenderzeit ab). Der §5.12-Seam-Satz bleibt unverändert (Lease-Staleness/Recovery: Story 3.6); diese Sektion mutiert deren Mechanik nicht.
| AD-6-Phase (logisch) | §0-Ablaufstruktur (deterministische Takt-Folge) |
|---|---|
| **Analyse** | (0) Input prüfen, (1) Interpretieren, (2) Reconcile (inkl. §3.2-Relevanzbestimmung) |
| **Änderungsplanung** | §5.9 Pkt. 6-P2-Block (Pre-Run-Reconcile-Check-Block, nach Reconcile/vor Mutieren) |
| **Mutation** | (3) Synthetisieren, (4) Mutieren (§§45, §5.9/§5.10/§5.11/§5.12-Mechanik) |
| **Validierung** | (5) Validieren (§6; Rollback §5.3 Pkt. 3 / §6 Pkt. 3, Ghost-Diff-Rollback §5.9 Pkt. 5) |
Die Tabelle ist eine **Zuordnungsklassifikation ohne neue Norm**: §0 bleibt die **deterministische Ausführungs-Folge** (unverändert, keine Um-Nummerierung, keine neue Phase); die **vier** AD-6-Phasen decken sich **logisch** mit ihr (Reconcile ist in die Analyse-Zelle der Zuordnungstabelle gefasst) — ein Umbau des §0-Listentextes oder eine zusätzliche Phase wäre Ask-First.
1. **Änderungsplanung (AD-6, A0-7; Analyse-Ergebnis → konsistenter Plan):** Der **P2-Block (§5.9 Pkt. 6)** ist die **Änderungsplanung der AD-6-Kette** — er wird **Wortlaut-unverändert** referenziert (Rückverweis, keine Doppel-Instruktion): Aus der Analyse (Input prüfen/Interpretieren/Reconcile, §0 (0)(2)) konsolidiert der Producer **vor der Mutationsphase** einen konsistenten Plan — (1) Input-Zustand (AD-17a, committet), (2) Ziel-Pfade, (3) Quellen-Existenz (EC-1), (4) Betroffenheits-Liste (§3 Pkt. 2, textuell-deterministisch nach §3.2), (5) `wiki/index.md`-V-1, (6) Struktur-Erhaltungs-Check (Vertrag §3.3/§3.4-Subset, §5.9 Pkt. 6). Ein **Plan-Defizit** (z. B. fehlgeschlagene Vorprüfung, uncommitteter Input `INPUT_UNCOMMITTED`) wird **textuell benannt** (NFR-4) und **verhindert die Mutation** — die Kette bricht **vor** der Mutationsphase ab (I/O-Matrix `PLAN_BEABSICHTIGT`: „fehlgeschlagene Vorprüfung ⇒ keine Mutation"). Der konsistente Plan wird **textuell festgehalten** (Teil der Ausführungsdisziplin — der P2-Block-Befund; **kein** Artefakt-File, keine Erhaltungs-Invarianten-Ausweitung) und ist **deterministisch aus dem committeten Git-State** ableitbar (AD-17h/A0-19): gleicher Git-State + gleiche Eingabemenge → identische Planung.
2. **Plan-Freeze (Veränderungs-Sperre nach Phasenabschluss; AD-6-Kopplung §5.9 Pkt. 5):** Der Abschluss der Änderungsplanung friert den Plan ein: In der Mutationsphase **wird kein Plan-Gegenstand außerhalb der erlaubten Pfad-Menge****Kandidatenliste Neu-Anlage-Zielpfade `log.md` nachgeführte `index.md`** (§5.9 Pkt. 5) — mutiert (I/O-Matrix `PLAN_FREEZE`). Eine Verletzung der Sperre ist ein **Ghost-Diff (§5.9 Pkt. 5)**: Der Producer rollt den betreffenden Pfad aus dem Baseline-Zustand zurück und benennt die Verletzung **textuell** (NFR-4), bevor der Run als gültig vermerkt wird. Plan-Freeze und Diff-Probe sind dieselbe §5.9-Pkt.-5-Mechanik — diese Sektion institutionalisiert sie als Phasen-Disziplin, ohne deren Mechanik zu ändern.
3. **Mutationsphase — Abbruch mit Zustands-Restaurations-Invariante (AD-6, A0-7-AC-2):** Bricht der Run während der Mutationsphase ab (Fehler, I/O-Matrix `MUTATION_ABBRUCH`), greift die bestehende Rollback-Maschinerie **Wortlaut-unverändert**: **§5.3 Pkt. 3 / §6 Pkt. 3** (Teilzustand des Bundles zurückrollen) und der **Ghost-Diff-Rollback (§5.9 Pkt. 5)**. Die **Zustands-Restaurations-Invariante** macht die erzählte Konsistenz-Eigenschaft zu einer **beobachtbaren, über Git prüfbaren**: nach Abbruch/FAIL ist der **Post-Rollback-Diff gegen die Baseline leer**`git diff <Baseline-Commit> -- wiki/` ist leer und der Bundle-Zustand **== Baseline** (Restaurations-Fall); Erfolgsfall: das Bundle ist der **valide committete Zustand des Runs** (Commit-Boundary = Mutations-Boundary, AD-17f, §0/§5.3). Beide Pfade sind deterministisch und **ohne Wanduhr** (A0-20). `raw/` bleibt bei jedem Vorgang unverändert (AD-3).
4. **Kein Teilerfolg wird als fertige Mutation veröffentlicht (AD-17f; I/O-Matrix `MUTATION_TEILFOLGE`):** Sind mehrere Mutationen geplant und wird nur ein Teil ausgeführt, stellt der Rollback nach Pkt. 3 die Baseline wieder her — **Zwischenstände werden nie veröffentlicht** (Commit-Boundary = Mutations-Boundary, unverändert). Die **textuelle Failure-Benennung** (NFR-4) und die Phasen-Disziplin selbst bleiben unverändert.
5. **Validierungsphase — FAIL (AD-6; §6 Pkt. 3):** Melden die §6-Validierungen einen **VALIDATION_FAIL**, gilt der Run als gescheitert; es werden **keine weiteren Mutationen** durchgeführt, `raw/` bleibt unangetastet (AD-3), die Fehlerursache wird textuell benannt (NFR-4) und der Teilzustand gemäß **§5.3 Pkt. 3 / §6 Pkt. 3** zurückgerollt — Endzustand konsistent (Zustands-Restaurations-Invariante, Pkt. 3).
6. **Validierungsphase — SUCCESS mit Konsistenz-Commit (AD-6, A0-7-AC-3):** Sind **alle** geplanten Mutations-Validierungen erfolgreich durchlaufen (**VALIDATION_SUCCESS**, alle `wiki/`-Dateien SUCCESS nach §6), werden die Mutationen **als Ganzes committet** — erst nachdem der Diff-Selbsttest (§5.9 Pkt. 5) ohne Ghost-Diff abgeschlossen ist (AD-17f); die Konsistenz des Endzustands ist damit der **valide committete Zustand des Runs**. Der **§6-Pkt.-4-Ausführungs-Nachweis** wird geführt; **kein Wanduhr-Zeitstempel** steuert den Commit (A0-20).
7. **Keine eigene Workflow-Engine (D-3, AD-11, AD-6; I/O-Matrix `KEINE_EIGENE_ENGINE`):** Die Phasen-Trennung ist **logisch in einer Session** — Analyse, Änderungsplanung, Mutation und Validierung laufen in dem einen vorhandenen agentischen Run nacheinander ab; es wird **nichts Steuerndes gebaut** (kein neuer Prozess, kein Server, kein MCP, kein Standalone-Compiler/eigene LLM-Runtime, D-3/AD-11). Die Erhaltungs-Invariante (§5.9 Pkt. 5) und der Diff-Selbsttest gelten **unverändert**; der Determinismus-Vertrag (AD-17h/A0-19) gilt für Plan-Inhalt, Phasen-Reihenfolge, Rollback-Trigger und Post-Zustand — **mit dem dokumentierten `generated.at`-Wanduhr-Gap** (§5.10 Pkt. 8): zwei unabhängige Runs desselben Git-States können im Post-Zustand in `generated.at` abweichen (A0-20-Konvention); die übrigen Post-Zustands-Teile sind byte-identisch. Die **Behandlung des Gaps im Bundle-State-Vergleich** (Ausnahme-Menge, Abweichungs-Klassifikation) definiert **§5.14** (Determinismus-Vertrag & Agent-Instruktions-Validator, Story 3.8) — der Home-Verweis der A0-20-Konvention ist damit auf die §5.14-Definition verlagert.
## 5.14 Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)
Diese Sektion ist der **einzige Instruktions-Ort der geschlossenen Determinismus-Bestätigungs-Mechanik** (D-3, Story 3.8; AD-17h/FT-10/A0-19; §3.2/§5.9/§5.10/§5.11/§5.12/§5.13-Rückverweise verankern den Vertrag an vielen Stellen — hier wird seine **Bestätigung** geschlossen instruiert): Über denselben committeten Git-State + dasselbe Eingabeset erzeugen **zwei unabhängige Runs denselben Bundle-State**; eine Abweichung ist ein **Fehler der AD-16-Klassifikation**, kein Rauschen (textuell benannt, NFR-4). Das Enforcement lebt im MVP als **Agent-Instruktions-Validator (D-3, Q-6)** — ein Producer führt die Instruktion **zweimal über demselben committeten Git-State in einer Session** aus und vergleicht die Bundle-States; **kein** dedizierter Validator-Prozess, **keine** extra Runtime, **keine** eigene Workflow-Engine (AD-6, AD-11; D-3 — die Phasen-Trennung des §5.13 bleibt logisch in einer Session). Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`adapters/`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone/Schema-/Format-/Frontmatter-Key (D-3). Die **§0-Phasen-Ablaufstruktur (0)(5)**, die **§5.13-Phasen-Disziplin** und **§6/§6.5** bleiben **textuell unverändert** (Fugen-Identität; diese Sektion referenziert sie Wortlaut-unverändert). Der `generated.at`-**Wanduhr-Gap selbst bleibt verhaltens-seitig unverändert** (A0-20-Konvention, §5.9 Pkt. 2, §5.10 Pkt. 8) — §5.14 definiert **nur seine Behandlung im Bundle-State-Vergleich** (Ausnahme-Menge); ein Wechsel des `generated.at`-Verhaltens (z. B. Ableitung aus dem Git-State statt Wanduhr) wäre **Ask-First**.
1. **Bundle-State-Definition (deterministische Projektion des committeten Git-States):** Der **Bundle-State** eines Runs ist die vollständige, **deterministisch aus dem committeten Git-State ableitbare** Zustandsmenge des Runs — (a) der **committete Baum** (Datei-Gesamtheit unter `wiki/`/`raw/` gg. die Baseline, §5.9 Pkt. 6 R-1: mutierte/neu angelegte `wiki/`-Pfade, `log.md`-Einträge, nachgeführte `index.md`-Links, `raw/`-Zuwächse als Input), (b) die **Plan-/Kandidaten-/Reihenfolge-Outputs** (§3.2-Candidate-Liste in Zuwachs-Sicht-Ordnung, §5.13-P2-Block-Befund, §5.9-Vorprüfungen) und (c) die **Ausführungs-Entscheidungen** (§5.9-Form-Wahl, §5.10-Konsolidierung/Form-Zuordnung, §5.10-Pkt.-8-Reconcile-Orphan-Befund). **`generated.at` (und ggf. `verified[].at`) ist eine benannte Ausnahme** der Bundle-State-Projektion: es ist der **einzige** Projektions-Bestandteil, der zwischen zwei unabhängigen Runs desselben Git-States abweichen darf (A0-20-Konvention; §5.14-Pkt.-3-Ausnahme-Menge). Gleicher Git-State + gleiche Eingabemenge ⇒ **identischer Bundle-State bis auf die benannte `at`-Ausnahme** (FT-10, AD-17h, AC-1).
2. **Zwei-Run-Bestätigungs-Mechanik (agent-Instruktions-basiert, D-3/Q-6; re-executierbar):** Der Producer **führt die Instruktion zweimal über demselben committeten Git-State aus** (in einer Session; die zweite Ausführung startet aus dem **unveränderten Baseline-Commit-Baum** — Isolation pro Lauf, §5.13-Pkt.-3-Zustands-Restaurations-Invariante) und **vergleicht die Bundle-States**:
- **Vergleichs-Operandum = committeter Baum:** Vergleichbar ist der aus dem committeten Git-State ableitbare Bundle-State (Pkt. 1); der Vergleich nutzt re-executierbare, deterministische Formeln über denselben Baum — diff- und hashbasiert (z. B. `git diff --name-only <Baseline-Commit> -- wiki/`, pro-Pfad-SHA-256 der mutierten `wiki/`-Inhalte, Plan-/Kandidatenlisten-Textvergleich). Den **Zwei-Run-Abgleich** stellen die Formeln über den **Vergleich der beiden Run-Ausgänge gegeneinander** sicher — nicht nur je Lauf gegen die Baseline (z. B. `git diff <Run-A-Commit> <Run-B-Commit> -- wiki/` ohne die maskierte `at`-Zeile bzw. direkter Bundle-State-Hash-Textvergleich der zwei Läufe, Pkt. 1); ein Quervergleich fehlt, wenn jeder Lauf nur seinem eigenen Baseline-Diff gegenüber geprüft wird und keine eigene Abweichung zwischen den beiden Runs erkannt wird. **Keine Wanduhr/`now`-Zeit** steuert den Vergleich (A0-20; die Ausnahme-Menge definiert die einzig zulässige Differenz, Pkt. 3).
- **Identische Outputs:** zwei unabhängige Runs desselben committeten Git-States + desselben Eingabesets liefern **identische Plan-/Kandidaten-/Reihenfolge-Outputs** (§3.2-Candidate-Liste, §5.13-P2-Block, `sources`-Lexikografie, Konsolidierung/Form-Zuordnung) und **byte-identische mutierte Bundle-Bestandteile** — bis auf die `at`-Ausnahme. Die **Sandbox-Demonstration** (Szenario DET-2, `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh`) belegt die Identität **nicht-vakuum** (echte Content-Hashes, nicht nur Vorhandensein; AD-17h/A0-19, AC-3).
- **Mechanische Bestätigung (Agent-Instruktions-Validator):** Die Bestätigung ist der **textuell festgehaltene, re-executierbare Vergleich der zwei Ausführungen** (log.md-Nachweis je Run mit `<Baseline-Commit>`, D-2; der Befund „identisch bis auf `at`" wird textuell benannt). **Kein neuer Prozess/Server/MCP, kein Standalone** (D-3/AD-11): der Validator ist die **Instruktion selbst**, von einem Agenten einmal ausgeführt und durch eine zweite Ausführung bestätigt.
3. **Ausnahme-Menge des Bundle-State-Vergleichs (vollständig, deterministisch):** Der Bundle-State-Vergleich zweier unabhängiger Runs lässt **genau eine** benannte Differenz zu: den **`generated.at`-Wanduhr-Gap** (A0-20-Konvention, §5.9 Pkt. 2, §5.10 Pkt. 8 — ein Wanduhr-Zeitstempel im `generated.at`-Feld bzw. `verified[].at`, der zwischen zwei Runs desselben Git-States abweichen darf). Der Gap ist **dokumentiert, kein stiller Ausschluss**: die Ausnahme bezieht sich **ausschließlich** auf den `at`-Feld-Wert; **alle übrigen Bundle-State-Bestandteile** (Pkt. 1 (a)/(b)/(c)) sind zwischen zwei Runs desselben Git-States **byte-identisch**. Jede **andere** Differenz liegt **außerhalb** der Ausnahme-Menge.
4. **Abweichungs-Klassifikation (AD-16-Klassifikationsdefekt, kein Rauschen):** Eine bei der Zwei-Run-Bestätigung festgestellte Differenz außerhalb der benannten Ausnahme-Menge (Pkt. 3) **ist ein Fehler im AD-16-Klassifikations-Mechanismus** (FT-10/AD-17h-AC): der Run wird **textuell benannt** (NFR-4) und **korrigiert bzw. rollt zurück** (Zustands-Restaurations-Invariante, §5.13 Pkt. 3) — die Abweichung wird **nicht** als akzeptables Rauschen oder Umgebungs-Streuung toleriert. Erzeugt ein deterministischer Bestandteil (Pkt. 1) in zwei Runs unterschiedliche Werte, ist die Ursache in der Ausführungs-Instruktion zu suchen und dort zu beheben, **bevor** der Vertrag als bestätigt gilt. Die textuelle Benennung (NFR-4) ist verpflichtend; der Befund wird (ggf. als korrigierter zweiter Lauf) erneut bestätigt.
5. **Normalisierungs-/Match-/Orphan-Schließung (§3.2/§5.10-Verankerung):** Die bekannten deterministischen Lücken der Relevanz-/Synthese-Erhebung sind mit dieser Sektion **geschlossen** (als deterministische Regel-Ergänzungen, ohne Umbruch des bestehenden Wortlauts): die **Em-Dash-Kollaps-Klasse**, die **Kollaps-Reichweite** und der **Match-Scope der Stufe a** gelten wie in **§3.2-Pkt.-1b** (geschlossene Determinismus-Lücken) definiert; die **Orphan-Politik** (§5.10 Pkt. 8) gilt als deterministische **Reconcile-Orphan-Regel**. Damit sind die §3.2-/§5.10-Erhebungen über den committeten Zustand **vollständig pinbar** — ein Bestandteil, der zwischen zwei Runs differiert (Ziel-Pfad via §3.2/§5.7, Candidate-Liste, Form-Zuordnung, Verwaist-Befund), ist ein AD-16-Klassifikationsdefekt (Pkt. 4), keine offene Frage.
## 6. Validieren (mechanische Bestätigung)
1. Nach Abschluss aller Mutationen wird das gesamte Bundle gemäß `schema/validator.md` geprüft (§3 14 Punkte je Datei + §6-Fachprüfungen; Verdikt-Grammatik §5).
@@ -328,15 +422,16 @@ Interpretations-Hinweis: Die „✗"-Zeilen zeigen die deterministische Fehlerur
## 7. Selbstbegrenzung (Scope der Instruktion)
Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in Areas gemäß §5.7 sowie das inkrementelle Update bestehender Concepts gemäß §3 + §5.9** begrenzt. Folgendes verbleibt in anderen Stories und wird hier **nicht** vorweggenommen:
Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in Areas gemäß §5.7, das inkrementelle Update bestehender Concepts gemäß §3 + §5.9, die Synthese mehrerer `raw/`-Quellen zu einer gemeinsamen Wissensrepräsentation gemäß §5.10 sowie die Leasing-/Dirty-Tree-Koordination für konkurrierende Producer gemäß §5.11** begrenzt. Folgendes verbleibt in anderen Stories und wird hier **nicht** vorweggenommen:
- **Claim-granulare Provenienz** je belegter Aussage (Inline-`raw/`-Verweise, Kontext-Marker) — in **§5.5** dieser Instruktion verankert (Story 2.2; AD-4a, A0-3). Keine neue §7-Klasse, kein Standalone, keine Vertragsänderung.
- **Deterministische Area-Zuordnung & Concept-Hierarchie** (Anlage von `wiki/<area>/index.md` + `wiki/<area>/<concept>.md`) — in **§5.7** dieser Instruktion verankert (Story 2.4; AD-7c, A0-10, A0-8, AD-13). Keine neue §7-Klasse, kein Validator-Change.
- **Progressive Discovery über `index.md`** (Navigation, Area-Indizes) — in **§5.8** dieser Instruktion verankert (Story 2.5; AD-9, FR-11, AD-13, NFR-3). **Suche** bleibt konsumenten-/extern-seitig (Consumer-grep über `wiki/`, §5.8 Pkt. 4 — kein Bundle-/Instruktions-Thema mehr). Keine neue §7-Klasse, kein Schema-/Validator-Change.
- **Eine genau-eine-Linkform** (file-relativ mit `.md`-Endung, in Areas `../`-fähig) — in **§5.6** dieser Instruktion gepinnt (Story 2.3; AD-7b, A0-9, FR-10; Auflösungsmodell §5.7 Pkt. 4) — der Punkt-11-Check des Validators akzeptiert bis auf Weiteres beide Schreibweisen (strukturell unverändert, Story-2.2-Präzedenz).
- **Synthese über mehrere Concepts** (mehrere Sources → eine gemeinsame Wissensrepräsentation mit gemischter, claim-granularer Provenienz) **Epic 3, Story 3.4** (AD-4, FR-7). Die Verankerung des inkrementellen Datenflusses (Erweitern/Präzisieren/Korrigieren einzelner bestehender Concepts) bleibt **§3 + §5.9** überlassen und ist dort bereits verankert (Story 3.1) — sie ist damit aus diesem Vorbehalt entlassen.
- **Leasing / Dirty-Tree-Schutz** für konkurrierende Producer **Story 3.5/3.6** (AD-17a..f, A0-12..A0-16); die bestehende Commit-Boundary = Mutations-Boundary-Regel (§0/§5.3, AD-17f) bleibt in v1 bestehender Schutz.
- **Relevanzbestimmung** (feinkörniger Relevanz-Findungsmechanismus der Kandidatenerhebung) — in **§3.2** dieser Instruktion verankert (Story 3.2; Term-Ziehverfahren + Kanonisierungs-Resolver `schema/canonical-terms.md`, drei Erhebungs-Stufen grep/ripgrep + `index.md`-Traversal + Link-Following mit besuchter Menge, `log.md`-Exklusion, Candidate-Liste als relative OKF-Pfade ohne `.md`, Determinismus-Vertrag AD-17h/A0-19). Keine neue §7-Klasse, kein Schema-/Validator-Change; die Em-Dash-`—`-Varianten-Lücke ist als Determinismus-Frage an Story 3.8 übergeben.
- **Synthese über mehrere Sources** (mehrere `raw/`-Quellen → eine gemeinsame Wissensrepräsentation mit gemischter, claim-granularer Provenienz) **in §5.10** dieser Instruktion verankert (Story 3.4; AD-4, FR-7). Die Verankerung des inkrementellen Datenflusses (Erweitern/Präzisieren/Korrigieren einzelner bestehender Concepts) bleibt **§3 + §5.9** überlassen und ist dort bereits verankert (Story 3.1). Beide sind damit aus diesem Vorbehalt entlassen.
- **Leasing / Dirty-Tree-Schutz** für konkurrierende Producer **in §5.11 dieser Instruktion verankert** (Story 3.5; AD-17a..f, A0-12..A0-16; die Enum enthält AD-17d/A0-15 nur als **Norm-Rückverweis** — die §5.11-Auflösung selbst schließt Staleness aus und delegiert an Story 3.6, Pkt. 7-Seam-Kriterium): Lease-Akquise auf `lease/<area>/<id>`-Branches mit Lockfile und Merge-Base-Disziplin, Root-Scope-Lease inkl. `log.md`/`index.md`, Dirty-Tree-Schutz mit Stash/Scratch-Zone und `log.md`-Dokumentation, compiler-vermittelter Merge als AD-16-Pfad, kein textueller Auto-Merge, Commit-Boundary = Mutations-Boundary, Lease-Freigabe (Release, deterministisch); die bestehende Commit-Boundary = Mutations-Boundary-Regel (§0/§5.3, AD-17f) bleibt unverändert bestehender Schutz. **Lease-Staleness/Recovery** (TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery; AD-17d, A0-15) — **in §5.12 dieser Instruktion verankert (Story 3.6)**.
- **Relevanzbestimmung** (feinkörniger Relevanz-Findungsmechanismus der Kandidatenerhebung) — in **§3.2** dieser Instruktion verankert (Story 3.2; Term-Ziehverfahren + Kanonisierungs-Resolver `schema/canonical-terms.md`, drei Erhebungs-Stufen grep/ripgrep + `index.md`-Traversal + Link-Following mit besuchter Menge, `log.md`-Exklusion, Candidate-Liste als relative OKF-Pfade ohne `.md`, Determinismus-Vertrag AD-17h/A0-19). Keine neue §7-Klasse, kein Schema-/Validator-Change; die Em-Dash-`—`-Varianten-Lücke ist als Determinismus-Frage an Story 3.8 übergeben. **Story-3.8-Auflösung:** die Determinismus-Frage ist in **§5.14** dieser Instruktion verankert (Story 3.8, Determinismus-Vertrag & Agent-Instruktions-Validator): die Em-Dash-Kollaps-Klasse, die Kollaps-Reichweite und der Match-Scope der Stufe a sind in **§3.2-Pkt.-1b** als geschlossene, deterministische Regel-Ergänzungen verankert; die Orphan-Politik ist in **§5.10 Pkt. 8** als deterministische Reconcile-Orphan-Regel präzisiert; die `generated.at`-Wanduhr-Gap-Ausnahme ist in §5.14-Pkt.-3 in die **Ausnahme-Menge** des Bundle-State-Vergleichs gefasst (A0-20-Konvention, Ask-First-geschützt). Keine offene Determinismus-Frage verbleibt (bestehende Story-Bullets unverändert).
- **Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand** (logische Phasen-Disziplin: Analyse → Änderungsplanung → Mutation → Validierung, AD-6/A0-7, AC-1/AC-2/AC-3/AC-4) — **in §5.13 dieser Instruktion verankert (Story 3.7; D-3):** Änderungsplanung als institutionalisierter §5.9-Pkt.-6-P2-Block (textuell festgehalten, Plan-Defizit = textuell benannte Abbruch-Kette vor der Mutation), Plan-Freeze = Veränderungs-Sperre an die §5.9-Pkt.-5-Ghost-Diff-Kopplung (erlaubte Pfad-Menge: Kandidatenliste Neu-Anlage `log.md` `index.md`), Zustands-Restaurations-Invariante (Post-Rollback-Diff gg. Baseline leer / Bundle == valide committet, §5.3-Pkt.-3-/§6-Pkt.-3-Rollback unverändert), kein Teilerfolg als fertige Mutation (AD-17f), keine eigene Workflow-Engine (logische Trennung in einer Session, kein Prozess/Server/MCP, D-3/AD-11). Keine neue §7-Klasse, kein Schema-/Validator-Change, keine Vertragsänderung.
- **Standalone-Compiler / eigene LLM-Runtime / MCP** → verboten in v1 (D-3, D-4, AD-11).
- **OKF-Dialekt / Schema-Erweiterung** → niemals (AD-1a; Vertrag §7 „abschließende Liste").
@@ -346,9 +441,9 @@ Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in A
- `schema/wiki-compiler.md` — autorisierter Vertrag (Story 1.3): §2 Bundleroot, §3.1–§3.7 Feldsubset & Formate, §5 `log.md`-Typ, §6 Index-Regel/Prädikate, §7 abschließende 14-Punkte-Liste, §8 Normreferenzen.
- `schema/validator.md` — Prüfgrundlage (Story 1.4, Revision 9): §3 14 Punkte, §4 Normalform (Reihenfolge §4.1, ISO-8601 §4.3), §5 Verdikt, §6 Fachprüfungen (EC-1 Existenz, EC-3 Kalender, EC-11 non-md; Punkt-11-Area-Lesart formalisiert).
- Architektur-Spine (raw/`architecture-spine`): AD-2/AD-3 (raw immutable), AD-4a (claim-granulare Provenienz, §5.5), AD-5 (inkrementelle Kompilation), AD-6 (Reason/Mutate-Trennung), AD-7a (Identität = OKF-Pfad ohne `.md`, §5.7), AD-7b (genau eine Linkform gepinnt, §5.6), AD-7c (deterministische Bereichszuordnung, §5.7), AD-7d (Renaming/Redirect-Pflicht — nicht in den ACs, Epic 3), AD-8 (Standard-Markdown-Links = Navigations-/Beziehungsschicht, §5.6), AD-9 (Progressive Discovery, §5.7/§5.8), AD-10 (agent-unabhängige Regeln), AD-11 (keine eigene Runtime), AD-13 (Retrieval gehört zu Consumers / Suche = Consumer-grep / keine Embedding-Bereichszuordnung, §5.7/§5.8; Relevanzbestimmung textuell-deterministisch, §3.2), AD-14 (Git liefert Historie, nicht Domain-State), AD-15 (Trust-Metadaten v1), AD-16 (Konflikte werden explizit bewahrt), AD-17a (nur veröffentlichte/committete Inhalte als Input), AD-17f (Commit-Boundary = Mutations-Boundary), AD-17h (Determinismus), D-3 (kein Standalone).
- Architektur-Spine (raw/`architecture-spine`): AD-2/AD-3 (raw immutable), AD-4a (claim-granulare Provenienz, §5.5), AD-5 (inkrementelle Kompilation), AD-6 (Reason/Mutate-Trennung, §5.13), AD-7a (Identität = OKF-Pfad ohne `.md`, §5.7), AD-7b (genau eine Linkform gepinnt, §5.6), AD-7c (deterministische Bereichszuordnung, §5.7), AD-7d (Renaming/Redirect-Pflicht — nicht in den ACs, Epic 3), AD-8 (Standard-Markdown-Links = Navigations-/Beziehungsschicht, §5.6), AD-9 (Progressive Discovery, §5.7/§5.8), AD-10 (agent-unabhängige Regeln), AD-11 (keine eigene Runtime), AD-13 (Retrieval gehört zu Consumers / Suche = Consumer-grep / keine Embedding-Bereichszuordnung, §5.7/§5.8; Relevanzbestimmung textuell-deterministisch, §3.2), AD-14 (Git liefert Historie, nicht Domain-State), AD-15 (Trust-Metadaten v1), AD-16 (Konflikte werden explizit bewahrt), AD-17a (nur veröffentlichte/committete Inhalte als Input), AD-17b (Root-Scope-Lease, §5.11), AD-17c (kein textueller Auto-Merge, compiler-vermittelter Merge, §5.11), AD-17d (Lease-Staleness, §5.12), AD-17e/f (Dirty-Tree-Schutz; Commit-Boundary = Mutations-Boundary, §5.11/§0/§5.3), AD-17g (Unentscheidbarkeit → menschliche Eskalation, §5.11), AD-17h (Determinismus, §5.14 — Zwei-Run-Bestätigung mit benannter `generated.at`-Wanduhr-Gap-Ausnahme, A0-20/§5.9-Pkt.-2/§5.10-Pkt.-8), D-3 (kein Standalone).
- PRD (raw/prd): FR-2 (Sources vs. Curated), FR-5 (Concept-Erzeugung), FR-9 (OKF-Konformität), FR-10 (Concepts miteinander verlinken, §5.6), FR-11 (Progressive Discovery siehe PRD-§4.3-Zeile unten — Discovery-Pfad/gewurzelte Erreichbarkeit, §5.8), FR-16 (Consumer-Unabhängigkeit), NFR-3 (Agent Readability — Standard-Dateioperationen/grep über `wiki/`, §5.8 Pkt. 4), A-4 (nur lokale Sources).
- Epics (raw/epics): Story-2.1-Ziel und -Abgrenzung zu Story 2.22.5; A0-3 (Kontext-Marker-Wortlaut, §5.5), A0-8 (Concept-Identität/Normalisierung, §5.7), A0-9 (eine erlaubte Linkform, §5.6), A0-10 (deterministische Bereichszuordnung, §5.7), A0-13 (Lease-Root-Scope); FR-6/FR-12/FR-14, A0-6/A0-7/A0-11/A0-18 (Belege der nachkonformierten Concept-Bodies); **A0-6 (inkrementeller Datenfluss Interpret → Reconcile → Synthesize → Update, §0/§3/§5.9), FR-6 (Aktualisierung statt neuer Dateien, §3/§5.9), FR-12 (unverändertes Wissen bleibt erhalten, §5.9)**, **A0-18 (Deterministische Relevanzbestimmung — grep/ripgrep, `index.md`-Traversal, Link-Following, keine Embedding-/Vector-Infrastruktur; §3.2, AD-13, PRD OQ-3)**, **A0-19 (Determinismus-Vertrag: gleicher Git-State + gleiche Eingabemenge → gleicher Bundle-State / gleiche Candidate-Liste in gleicher Reihenfolge; §3.2, AD-17h)**.
- Epics (raw/epics): Story-2.1-Ziel und -Abgrenzung zu Story 2.22.5; A0-3 (Kontext-Marker-Wortlaut, §5.5), A0-8 (Concept-Identität/Normalisierung, §5.7), A0-9 (eine erlaubte Linkform, §5.6), A0-10 (deterministische Bereichszuordnung, §5.7), A0-13 (Lease-Root-Scope, §5.11); FR-6/FR-12/FR-14, A0-6/A0-7/A0-11/A0-18 (Belege der nachkonformierten Concept-Bodies); **A0-6 (inkrementeller Datenfluss Interpret → Reconcile → Synthesize → Update, §0/§3/§5.9), FR-6 (Aktualisierung statt neuer Dateien, §3/§5.9), FR-12 (unverändertes Wissen bleibt erhalten, §5.9)**, **A0-12 (Leasing-Modell — `lease/<area>/<id>`-Branches, Merge-Base-Disziplin, Lockfile semantisch identisch in jedem Adapter, §5.11), A0-13 (Root-Scope-Lease inkl. `log.md`/`index.md`/Root-Dateien, §5.11), A0-14 (kein textueller Auto-Merge; compiler-vermittelter Merge über AD-16, §5.11), A0-15 (Lease-Staleness, §5.12), A0-16 (Dirty-Tree-Schutz: Pre-Mutation-Prüfung, Stash/Scratch-Zone, `log.md`-Dokumentation, §5.11)**, **A0-18 (Deterministische Relevanzbestimmung — grep/ripgrep, `index.md`-Traversal, Link-Following, keine Embedding-/Vector-Infrastruktur; §3.2, AD-13, PRD OQ-3)**, **A0-19 (Determinismus-Vertrag: gleicher Git-State + gleiche Eingabemenge → gleicher Bundle-State / gleiche Candidate-Liste in gleicher Reihenfolge; §3.2/§5.11, AD-17h)**.
- PRD §4.3 (FR-11 — progressive Discovery, §5.7 Pkt. 5/§5.8) und §8.2/§8.3 (Canonical State; Separation of Concerns), **PRD OQ-3 („Compilation Scope" — wie findet der Compiler relevante vorhandene Concepts; textuell-deterministische Relevanzbestimmung, §3.2; AD-13/A0-18; Muster `raw/architecture-spine/…md` §8.3)**.
**Revisionslog:**
@@ -373,3 +468,9 @@ Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in A
- **Revision 2.5 (2026-08-19, Story 3.2):** Neue Sektion **§3.2 „Relevanzbestimmung (Story 3.2)"** eingefügt (nach §3, vor §4) — die **verbindliche Ausformulierung der §3-Pkt.-2-Kandidatenerhebung** („Erhebung nach §3.2"): (1) **Term-Ziehverfahren** deterministisch (bedeutungstragende Token-Folgen nach §2-Interpretation; Normalisierung lowercasing + `[-_ ]``-`-Kollaps; **kanonischer Schreibweisen-Resolver `schema/canonical-terms.md`** — committete, append-only Registry (canon. Form + erlaubte Varianten), damit Bestandteil des Git-States und deterministisch pinbar; genau eine canon. Form je Semantik A0-18; kein stiller Ausschluss nicht auflösbarer Varianten — Verwendung wie notiert; mehrere Terme je Einheit → Vereinigung), (2) **dreistufige term-übergreifende Erhebung über `wiki/`** — (a) `rg -l '<term>' --exclude=log.md wiki/` (grep-Äquivalent `grep -rl … --exclude=log.md`; `log.md` **strukturell** exkludiert, Candidate-Liste auf Concept-Pfade definiert), (b) `index.md`-Traversal (gewurzelte Erreichbarkeit Root → Area → Concept, §5.8; TRAVERSAL_REACH_ONLY; fehlende Bundleroot → Run-FAIL V-1), (c) Link-Following mit **besuchter Menge** (file-relativ auflösen, §5.7 Pkt. 4; Zyklen enden, LINK_FOLLOWING_ZYKLUS); (3) **Candidate-Liste + Determinismus-Vertrag (AD-17h/A0-19):** relative OKF-Pfade ohne `.md` (Strip `wiki/`-Präfix + `.md`-Suffix), **Zuwachs-Sicht-Ordnung** mit Reihenfolge auch für Stufe-b/c (nach Stufe-a; lexikografisch als deterministischer Tie-Break), keine Duplikate (besuchte Menge), NO_MATCH → leere Candidate-Liste → UNTOUCHED_CONCEPT (Story-3.1-Pfad); Determinismus-Selbsttest (Membership + Zwei-Run-Identität) in Spec-Verification und `wiki/log.md` belegt. **§3 Pkt. 2:** Story-3.2-Vorbehalt **aufgelöst** — Pkt. 2 bleibt Kern-Anker, die Erhebung zeigt auf die neue Sektion („Erhebung nach §3.2"), `rg -l '<term>' --exclude=log.md wiki/` als Stufe-a-Form genannt. **§7:** Story-3.2-Vorbehalt **aufgelöst** (Relevanzbestimmung in §3.2 verankert; verbleibende 3.x-Themen: Synthese → Story 3.4, Leasing/Dirty-Tree → Story 3.5/3.6). **§8:** Normreferenzen um **AD-13** (bereits gelistet, §3.2-angeankert), **A0-18** (Deterministische Relevanzbestimmung, §3.2) und **A0-19** (Determinismus-Vertrag, §3.2 — war im Ist-§8 noch nicht gelistet, wird als Determinismus-Referenz ergänzt) sowie **PRD OQ-3** (Compilation Scope, §3.2) ergänzt; **`schema/canonical-terms.md`** als nebengeordnete committete Resolver-Registry referenziert (kein `schema/`-Root-Change; gleiche read-only-Hierarchie, append-only). Em-Dash-`—`-Varianten-Lücke als offene Determinismus-Frage an **Story 3.8** übergeben (nicht stillschweigend ergänzt). **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; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip` `backlog`**`in-progress`**. `wiki/log.md`-Eintrag (append-only, bestehende Bullets unverändert), `deferred-work.md`-epic-3-context-Eintrag → aufgegriffen (append-only), Determinismus-Selbsttest + Validator-Lauf (7/7 SUCCESS) siehe `wiki/log.md`-Nachweis.
- **Revision 2.4.2 (2026-08-19, Story 3.1, bmad-code-review Loop 2, 4 Layer; Nutzer-Entscheidungen D-1/D-2/D-3/D-4 = 1/1/1/1):** (1) **Diff-Selbsttest (Pkt. 5) operationalisiert + Blind-Spots geschlossen (D-1):** Probe erweitert auf `git diff --name-only <Baseline-Commit> -- wiki/` **plus** `git status --porcelain -- wiki/` (erfasst ungetrackte neue Dateien `??` — die Duplikat-Kontrolle „keine neue Datei" braucht diese Sicht; `git diff` allein ist blind für Untracked); **Probe-Zeitpunkt fixiert vor dem Commit** (am Ende des P2-Blocks, nach den Mutationen) — nach dem Commit wäre die Probe vacuous (leere Ausgabe, AD-17f); erlaubte Teilmenge-Menge um **Neu-Anlage-Zielpfade** (§5.1/§5.7) ergänzt — Misch-Runs (Neu-Anlage + Update im selben Run) markieren neu angelegte Pfade nicht fälschlich als Ghost-Diff; **Pfad-Normalisierung** definiert (Strip `wiki/`-Präfix + `.md`-Suffix vor dem Teilmenge-Vergleich, da Kandidaten/Ziel-Pfade als relative OKF-Pfade *ohne* `.md` definiert sind); **Rollback-Mechanik** für den Ghost-Diff deterministisch benannt (modifizierte Pfade via `git checkout -- <wiki/pfad>`, ungetrackte neue Dateien gelöscht, Index via `git checkout -- <index>`; §5.3/§6.3-Teilzustand-Rollback greift daneben unverändert). (2) **R-1-Baseline deterministisch + Abweichungsregel (D-2):** `<Baseline-Commit>` (HEAD der vorherigen Mutations-Boundary, AD-17f) wird vom Producer **im `wiki/log.md`-Run-Eintrag notiert** (voller SHA) — deterministisch auflösbar ohne Domain-State-Annahme an Git (AD-14); **Diskrepanz-Regel**: widersprechen `git diff`-Befund und SHA-256-Record derselben Datei, **gewinnt `git diff`** (Commit-Boundary-Prinzip), SHA-256 bleibt Sekundär-Fingerprint; **Fallback**: ohne vorherige Mutations-Boundary gelten alle `raw/`-Dateien als Zuwachs; Rev-2.4.1-Claim „derselbe Baseline-Commit wie Pkt. 5" **korrigiert** (die Pkt.-5-Probe trägt das Baseline-Commit-Argument jetzt explizit). (3) **INPUT_UNCOMMITTED-Abbruch (D-3) + Anker-Divergenz:** neuer P2-Check-Block-Element (1) **Input-Zustand** — Working-Copy von `raw/`/`wiki/` gegen HEAD prüfen, bei Abweichung **benannter Abbruch „published/committed Input erforderlich"** vor Interpretation und vor jeder Mutation (AD-17a, I/O-Matrix-`INPUT_UNCOMMITTED` — zuvor nur Referenz auf §1 Pkt. 1, keine Zustandsprüfung/Abbruchmeldung); die **Spec↔Anker-Divergenz** (frozen Spec zitiert 3× „§1.1", §1 ist eine nummerierte Liste Pkt. 14 ohne §1.1-Label; Rev-2.4.1 korrigierte nur die Anker *in* compiler.md) wird hier als dokumentierte Fußnote gesichert — die Spec bleibt frozen (nur per menschlicher Renegotiation änderbar). (4) **Sandbox-Edge-Test-Nachweis (D-4):** die fünf I/O-Matrix-Szenarien (HAPPY_PATH_UPDATE, UNTOUCHED_CONCEPT, CONCEPT_COLLISION_BESTEHEND, CHANGE_DETECTION, PRE_RUN_RECONCILE) + die D-3-Abbruch-Kontrolle sind als re-executierbare Sandbox-Skripte mit **konkreten deterministischen Ausgaben** in der Spec-`## Verification`-Sektion (Sandbox-Beleg) verankert — die `wiki/log.md`- und `deferred-work.md`-Verweise („in der Spec-Verification enthalten") auflösbar. (5) **Instruktions-Präzisierungen:** P2-Block-**Zeitpunkt** (nach Reconcile, am Anfang der Mutationsphase; Diff-Probe am Block-Ende) — löst den Widerspruch „einmal an der Spitze erhoben" vs. „Kandidatenliste entsteht erst im Reconcile"; **No-Op-Kandidat**-Regel (Pkt. 2: Pfad, der die Evidenz bereits vollständig enthält → keine Mutation/kein `at`-Bump/kein `log.md`-Eintrag, byte-identisch — die engere Auslegung); **Mehrfach-Treffer-Reihenfolge** für *alle* Einheiten definiert (Zuwachs-Sicht-Ordnung; lexicografisch nur als Tie-Break bei identischem Ort — AD-17h); **Update-Pfad-Rollback** in §5.3 Pkt. 3 (modifizierte Pfade aus Baseline-Commit wiederherstellen); **`generated.at`↔AD-17h-Gap** explizit als offener Punkt mit Home Story 3.8 benannt (Wanduhr-`at` erzeugt bei gleichem Input unterschiedliche Bundle-States; Konvention bleibt bis dahin bindend, Wechsel = Ask-First); **Term-Ableitung** (Pkt. 2 (a)) als §2-Interpretation abgegrenzt — die Erhebung *mit festem Term* ist textuell-deterministisch, der Term-Mechanismus (Kanonisierung/Synonyme) Story 3.2; **Link-Following** mit besuchter Menge (keine Schleife bei zyklischen Links); **Worked Example (Pkt. 7) auf den realen Ist-Baum korrigiert** (`rg -l 'FR-12' wiki/` trifft `wiki/knowledge-kompilation-inkrementell.md`, **nicht** `wissensarchitektur/source-material.md` — Rev-2.4.1-Beispiel enthielt den Term nicht); stale-Anker `§3.2-Kollisionsprüfung` (Pkt. 3-Zuordnung) und `§3-Voraussetzungsprüfung` (P2-Block) sowie §5.8-Pkt.-3-Zeiger („Pkt. 1" → „Pkt. 2") nachgeführt. **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; Commit-Boundary-Regel unverändert.
- **Revision 2.7 (2026-08-19, Story 3.2, Step-04-Review Loop 3, 4 Layer; Nutzer-Entscheidungen D-1/D-2/D-3/D-4/D-5 = 1/1/1/1/1, jeweils empfohlene Option; + 2 Patches; D-3-Instruktions-Patch, kein neuer Inhalt):** (1) **§3.2-Pkt.-3b-Ordnung auf reine Lexikografie gehoben (D-4):** Stufe-a-Treffer waren „in der Reihenfolge des ziehenden Terms, dann lexikografisch als deterministischer Tie-Break" — die Term-Ordnung selbst war für Mehrfach-Terme (Pkt. 1c) nirgends festgelegt, zwei rechtmäßige Term-Ordnungen erzeugten zwei unterschiedliche Listen (AD-17h-Lücke). Jetzt: **innerhalb jeder Stufe rein lexikografisch aufsteigend (LC_ALL=C)**; die Term-Verarbeitungsreihenfolge dient nur der Interpretation/Erhebung, **nicht** der Listen-Ordnung — dieselbe Treffermenge → identische Liste. (2) **`schema/canonical-terms.md`-Lookup-Semantik vervollständigt (D-5):** 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`-Handoff / Ask-First, analog zur Umbenennungs-Regel). (3) **Statuskette `in-progress → done` dokumentiert (D-1):** der Review-Loop-Abschluss-Flip `in-progress → done` (Sprint-Sync-Konvention, Story-3.1-Präzedenz) war in keinem `wiki/log.md`-Eintrag als eigener Schritt belegt — nachgeführt als neuer oberster `wiki/log.md`-Bullet (append-only; die gefrorene Always-Klausel `→ in-progress` beschreibt den Implementierungsstand, der Review-Abschluss `done` ist der übliche Folgezustand). (4) **Mini-Sandbox um T5/T6/T7 erweitert + T1/T4-Asssertionen + T2-Kommentar-Korrektur (D-2 + Patch P-1):** T5 `LINK_FOLLOWING_ZYKLUS` (A→B→A-Links; besuchte Menge → endliche, doppelungsfreie Liste), T6 `TRAVERSAL_REACH_ONLY` (Term nur in Area-`index.md` → gewurzelte Concept-Pfade als Kandidaten; `index.md` selbst ist kein Concept-Kandidat), T7 `TERM_ABLEITUNG_SYNONYM` (Registry-Test-Doppel: Schreibvarianten → canonische Form via Lookup auf normalisierter Form; Negativ-Fall: nicht auflösbar → wie notiert, kein stiller Ausschluss); T1/T4 tragen jetzt harte Pass/Fail-Asssertionen (`exit 1` bei Abweichung) — die exakte Candidate-Liste wird erzwungen, nicht nur ausgegeben; der T2-Kommentar zu `index` korrigiert (Stufe-a-Treffer auf `wiki/index.md` löst Stufe b aus; `index.md` ist kein Concept-Kandidat). (5) **Selbsttest-Beleg (b) gegen den realen Sandbox-Ist-Baum re-executiert (D-3):** der Rev-2.5-Log-Beleg (b) beschrieb einen nicht-committierten Baum (Term `quanten-protocol`, `sub/beta.md`, SHA-256 `159092bb…`) — Stale-Evidenz-Falle (B1-Präzedenz). Der Nachweis wird jetzt gegen die echte `run-sandbox.sh`-Ausgabe (Term `deterministische-relevanz-bestimmung`, root-level `alpha`/`beta`/`gamma`) neu belegt; der Rev-2.5-Eintrag bleibt historisch unverändert, die Korrektur steht als neuer `wiki/log.md`-Bullet (append-only). (6) **Typos im normativen Text korrigiert (Patch P-2):** `Determinsmus``Determinismus` (§8-Referenzen + Log-/Defer-Belege), `§3.2-beankert``§3.2-angeankert`, `Membrum``Mitglied` (Spec-Design-Notes), `Konventionelle Determinismus-Lücke``Bekannte Determinismus-Lücke` (Defer-Beleg; `compiler.md:53` sagt „Bekannte"), `Resovierung``Auflösung` (Spec-Change-Log/-Verification). **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; Commit-Boundary-Regel unverändert. (Revisionslog-Reihenfolge-Anomalie 2.6/2.5 bleibt als dokumentierter Defer bestehen — nicht in dieser Runde umgeordnet.)
- **Revision 2.8 (2026-08-19, Story 3.3):** §5.9-Update-Formen **operationell ausformuliert** (dieselbe Evidenz + formale Struktur → identisches Update-Ergebnis, AD-17h/A0-19): (1) **Pkt. 2 — Präzisierungsebene je Update-Form** (als Sub-Bullet an die Commit-Boundary-Regel angefügt; der frozen Story-3.1-Regeltext bleibt textuell **unverändert** als normative Basis, die operationellen Regeln sind die Ausführungs-Ebene darunter — keine Re-Negotiation): je Form **(a) Abgrenzungskriterium** (aus der committeten Evidenz: neue belegte Aussage → Erweitern; Schärfung einer bestehenden Formulierung/Abgrenzung ohne Ersatz → Präzisieren; Ersetzung einer fehlerhaften/überholten → Korrigieren; keines davon → No-Op, engere Auslegung), **(b) Struktur-Erhaltungsregel** (das Geschützte — Frontmatter-Subset nur als `sources`-Zuwachs um echten neuen Beleg + `generated.at`-Bump; bestehende belegte Aussagen nicht umgeschrieben ohne dass Präzisieren/Korrigieren greift; §5.5-Inline-Verweise gültig soweit Beleg Body-Bestand; §5.6-Linkform unverändert, keine neuen Links außer bei echten Beziehungen) und **(c) Textgenauigkeits-Rahmen für Präzisieren** (Schärfen an der Aussage, kein Satz-Umbau, kein Neuschreiben ohne Ersetzungsbeleg). (2) **Pkt. 5 — Erhaltungs-Invariante um den Struktur-Erhaltungs-Bezug ergänzt:** die Diff-Probe verifiziert zwei Ebenen — die Pfad-Mengen-Ebene (bestehender Teilmenge-Vergleich) und die **Struktur-Ebene je berührtem Pfad** (pro betroffenem Concept-Pfad über die Pkt.-2-Regeln geprüft: Frontmatter-Subset, keine Umschreibung belegter Aussagen außerhalb der Formen, Inline-Verweise, Linkform — Verstöße textuell benannt (NFR-4), vor dem Commit zu beheben, sonst Ghost-Diff mit Rollback). (3) **Pkt. 6 — P2-Check-Block um den Struktur-Erhaltungs-Check erweitert** (Element (6), zusätzliches textuelles Element: keine unbefugten Keys — Vertrag §3.3/§3.4-Subset, keine stille Löschung — AD-16/„Korrigieren"-Form, Links unverändert/keine neuen ohne echte Beziehung — §5.6-Pin; Verstöße textuell benannt (NFR-4) und vor dem Commit behoben). (4) **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; Commit-Boundary-Regel unverändert; kein Leasing-Scope (AC-4 → Story 3.5/3.6, AD-17.3-Hinweis unverändert in §7). **`sprint-status.yaml`:** Key `3-3-bestehende-concepts-erweitern-präzisieren-korrigieren` bleibt **`in-progress`** (Review-Abschluss `done` erfolgt gemäß Workflow-Konvention durch den Review-Schritt). Sandbox-Nachweis und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.3, Revision 2.8).
- **Revision 2.9 (2026-08-19, Story 3.4):** Neue Sektion **§5.10 „Synthese aus mehreren Sources (Story 3.4)"** eingefügt (nach §5.9, vor §6) — die **verbindliche Verankerung der Synthese-Dimension** (AD-4, FR-7): (1) **Synthese-Stimulus** (≥ 2 belegende `raw/`-Quellen desselben Themas per §2-Interpretation → eine Synthese-Einheit; einzelne Quelle bleibt auf §5.9-/§3.2-Pfad), (2) **Ein-Ziel-Repräsentation (§5.7-Routing; FR-7 AC-1)** — ein Synthese-Concept über einen Ziel-Pfad, `sources`-Liste ≥ 2 Einträge, **deterministisch lexikografisch nach `resource` (LC_ALL=C, AD-17h)**, getrennte Zusammenfassungs-Concepts je Quelle verboten; (3) **gemischte claim-granulare Provenienz (AD-4a/4b, A0-3)** — je Aussage Inline-`raw/`-Verweis §5.5, **Multi-Beleg-Konsolidierung** (§5.5-Semikolon-Form, voller Pfad je Beleg; keine Beleg-Tilgung AD-4), **AD-16-Default** (widersprüchliche Aussagen bleiben, Disagreement in `log.md`; Sandbox-N2); (4) **AD-4c-Übernahme-Marker** („übernommen aus `<Concept>` auf Basis von `<source>`, nicht eigenständig belegt" — nie alleinige Provenienz; Sandbox-N3); (5) **Reflektiertheits-Selbsttest** (FR-7 AC-4/NFR-7) — keine per-Source-Zusammenfassungs-Struktur („Quelle A: …"), grepbasiert `grep -nE 'Quelle [A-Z]:|Source [A-Z]:'`, Selbsttest-FAIL → textuell benannt (NFR-4) und vor Run-Abschluss behoben; (6) **§5.6-Pin unverändert** + **Form-Wahl-Klassifikationsprobe (Story-3.3-Defer U2/U7)** — Überlapp-Einheiten an die §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` (erste zutreffende Form), Textgenauigkeits-Rahmen der übrigen je Teilbestand; (7) **`log.md`-Eintragspflicht** („Story 3.1-Update" bei Update, Anlage-Eintrag bei neuem Synthese-Concept; Multi-Source-Liste + `<Baseline-Commit>`); (8) **Erhaltungs-Invariante (§5.9 Pkt. 5 gilt) + Determinismus-Vertrag** (Ziel-Pfad via §3.2/§5.7, `sources`-Lexikografie, Konsolidierung/Form-Zuordnung; `generated.at`-Wanduhr-Gap bleibt A0-20-Konvention, Home Story 3.8). **§7:** Synthese-Vorbehalt **aufgelöst** (in §5.10 verankert; verbleibende 3.x-Themen: Leasing/Dirty-Tree → Story 3.5/3.6). **§8:** Normreferenzen bleiben unverändert (AD-4/AD-4a/FR-7/A0-3 sind bereits über §5.5/Roh-Normreferenzen abgedeckt; AD-4c-Kontext-Marker weiterhin §5.5). **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; **keine fünfte Update-Form** (Abgrenzungs-Reihenfolge §5.9 unverändert, Synthese = Querschnitt); kein Leasing-Scope; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-4-wissen-aus-mehreren-sources-synthetisieren`**`in-progress`**. Sandbox-Nachweis (S1S6 + N1N3 + Form-Wahl-Probe = 10 Szenarien, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.4, Revision 2.9). **Review-Loop-1-Patches (bmad-code-review, 3 Layer; dieser Eintrag nachgeführt):** Reflektiertheits-Selbsttest-Muster auf Zeilenanfangs-Label erweitert (`^(Quelle|Source) [A-Z][A-Za-z0-9_ -]*:` — mehrbuchstabige/nummerierte Quell-Labels werden ebenso erkannt); **Body-Reihenfolge deterministisch** (Aussagen-Positionierung lexikografisch über die Beleg-Anker, AD-17h) + **Konsolidierungs-Kriterium** (Befund-Äquivalenz über dieselbe erkannte Wissenseinheit) explizit; Stil-/Genus-Korrekturen (`sole``einzige`, `der Update``das Update`, `sources`-Pluspunkt→`sources`-Zuwachs). Keine Änderung an Normreferenzen, §7, Abschlussklausel.
- **Revision 3.0 (2026-08-19, Story 3.5):** Neue Sektion **§5.11 „Leasing & Dirty-Tree-Schutz für konkurrierende Producer (Story 3.5)"** eingefügt (nach §5.10, vor §6) — die **verbindliche Verankerung der Koordinations-Dimension für konkurrierende Producer** (AD-17a..f, A0-12..A0-16, FR-2/FR-12; §7-Vorbehalt `:357` aufgelöst): (1) **Lease-Akquise (AD-17a, A0-12)** — Arbeits-Branch-Form **`lease/<area>/<id>`** von der Merge-Base aus, **Lockfile** (`lease/<area>/<id>.lock`, semantisch identisch in jedem Adapter, A0-12; deterministisches Format: `area`, `id`, `producer`, `baseline_commit` voller SHA, `holder_id`), Lease-Hold bei existierendem Lockfile (kein Überschreiben, keine Mutation), **Merge-Base-Disziplin** (Lease gg. eindeutigen Commit-Object-Wert über `git merge-base`/`<Baseline-Commit>`, AD-17h); (2) **Root-Scope-Lease (AD-17b, A0-13)** — umfasst `wiki/` inkl. `log.md`, `index.md` und aller Root-Dateien; kein Bereich jenseits `wiki/`; (3) **Dirty-Tree-Schutz (AD-17e/f, A0-16)** — Pre-Mutation-Prüfung (`git status --porcelain -- <Mutationsbereich>`), fremde uncommittete Änderungen **geschützt statt gelöscht** (Stash/Scratch-Zone außerhalb `wiki/`, dokumentiert in `log.md`), Screen-Artefakte textuell benannt (NFR-4), **UNCOMMITTED_INPUT-Abbruch** „published/committed Input erforderlich" (AD-17a; I/O-Matrix-`UNCOMMITTED_INPUT` — dieselbe Pre-Run-Prüfung wie §5.9-P2-Element-(1)-`INPUT_UNCOMMITTED`, kein zweiter Abbruch-Pfad), Mutationen nur auf Directory-/Commit-Ebene; (4) **kein textueller Auto-Merge (AD-17c, A0-14)** — compiler-vermittelter Merge über die **AD-16-Klassifikation** (Default: Erhaltung) mit `log.md`-Eintrag; Lease-Konflikt → AD-16-Pfad; Unentscheidbarkeit → menschliche Eskalation (AD-17g); (5) **Commit-Boundary = Mutations-Boundary** unverändert (§0/§5.3, AD-17f) — Diff-Selbsttest (§5.9 Pkt. 5) auch für Leasing-Runs, Ghost-Diff-Rollback; (6) **`log.md`-Eintragspflicht** (Lease-Akquise, Dirty-Tree-Schutz-Dokumentation, Merge-Klassifikation, Eskalation, **Lease-Freigabe**; `<Baseline-Commit>` notiert, D-2; Datumsgruppe **neueste zuerst**); (7) **Determinismus-Vertrag (AD-17h/A0-19)** — Lease-Akquise, Lockfile-Inhalte, Merge-Klassifikation deterministisch aus dem committeten Git-State; **Lease-Staleness/Recovery bleibt Story 3.6** (AD-17d, A0-15) — diese Sektion mutiert deren Mechanik nicht; testbares Seam-Kriterium (3.5 = committed-state-deterministisch, 3.6 = Zeit-/Umgebungs-Zustands-Frage, die den committeten Zustand verlässt). **§7:** Leasing-/Dirty-Tree-Vorbehalt **aufgelöst** („in §5.11 verankert (Story 3.5)"); Scope-Einleitung um §5.11 geöffnet; verbleibendes 3.x-Thema: Lease-Staleness/Recovery → Story 3.6. **§8:** Normreferenzen um AD-17b/AD-17c/AD-17d/AD-17e-f/AD-17g (Spine) und A0-12/A0-13/A0-14/A0-15/A0-16 (Epics) ergänzt; AD-17d/A0-15 in der §5.11-Enum als Norm-Rückverweis (ohne §5.11-Auflösungs-Bezug) gekennzeichnet. **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; **kein neuer Frontmatter-Key für Lease-Metadaten** (Lease lebt in Git/Datei-Ebene, Vertrag §3.1–§3.7 unverändert); **keine fünfte Update-Form** (Abgrenzungs-Reihenfolge §5.9 unverändert, Leasing = Querschnitt); kein Staleness-Scope (→ Story 3.6); Commit-Boundary-Regel unverändert. **Lease-Freigabe** (Release) ist in §5.11 Pkt. 1/6 verankert (deterministisch, auf das committete Ergebnis bezogen); Staleness-/Recovery-Aspekte der Freigabe (verwaiste Leases) bleiben Story 3.6. **`sprint-status.yaml`:** Key `3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz`**`in-progress`**. Sandbox-Nachweis (L1L6 + Negativ-Kontrollen, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.5, Revision 3.0).
- **Revision 3.1 (2026-08-19, Story 3.6):** Neue Sektion **§5.12 „Lease-Staleness & Recovery-Basis (Story 3.6)"** eingefügt (nach §5.11, vor §6) — die **verbindliche Verankerung der Staleness-/Recovery-Dimension** (AD-17d, A0-15; §5.11-Seam-Kriterium S-1 und Pkt.-7-Text sowie §5.11 Pkt. 1 „verbleibt bis Story 3.6" bleiben textuell unverändert): (1) **Lease-Registrierung & generationenbasiertes TTL** — Registrierung im **Clone-Root-State**, monotoner Run-Generator (`generation`-Zähler je Clone-Root, deterministisch aus dem committeten Git-State ableitbar: `lease/`-Baumableitung oder Mono-Commit-`lease-granite-root`-Marker), **TTL-Ablauf-Kriterium generationen-basiert** (ältere/niedrigere Erzeugungs-Generation = stale, blockiert keinen nachfolgenden Run), **kein Wanduhr-Timestamp** im Lockfile-/Registrierungs-Format (A0-20; streng durchsetzendes Zeit-TTL = Ask-First); (2) **holder_id-Ableitung** (deterministischer Default `holder_id := <producer>-<id>`, Defer aufgegriffen); (3) **Verwaist-Klassifikation** (Übernehmen gg. erneute Merge-Base-Prüfung oder Stale-Markieren via Registry-Marker, je `log.md`-Eintrag; verwaiste Leases **nie still gelöscht**, AD-17e; Konflikt → AD-16-Pfad/Eskalation AD-17g); (4) **baseline_commit-Merge-Base-Diskrepanz-Regel** (git-merge-base gewinnt, notierter SHA = Sekundär-Fingerprint, Fallback ohne Boundary; Defer aufgegriffen); (5) **`raw/`-Recovery-Basis & native `git stash`-Variante** (`raw/` immutable AD-3 als Zugriffs-/Consistency-Basis, Real-Baum-Beweis auf Sandbox-Evidenzwege beschränkt, EC-1-Grenze; `git stash push -- <Pfade>` als zweitezulässige Schutzvariante, Defer aufgegriffen); (6) **Registrierungs-Invariante & kumulativer Registry-Aufbau über Runs** (Gen > erzeugend oder gleiche Gen, hält den sichtbar höchsten Reg-Generator; kein eigener `# Log`-Stand — `wiki/log.md` alleiniger Aufzeichnungs-Ort, Vertrag §5; Defer aufgegriffen); (7) **`log.md`-Eintragspflicht & Determinismus-Vertrag** (Registrierung/TTL, Lease-Übernahme, Stale-Markierung, Recovery-Basis-Nutzung als Datumsgruppen-Einträge; Registrierung/TTL/Verwaist-Klassifikation/log.md-Texte deterministisch aus dem committeten Git-State). **§7:** Staleness/Recovery-Vorbehalt **aufgelöst** („in §5.12 verankert (Story 3.6)"). **§8:** Normreferenzen AD-17d/A0-15 von reiner Story-Zuordnung auf **§5.12-Anker** angehoben (`AD-17d (Lease-Staleness, §5.12)`, `A0-15 (Lease-Staleness, §5.12)`). **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; **kein neuer Frontmatter-Key für Lease-Metadaten** (Lease lebt in Git/Datei-Ebene, Vertrag §3.1–§3.7 unverändert); kein Prädikat-/Format-Key; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-6-lease-staleness-recovery-basis-absichern`**`in-progress`**. Sandbox-Nachweis (STALE-1..STALE-6 + Erhaltungs-Invariante, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.6, Revision 3.1).
- **Revision 3.2 (2026-08-20, Story 3.7):** Neue Sektion **§5.13 „Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)"** eingefügt (nach §5.12, vor §6) — die **verbindliche Verankerung der logischen Phasen-Disziplin** (AD-6, A0-7; §7-Vorbehalt der Reason/Mutate-Trennung aufgelöst): (1) **Änderungsplanung** — der §5.9-Pkt.-6-P2-Block wird Wortlaut-unverändert als Änderungsplanungs-Phase der AD-6-Kette institutionalisiert (textuell festgehaltener konsistenter Plan: Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheits-Liste, Struktur-Erhaltung; Plan-Defizit textuell benannt NFR-4 und verhindert die Mutation — I/O-Matrix `PLAN_BEABSICHTIGT`); (2) **Plan-Freeze** (Veränderungs-Sperre nach Phasenabschluss, Kopplung an die §5.9-Pkt.-5-Ghost-Diff-Probe; erlaubte Pfad-Menge = Kandidatenliste Neu-Anlage `log.md` `index.md``PLAN_FREEZE`); (3) **Zustands-Restaurations-Invariante** (Post-Rollback-Diff gg. Baseline leer / Bundle == valide committet; §5.3-Pkt.-3-/§6-Pkt.-3-Rollback und Ghost-Diff-Rollback Wortlaut-unverändert, Endzustands-Konsistenz als git-prüfbare Eigenschaft — `MUTATION_ABBRUCH`, A0-7-AC-2); (4) **kein Teilerfolg als fertige Mutation** (`MUTATION_TEILFOLGE`, AD-17f unverändert); (5) **Validierungsphase `VALIDATION_FAIL`** (§6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent); (6) **Validierungsphase `VALIDATION_SUCCESS`** (Mutationen als Ganzes committet erst nach Diff-Selbsttest ohne Ghost-Diff, §6-Pkt.-4-Nachweis, kein Wanduhr-Trigger A0-20 — A0-7-AC-3); (7) **keine eigene Workflow-Engine** (`KEINE_EIGENE_ENGINE`: logische Trennung in einer Session, kein Prozess/Server/MCP, D-3/AD-11 — A0-7-AC-4). Die **AD-6-Phasen-Zuordnungstabelle (§0 ↔ vier AD-6-Phasen)** ist eine Klassifikation ohne neue Norm: §0 bleibt die deterministische Takt-Folge (Wortlaut unverändert, keine Um-Nummerierung, keine neue Phase); §5.9-Pkt.-6-P2-Block, §5.3-Pkt.-3/§6-Pkt.-3-Rollback und §5.9-Pkt.-5-Diff-Probe bleiben **textuell unverändert** (Rückverweise, keine Doppel-Instruktion); §5.12-Seam-Satz unverändert (Lease-Staleness/Recovery bleibt Story 3.6). **§7:** Reason/Mutate-Vorbehalt **aufgelöst** („in §5.13 verankert (Story 3.7)"-Bullet, AD-6/A0-7, AC-1/2/3/4; bestehende Story-Bullets unverändert). **§8:** Normreferenz AD-6 von reiner Story-Zuordnung auf **§5.13-Anker** angehoben (`AD-6 (Reason/Mutate-Trennung, §5.13)`); A0-7-Reihenfolge-Referenzen in der Epic-Zeile um §5.13-Bezug nicht neu aufgelistet (A0-7 bleibt in der bestehenden Epic-Enum, die §5.13-Ankerung erfolgt über die Spine-AD-6-Zeile und den neuen §7-Bullet). **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; **kein neuer Prädikat-/Format-/Frontmatter-Key**; keine neue Workflow-Engine (AD-6); §0-Phasen-Listentext unverändert; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell`**`review`** (Implementierungs-Flip `in-progress` + Review-Start-Flip `review` im selben Commit; der finale `done`-Flip erfolgt im Step-05-Status-Sync). Sandbox-Nachweis (CONSIST-1..CONSIST-7 + Erhaltungs-/Restaurations-Invariante, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.7, Revision 3.2).
- **Revision 3.3 (2026-08-20, Story 3.8):** Neue Sektion **§5.14 „Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)"** eingefügt (nach §5.13, vor §6) — die **geschlossene, aus dem committeten Git-State ableitbare Bestätigungs-Mechanik des Determinismus-Vertrags** (AD-17h/FT-10/A0-19; §3.2/§5.9/§5.10/§5.11/§5.12/§5.13-Rückverweise verankern den Vertrag, §5.14 die Bestätigung): (1) **Bundle-State-Definition** (deterministische Projektion des committeten Git-States: committeter Baum unter `wiki/`/`raw/` gg. Baseline + Plan-/Kandidaten-/Reihenfolge-Outputs + Ausführungs-Entscheidungen; `generated.at`/`verified[].at` als benannte Ausnahme, A0-20 — einzig zulässige Differenz zwischen zwei Runs), (2) **Zwei-Run-Bestätigungs-Mechanik** (agent-Instruktions-basiert, D-3/Q-6: der Producer führt die Instruktion **zweimal über demselben committeten Git-State in einer Session** aus und vergleicht die Bundle-States — Vergleichs-Operandum = committeter Baum, re-executierbare diff-/hashbasierte Formeln, keine Wanduhr-Steuerung; identische Plan-/Kandidaten-/Reihenfolge-Outputs, byte-identische mutierte Bestandteile; keine eigene Runtime/kein neues Werkzeug, AD-6/AD-11), (3) **Ausnahme-Menge** (allein der `generated.at`-Wanduhr-Gap; dokumentiert, kein stiller Ausschluss; alle übrigen Bestandteile byte-identisch), (4) **Abweichungs-Klassifikation** (jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsfehler, kein Rauschen; textuell benannt NFR-4, Run korrigiert/rollt zurück, Zustands-Restaurations-Invariante §5.13 Pkt. 3), (5) **Normalisierungs-/Match-/Orphan-Schließung** (§3.2-Pkt.-1b-Em-Dash-Kollaps-Klasse `[-–— _]`, Kollaps-Reichweite, Match-Scope Stufe a; §5.10-Pkt.-8-Reconcile-Orphan-Regel; Erhebungen vollständig pinbar). **§3.2:** die bekannten Determinismus-Lücken als append-only-Regel-Ergänzungen geschlossen (Em-Dash in der Kollaps-Klasse, Kollaps-Reichweite, Match-Scope der Stufe a) — bestehender §3.2-Wortlaut unverändert. **§5.10 Pkt. 8:** Orphan-Politik zur deterministischen **Reconcile-Orphan-Regel** präzisiert (unzugeordnet, datumsgruppierter `log.md`-Verwaist-Eintrag mit `<Baseline-Commit>`, kein Banner/keine stille Vorbearbeitung/keine eigenständige Anlage; AD-16-Default). **§5.13 Pkt. 7:** Home-Verweis des `generated.at`-Gaps auf die §5.14-Definition verlagert (Wortlaut des §5.13-Satzbaus semantisch unverändert; die Behandlung des Gaps ist jetzt in §5.14-Pkt.-3 definiert). **§7:** Determinismus-Vorbehalt der Relevanzbestimmung **aufgelöst** (Em-Dash-Lücke → „in §5.14 verankert (Story 3.8)"-Ergänzung; §3.2-/§5.10-/`at`-Gap-Schließung benannt; bestehende Story-Bullets unverändert — kein neuer Bullet ersetzt einen bestehenden). **§8:** Normreferenz **AD-17h** von reiner Story-Zuordnung auf **§5.14-Anker** angehoben (`AD-17h (Determinismus, §5.14 — Zwei-Run-Bestätigung mit benannter `generated.at`-Wanduhr-Gap-Ausnahme, A0-20/§5.9-Pkt.-2/§5.10-Pkt.-8)`). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`adapters/`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; das `generated.at`-Verhalten selbst **unverändert** (nur seine Behandlung im Bundle-State-Vergleich; Verhaltenswechsel = Ask-First); keine Workflow-Engine/kein neuer Prozess/Server/MCP (AD-6, AD-11); §0-Phasen-Listentext, §5.13-Phasen-Disziplin und §6/§6.5 textuell **unverändert** (Fugen-Identität); Commit-Boundary-Regel unverändert; `schema/canonical-terms.md` unverändert (Em-Dash-/Kollaps-Schließung lebt als Regel in §3.2, nicht als Registry-Edits). **`sprint-status.yaml`:** Key `3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato` **`backlog``in-progress`** (Implementierungs-Flip; finaler `review`/`done`-Flip im Step-04/05). Sandbox-Nachweis (**DET-1..DET-8 + Erhaltungs-Invariante, Exit 0**; Zwei-Run-Identität nicht-vakuum) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.8, Revision 3.3).
+16
View File
File diff suppressed because one or more lines are too long