25 Commits
Author SHA1 Message Date
mita 390d31c70e . 2026-08-25 10:42:53 +02:00
Michael Tamse 0d79421969 retro auf Epic 3 2026-08-24 16:36:42 +02:00
Michael TamseandClaude 3f57586a50 feat: Story 3.13 finaler Status-Sync — done-Flip + Epic 3 done (Step-05 nach grünem Re-Run #10 + konvergiertem Review-Loop-2)
Grüner Re-Run #10 (committed 09c1c83): FAILED=0 PASS_COUNT=51, SANDBOX-3-13-OK,
RUN_OK/Exit 0 — A.5 Pre-Check sauber, C 12/12, D 4/4 SUCCESS, D-NEG 3×FAIL+1×SUCCESS,
E.4 NON_AT=0 AT=2, E.6 Feldsatz-exakt (P-L2-2) + A==B + Known-Value-Witness (P-L2-1),
E.9 kanonische 'Baseline $BASE'-Form (P-L2-6), F G-1..G-8 (G-6 Negativ-/Positiv-Kontrolle),
G-8 Porcelain-Clean (inkl. Untracked) + AD-3 Read-only ohne Diff.
Review-Loop-2 konvergiert (bmad-code-review 4 Layer): 38 rohe → 4 decision-needed /
11 patch / 2 defer / 4 dismissed; alle Entscheidungen mit ProMods geschlossen,
alle Patches P-L2-1..11 angewendet (Gate 1005 Z., bash -n clean).

Status-Sync (Präzedenz 3.7–3.12):
- spec-3-13 Frontmatter status: done, review_loop_iteration: 2; Tasks L71/L72/L75 getickt
- sprint-status.yaml: Key 3-13 → done, epic-3 → done (AC-9: alle Stories 3.1–3.13 done),
  last_updated → 08-24-2026 13:22
- deferred-work.md: 3.13-Home-Defers (L551/560/576/594/597/611/629/633) über den
  "Aufgegriffen: Story-3.13-Abnahme als Home"-Block erledigt (bereits in 09c1c83);
  Review-Loop-2-Block (W-L2-1/2 + D-3.13-L2-1/2/3) als offene Defers → Rev 3.9/Epic-4
- epic-3-context.md: Ist-Zeilen (Z.7/46/52) auf grünen Re-Run #10 + done nachgeführt
- wiki/log.md: neuer ## 2026-08-24-Eintrag (append-only, P-12-Präzedenz; bestehende
  Datumsgruppen unverändert) — Review-Loop-2-Abschluss + grüner Re-Run #10 + done-Flip

Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt: git status --porcelain -- wiki/ zeigt nur
diesen log-Eintrag; AD-3 Read-only (schema/validator.md, schema/wiki-compiler.md,
schema/compiler.md, adapters/, raw/, schema/canonical-terms.md) unverändert;
kein Standalone (D-3), keine neue §7-Invaliditätsklasse, keine Vertragsänderung.
epic-3-retrospective (optional) bleibt offen.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 13:36:07 +02:00
Michael TamseandClaude 09c1c83a5a fix: Story 3.13 Review-Loop-2-Auflösung (bmad-code-review 4 Layer, Diff 50f3628..HEAD + working tree): 38 rohe → 4 decision-needed / 11 patch / 2 defer / 4 dismissed; alle Entscheidungen mit ProMods geschlossen (D-3.13-L2-1 B2-Reihenfolge-Pin+§8-Überreich = Defer+Bearer-Fix, D-3.13-L2-2 Datumsgruppen-Wanduhr = Defer+Gate-Dokumentation, D-3.13-L2-3 AC-6-CREATE-Residual = Defer+Substitutions-Notiz, D-3.13-L2-4 frozen-L24-Wortung = dokumentierte Abweichung annotiert); Patches P-L2-1..P-L2-11: E.6 Known-Value-Witness (baseline==$BASE + sources_added==raw/alpha-v2.md) + Feldsatz-exakt-Check (4 Felder, keine Fremdzeilen), AGENT_PROMPT-Receipt-Form präzisiert (GATE-spezifisch vs. compiler.md-Konzept §5.14 Pkt. 2, kommagetrennt oh. Leerzeichen, lex LC_ALL=C), E.9 kanonische 'Baseline $BASE'-Form hart (Rev 3.8), P-16 Floor auf Dezimalzwang (10#), A.2-Meldung 5 Referenzen, D-NEG.3-Punktbezeichnung, Spec-Doku-Sync (Review-Loop-2-Sektion, Change-Log, #6/#7-Teilkorrektur); Status: in-progress (grüner Re-Run #10 + Step-05-Status-Sync ausstehend), bash -n clean (1005 Z.)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 12:13:03 +02:00
Michael TamseandClaude cccc0a35e9 fix: Story 3.13 Re-Run-#8-Auflösung (P-19): E.6-Feld-Dreideutigkeit — AGENT_PROMPT pinst 'baseline' (Vor-Mutation-HEAD) + 'sources_added' (neue resource:-Werte der Frontmatter-sources:-Liste, sortiert) als reine Funktionen der Lauf-Eingaben; E.6-Assertion (A==B byte-identisch) unverändert; P-18 bestätigt (5/5 Receipts); C 12/12 + D/D-NEG/E.1-E.5 grün (E.4 NON_AT=0 AT=2, Rev 3.8 dreifach); Status-Sync (Spec Change Log)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 09:41:36 +02:00
Michael TamseandClaude 65dfc26961 fix: Story 3.13 Re-Run-#7-Auflösung (P-18) — AGENT_PROMPT entkoppelt den
Run-Receipt als verpflichtenden Gate-Reporting-Lieferant von compiler.md

Befund Re-Run #7 (grün bis E.6): E.4 erneut GRÜN (NON_AT=0 AT=2 — doppelte
Bestätigung der Rev-3.8-Remediation unter echten frischen LLMs), C/D/D-NEG/
E.5 grün; HARD-FAIL an E.6 mit NEUER Ursache: "run-receipt.txt B fehlt/leer"
(Agent A lieferte den Receipt, Agent B nicht — B's wiki/-Mutationen waren
vollständig, E.5-Witness grün, RUN_DONE b vorhanden; Agent B behandelte den
Receipt als optional).

Klassifikation: LLM-Prompt-Adhärenz-Flake, kein Gate-Logik-Fehler, kein
Schema-Defizit (3/4 Receipts in #6/#7 geliefert = ~75% Adhärenz). Gate-
Verhalten korrekt (fehlender Nachweis = HARD-FAIL, kein stilles Grün — die
E.6-Härtung aus #6 bewahrt ihre Eigenschaft: sie prüft nur, wenn der Receipt
existiert). Wurzelursache: der Prompt hatte den Receipt "EXAKT nach den
Konventionen von schema/compiler.md" zugeordnet — compiler.md definiert ihn
aber nicht (0 Treffer "receipt") und der Prompt erklärt compiler.md zur
einzigen normativen Instruktion → Agent kann den Receipt als außerhalb-des-
Vertrags/optional deuten (gleiche Klasse wie P-17 Prompt-Wortungs-Präzisierung).

Änderung (Gate-only, bash -n clean; kein compiler.md-/frozen-Block-Change;
D-1-konform — exakte Feldstruktur != erwartete Werte):
- AGENT_PROMPT: Receipt als expliziter verpflichtender Gate-Reporting-
  Lieferant von compiler.md entkoppelt ("GATE-spezifische Berichtsdatei, die
  schema/compiler.md NICHT definiert"); beide Lieferungen (wiki/-Mutationen +
  run-receipt.txt) sind PFLICHT; RUN_DONE erst NACH Vorliegen beider
  Lieferungen; exakte 4-Feld-Form bleibt (Feldnamen exakt, Werte frei).
- Header: P-15/P-16/P-17/P-18-Patch-Referenzen ergänzt (bisher nur P-2..P-14
  genannt).
- Spec Change Log: Re-Run-#7-Befund + P-18-Härtung + Stabilitäts-Beobachtung
  für #8 (frozen Block L12-51 unangetastet, diff-geprüft).

Nächster Schritt: Re-Run #8 (Erwartung: E.6 grün + F G-1..G-8 + G Porcelain
erstmals). Falls Agent erneut keinen Receipt liefert = systematischer
Prompt-Adhärenz-Defekt (breitere Pflicht-Schritt-Liste), sonst Flake.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 08:31:05 +02:00
Michael TamseandClaude 77fd460ec3 fix: Story 3.13 Re-Run-#6-Auflösung (E.6-Over-Pin) — Gate-Härtung: Receipt-Abgleich
auf die drei deterministischen Felder (baseline/candidates/sources_added byte-identisch
A==B + decision-Vorhandensein); decision-Prosa = Befund-Äquivalenz (Nicht-Wort-Identität)

Befund Re-Run #6 (grün bis E.6): E.4 GRÜN (NON_AT=0 AT=2 — Rev-3.8-Remediation
wirkt: Bundle-State byte-identisch bis at), D/D-NEG/C/E.5/E.7/E.8/E.9 grün;
HARD-FAIL an E.6: Run-Receipt decision:-Zeile (freie LLM-Prosa) wurde byte-
vergleicht und divergierte A/B (identischer Befund, freie Wortung), während
baseline/candidates/sources_added byte-identisch A==B waren.

Klassifikation: Gate-Over-Pin (kein compiler.md-Defizit, kein AC-4-Fall) —
schema/compiler.md definiert den Receipt nicht (0 Treffer "receipt"), der
Gate-Prompt (D-1) deklariert decision selbst als frei; der alte E.6-Byte-
Vergleich widersprach damit dem eigenen Prompt (selbe Over-Pin-Klasse wie
Re-Run #5 log.md, auf der Gate-Assertion-Seite). Semantische Entscheidung
bereits am Bundle-State bewiesen (E.4 + E.5/E.7/E.8).

Änderung (Gate-only, bash -n clean; kein compiler.md-/frozen-Block-Change;
D-2/D-1-konsistent, D-3.13-2-Option-1-Prinzip):
- E.6: receipt_field-Helfer (Präfix-Strip, WT-Pfad-Normalisierung, Trailing-
  Whitespace/CRLF-robust); nur deterministische Felder byte-identisch A==B,
  decision-Zeile auf Vorhandensein (nicht leer); Prosa = Befund-Äquivalenz.
- Header-D-2-Bullet: Härtung + Defer-Verweis dokumentiert.

Dokumentation:
- Spec Change Log: Re-Run-#6-Befund + Härtung + Defer (frozen Block L12-51
  und GENERATED_AT_AUSNAHME unangetastet, diff-geprüft).
- deferred-work.md: neuer Abschnitt "Deferred from: Re-Run #6" (D-3.13-R6-1):
  byte-deterministischer Receipt = offener Defer/Ask-First-Kandidat (spätere
  compiler.md-Revision Rev 3.9 oder Epic-4), kein stiller Semantik-Change.

Entscheidungshinweis: Gate-Härtung + Defer-Notiz ausgeführt (kleinste, ehrliche,
vertragskonforme Variante, konsistent mit dem autorisierten Muster Rev 3.8 /
D-3.13-6 Option 2); Nutzer-Away — zur Bestätigung im Review-Loop-2.

Nächster Schritt: Re-Run #7 (Erwartung: E.6 grün, F G-1..G-8 + G Porcelain
erstmals erreichbar).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 07:19:14 +02:00
Michael TamseandClaude 2a9fa89f8a docs: Story 3.13 Status-Sync nach Rev-3.8-Ausführung (Spec Change Log + Design Notes + Autorisierungs-Träger §0.1)
Spec-3-13:
- Neues Spec-Change-Log-Top-Eintrag: Epic-1-Remediation (Rev 3.8) +
  A/B-Umfangs-Entscheidung (b) ausgeführt (24/26 NON_AT CREATE-getrieben;
  Minimal-Änderungssatz: (1) CREATE-Kern aus A/B-Fixture, (2) log.md-Pin).
- Design Notes: Allowance-Zeile auf alpha-UPDATE-Witness (raw/alpha-v2.md#S-3)
  umgestellt; Fixture-Beschreibung = 3-12-Muster OHNE CREATE-Kandidat
  (D-3.13-6 Option 2; index/gamma unverändert, IDX_ADDED=0).
- Frozen-Block L12-51 + GENERATED_AT_AUSNAHME-Zeile UNBERÜHRT (diff-geprüft).

Autorisierungs-Träger (compiler-revision-3-8-...):
- §0.1 Ausführungsstatus: B2 (log-Pin) + A/B-Umfang (b) = ANGEWENDET (Rev 3.8,
  Commit 7c3c19b); B1 (Slug) = offener Defer/Ask-First-Kandidat; C (Body-Byte)
  = zurückgewiesen (FR-2-Verstoß). Original-Entwurfstext als historische
  Entscheidungsgrundlage erhalten.

Status: Story 3.13 bleibt in-progress; finaler done-Flip + Review-Loop-2
erfolgen nach grünem Re-Run #6 (Step-05-Status-Sync).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 06:04:04 +02:00
Michael TamseandClaude 7c3c19bac4 fix: Story 3.13 Epic-1-Remediation (Rev 3.8) — kanonische byte-deterministische log.md-Eintragsform + CREATE-Kern aus A/B-Fixture (D-3.13-6 Option 2)
Re-Run #5 lief RED an E.4 (A/B-Determinismus: NON_AT=26 AT=4 → HARD-FAIL).
Exakter Zeilen-Nachzähl-Befund: 24/26 (92%) der NON_AT-Abweichungen sind
CREATE-/Slug-getrieben (beta.md 11 + quanten-observatorium-kanal.md 11 +
index.md 2), verbleibende 4 log.md-Zeilen = 2 CREATE-Einträge (Slug-Divergenz)
+ 2 freie UPDATE-Wortungen (+ Wanduhr-generated.at im Body).

Autorisierter Minimal-Änderungssatz (Q1 + "continue"):
(1) compiler.md Rev 3.8 (D-3-Instruktions-Patch, additive Präzisierung):
    - §5.9 Pkt 4 (Update), §5.1 Pkt 4 (Anlage), §5.10 Pkt 7 (Synthese-Anlage):
      kanonische, byte-deterministische log.md-Eintragsform fixiert
      (- Story 3.1-Update: <concept> (<neue-Quellen>; Baseline <SHA>) etc.),
      kein freier Zusatztext, kein Wanduhr-Wert im log.md-Body (Wanduhr lebt
      ausschließlich im Frontmatter-generated.at, §5.14 Pkt 3).
    - §8 Revisionslog Rev 3.8-Eintrag.
    - NUR log.md-Form gepinnt; NUR Concept-Body/Slug/FR-2/§5.14-Pkt-3-Ausnahme
      UNVERÄNDERT. CREATE-Zielpfad-Signal-Pin = offener Defer (Ask-First).
    - Form = genau die, die Sub-Runs 3-3/3-4 mechanisch bereits demonstrieren
      (Ist-Behaviour verankert, kein Widerspruch).
(2) run-sandbox.sh E-Sektion: CREATE-/beta-Fall aus der A/B-Fixture entfernt
    (D-3.13-6 Option 2): E.1-Fixture (keine beta-Quellen), AGENT_PROMPT (keine
    beta-Fakt), E.5 (CREATE-Witness entfernt, UPDATE-Alpha-Witness bleibt),
    E.8 (index.md unverändert statt "+1 CREATE-Link"), Header-Kommentare.
    A/B-Prüfung deckt den Update-/Erhaltungs-Kern; CREATE/Synthese gelten über
    Sub-Runs 3-3/3-4 + D-Validator als demostriert.

Beide Änderungen berühren NICHT die frozen I/O-Matrix-Zeile GENERATED_AT_AUSNAHME
und NICHT den §5.14 Pkt 3-Vertrag. bash -n clean. Erwartetes E.4-Ergebnis:
NON_AT=0 AT=2 (alpha.md at-only).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 05:55:05 +02:00
Michael TamseandClaude fb4c1c8b05 docs: Story 3.13 E.4-Befund-Raffinierung (Prämisse-Korrektur vor Remediation)
Quelltext-verifiziert (compiler.md L34/84/297/374/375, spec-3-8 P-5 L150):
- §5.14 Pkt.3 verlangt byte-Identität aller Bundle-Bestandteile außer `at`;
  §5.10 Pkt.3 pinnt nur die CREATE-Body-REIHENFOLGE (Wortlaut =
  "Befund-Äquivalenz, nicht Wort-Identität"); §5.1/FR-2 verlangen freien,
  eigenständig formulierten Body.
- 3.8-Determinismus-Basis wurde NUR mechanisch simuliert (Subshell-Läufe),
  NUR UPDATE-Pfad (nachweislich A/B byte-identisch bis auf `at:`), nie
  echte frische LLMs; CREATE-Byte-Identität unter echten LLMs ist eine
  latente Vertrags-Spannung (in 3.8 verdeckt, in 3.13/E.4 aufgedeckt) —
  kein bloßes Instruktions-/Validator-Defizit.
- Slug-Teil ist per compiler.md-Patch auflösbar; Body-Byte-Teil NICHT
  innerhalb des gültigen Vertrags (Byte-Pin bricht FR-2).
- Q1-Prämisse ("Epic-1-Remediation → Re-Run #6 grün") korrigiert;
  Remediation/12-Sandbox-Re-Execution/Re-Run #6 GEHALTEN bis zur
  A/B-Vergleichsumfangs-Entscheidung (frozen-Matrix-Zeile
  GENERATED_AT_AUSNAHME, Ask-First). Empfehlung (b): CREATE aus A/B-
  Fixture nehmen; CREATE-Byte-Fall als benannter Defer.
Keine Schema-/Gate-/Fixture-/frozen-Block-Änderung — Notiz dokumentiert
nur Befund + Verankerungen + Entscheidungsbasis.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-23 19:51:13 +02:00
Michael TamseandClaude 5029124445 fix: Story 3.13 Re-Run-#5-Auflösung (E.4-AD-16-Befund + AC-4-Roadmap + Matrix-Zeile VALIDATOR_DEFIZIT)
Re-Run #5 (Voll-Lauf gehärtetes Gate 915 Z., /tmp/gate-313-run5.log): ROT an E.4 (NON_AT=26 AT=4) — korrekt erkannter, echter AD-16-A/B-Divergenz-Befund in der freien CREATE-Synthese (Slug-Identität beta vs. quanten-observatorium-kanal + freier Body-/log.md-Wortlaut), kein false-PASS; UPDATE-Pfad alpha byte-identisch bis auf at:. Grün: C 12/12, D 4/4 SUCCESS, D-NEG 3xFAIL+1xSUCCESS, E.1-E.3 + beide frische Agent-Läufe (RUN_DONE); F/G nicht demonstriert (fail-fast an E.4).

AC-4-Defizit-Roadmap (Nutzer-Entscheidung 2026-08-23): autorisierte Epic-1-Remediation (compiler.md Rev 3.7->3.8: CREATE-Slug-Pin, CREATE-Body-kanonische Form, log.md-Regel) -> 12-Sandbox-Re-Execution -> Re-Run #6. Frozen-Matrix-Zeile VALIDATOR_DEFIZIT auf G-5 FAIL (Non-Zero) korrigiert (Ask-First-Erfüllung). Tasks getickt (5/8); Status-Sync: log.md/epic-3-context/sprint-status (Key bleibt in-progress, last_updated 08-23-2026 17:09); Gate/Schemas/raw unverändert.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-23 19:32:33 +02:00
Michael TamseandClaude 37d9fdcec5 fix: Story 3.13 Re-Run-#4-Auflösung — P-16 Floor-Semantik (Env exportiert 16000; geerbte Werte <32000 angehoben, A/B 1698/5002, 6/6-Subshell-Test) + Status-Sync (Spec-Change-Log, log.md Re-Run-#2/-#3/-#4-Befunde: winpty/CLAUDE_BIN, P-17 Normalisierer 21/21, Prompt-Wortung)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-23 12:57:42 +02:00
Michael Tamse 90a01fed37 fix: Story 3.13 Re-Run-#3-Auflösung (P-16/P-17): CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 (16000-Default bricht E.3 ab, A/B-geprüft: 1698 vs 5002 Zeilen) + D.4/D-NEG Pfad-Normalisierung (Agent schrie 'wiki.alpha.md' via alter Prompt-Wortung; 21/21-Test grün) + Prompt-Pfad-Wortung präzisiert; Gate 906 Z., bash -n clean 2026-08-23 12:45:16 +02:00
Michael Tamse 35b5939df6 docs: Story 3.13 §B-Käfig-Kommentar auf Ist-A.3 angepasst (stale V-5-Klammer entfernt) 2026-08-23 09:38:56 +02:00
Michael Tamse 52312c254b fix: Story 3.13 Gate-Pre-Runs (2. Re-Run-Ablage): A.3 auf vertraglich definierte Bundle-Prämissen (V-1 Bundleroot, raw/, log.md — V-5/V-6-Area-Menge existiert nicht im Vertrag), D-NEG-Käfig gültig (Bundleroot-/Area-Frontmatter, EC-1-existierende raw/-Ressourcen, wiki/-Präfix-Toleranz im Count); log.md-D-NEG-Punktbezeichnung korrigiert (Punkt 1/9/14-EC-3) 2026-08-23 09:02:30 +02:00
Michael Tamse 50f3628df4 fix: Story 3.13 Review-Loop-1-Auflösung (D-3.13-1..7 = 1/1/2/1/1/1/1, P-1..P-15; Status-Revert in-progress, Gate gehärtet 839 Z., Status-Sync spec/sprint-status/log.md; VALIDATOR_DEFIZIT-Matrix-Zeile = Renegotiation-Kandidat) 2026-08-23 08:51:34 +02:00
Michael TamseandClaude 894ae346ab docs: Story 3.13 Spec-Frontmatter nachziehen — status in-progress → done (Abschluss-Sync)
Commit 6624e07 hat den Step-05-Status-Sync (sprint-status.yaml 3-13 → done,
wiki/log.md Story-3.13-Eintrag, epic-3-context.md Ist-Nachführung,
deferred-work.md append) ausgeführt, aber das Spec-Frontmatter selbst vergessen
(status blieb 'in-progress'). Der Nachzieher flippt es gemäß Präzedenz der
abgeschlossenen Epik-3-Stories (3.4/3.8/3.11/3.12 → status: 'done') auf done;
epic-3 bleibt in-progress bis zur Epic-3-Retrospektive (AC-9).

Überspringt das Zwischenstadium 'in-review' (kein Review-Start per Step-04 mehr
nötig — Review-Loop war bereits in 6624e07 abgeschlossen; der ungetrackte
in-review-Halbzustand war der Auslöser des G-8-Porcelain-HARD-FAIL beim
Gate-Re-Run).

G-8-Nachbedingung wiederhergestellt: git status --porcelain leer nach diesem
Commit.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-22 20:06:34 +02:00
Michael Tamse 6624e07d3b docs: Story 3.13 abschließen — Status-Sync nach grünem Epic-3-Abnahmegate (PASS_COUNT=70, RUN_OK)
Step-05-Sync nach RUN_OK/Exit 0 des Story-3.13-Gates:
- sprint-status.yaml: 3-13-... in-progress → done (finaler Flip; epic-3 bleibt
  in-progress bis zur Epic-3-Retrospektive, AC-9), last_updated → 08-22-2026 19:10.
- epic-3-context.md: Goal (Z.7), Abnahmegate-Entscheidung (Z.46), Cross-Story-
  Abhängigkeit (Z.52) auf Ist-Zustand (Gate ausgeführt und grün) nachgeführt.
- wiki/log.md: Story-3.13-Eintrag (AC-6, Kanonisches Log, Vertrag §5) — Gate-Lauf,
  Abschnitte A-G, 12 Sandbox-Sub-Runs, Validator-Agent 7/7 SUCCESS, Zwei-frische-
  Agenten A/B byte-identisch nur at-Ausnahme, G-1..G-8, Porcelain-Check, Befund-
  Fixes (Punkt-9-Literal de-literalisiert, G-7-Exclusion). Kein Punkt-9-Literal,
  keine ../schema|../adapters-Links (G-7/Validator-Konformität).
- deferred-work.md: append — Story-3.13-Aufgegriffen-Block (Home der 3.13-namigen
  Defer L551/L560/L576/L594/L597/L611/L629/L633 realisiert; Original-Defer-Blöcke
  unverändert, benannte Rest-Terme offen).

Keine Änderung an schema/, adapters/, raw/ (AD-3); keine neue §7-Klasse; kein
Standalone (D-3); keine Wanduhr-Steuerung (A0-20). Gate-Nachweis: PASS_COUNT=70,
FAILED=0, RUN_OK, git status --porcelain leer.
2026-08-22 19:13:07 +02:00
Michael TamseandClaude 584676e387 feat: Story 3.13 Gate-Härtung — G-3 Punkt-9/Bundle-Compliance + G-7-Smoke-Exclusion (index.md/log.md)
Gate-Lauf 6 (E-Kern grün, A/B byte-identisch nur at-Ausnahme, 60 PASS) deckte
zwei reale Befunde auf:
- G-3: frischer Validator-Agent (schema/validator.md Rev 9) reportet 1 FAIL —
  wiki/log.md Z.42 (Story-2.4-Review-Eintrag) zitiert das Literal 'type: bundle'
  in Prosa = Verletzung Punkt 9 (Verbot außerhalb der Bundleroot, irgendwo im
  Dateiinhalt, schließt log.md ein). Validator ist korrekt; Defekt liegt im
  Bundle. Fix: minimalistisch, bedeutungserhaltend de-literalisiert
  (Bundleroot-'type'-Verbot) — Bundle ist jetzt Punkt-9-valid.
- G-7: Smoke-Flag war überstreng — Bundleroot index.md trägt die 3 gepinnten
  ../schema/-Glossar-Links (compiler §5.6 'andere Schicht', AC-8 optionale
  Provenienz/Traceability) und log.md zitiert Formel-Texte (§5.6 Pkt. 2
  Log-Exclusion). Fix: G-7 exkludiert index.md (Schema-Glossar) + log.md
  (Protokoll), hält die Smoke-Pflicht für alle Concept-Bodies/Area-Indizes hart.

Keine Schema-/Validator-/Adapter-/raw-Änderung (AD-3); keine §7-Klasse.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-22 18:08:30 +02:00
Michael Tamse 02786f43e7 docs: Story 3.13 starten — Spec 3-13, Sprint-Status auf in-progress 2026-08-22 10:37:21 +02:00
Michael TamseandClaude c234fc361e fix: Story 3.12 Review-Loop-2-Abschluss (bmad-code-review 4 Layer, konvergiert; D-3.12-1 Option 1, 7 Patches, 2 Defer, 6 dismissed; Sandbox L-1..L-9 9/9 harte PASS/Exit 0)
- D-3.12-1 (Nutzer, Option 1): AC-6/§5.18-Pkt.-6-Grenze an log.md-Praxis — dokumentierte
  Run-Nachweise = notwendige Koordinationsereignisse; §5.18/AC-6 textuell unverändert;
  L-8-Guard auf Kategorie-Hyphenate + Negativ-Kontrolle geschärft
- P1: scopelock_healthy() + LOCK_READ_ERROR-Propagation (korrupter Lock nie en-bloc-stale)
- P2: Release-Fehler negativ geübt (L-7), L-6 "valide committet" hart verifiziert
- P3: Setup-Robustheit (Exit-Checks, verifizierter $BASE, Backup-cp byte-identisch)
- P4: L-9 Ghost-Diff-Probe (Index+Worktree), reg_write real geübt
- P5: AC-1-Ownership-Stale positiv getestet, L-2 Takeover-Exactly-once am CAS
- S1: Spec-Struktur (review_loop_iteration 0->1, </frozen-after-approval>, doppelte
  Change-Log-Überschrift entfernt), Status-Sync vereinheitlicht
- S2: SRO-/Code-Map-/deferred-work-Anker auf Ist-Zeilen (takeover Z. 153, L-2 Z. 367,
  L-3 Z. 442, L-7 Z. 648, L-8 Z. 700; §5.18 Z. 426-439, §6 Z. 441, Rev 3.7 Z. 543)
- Defer: DF1 (AC-3-stash-Variante + AC-5 kombiniert, Home 3.13), DF2 (assert_no_wallclock)
- Status: Spec status done, sprint-status Key 3-12 -> done, last_updated 08-22-2026 07:44;
  wiki/log.md Loop-2-Abschlusseintrag (## 2026-08-22)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-22 08:14:56 +02:00
Michael TamseandClaude efcc469f3d docs: Story 3.12 abschließen — Spec done, Suggested Review Order, Status-Sync (Review-Loop-1 konvergiert, §5.18 Rev 3.7)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 21:24:41 +02:00
Michael TamseandClaude d0cff08a2a fix: Story 3.12 Review-Loop-1-Patches (atomarer Ownership-CAS scopelock_takeover, AK-2->AC-2, L-2-Negativ-CAS, Sandbox-Härtung; L-1..L-9 9/9 harte PASS/Exit 0)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 21:22:36 +02:00
Michael TamseandClaude 7f839febab feat: Story 3.12 — Lease-Lifecycle und Commit-Abschluss transaktional schließen (Revision 3.7, §5.18; Sandbox L-1..L-9 9/9 harte PASS/Exit 0)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 20:49:50 +02:00
Michael TamseandClaude 80480af3ec feat: Story 3.11 — Review-Loop-1-Abschluss committen (20 Patches, D-3.11-1/2, Sandbox A-1..A-8 8/8)
Abschluss des in der vorigen Session erarbeiteten, nach Commit e02cf84
liegengebliebenen Review-Loop-1 von Story 3.11 (Root-Scope-Leasing atomar):

- wiki/log.md: Eintrag „Story 3.11 → Review-Loop-1-Abschluss + done"
  (D-3.11-1 Konstruktiv+härten, D-3.11-2 Intentionaler Mutationsversuch;
   20 angewendete Patches; kein Loopback)
- sprint-status.yaml: Key 3-11-… → done (finaler Step-05-Flip), last_updated 15:10
- schema/compiler.md: §5.17-Pkt.-1/4/5-Berichtigungen + §8-Revision-3.6-Nachführung
- spec-3-11 + sandbox-3-11: 20 Loop-1-Patches; deferred-work.md: 5 Defer-Einträge
  + KORREKTUR-Teileintrag (Zeilen-Drift-Defer widerlegt)

Erhaltungs-Invariante §5.9 Pkt. 5: git status --porcelain -- wiki/ zeigt nur
wiki/log.md; AD-3 read-only; kein Standalone, keine neue §7-Klasse.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 20:00:21 +02:00
16 changed files with 4475 additions and 169 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,115 @@
---
title: "Arbeitsauftrag — compiler.md Rev 3.7 → 3.8 (Epic-1-Remediation, Story 3.13): CREATE-Slug + log-Determinismus-Patch"
status: AUSGEFÜHRT (Teilumsetzung) — B2 (log-Form-/Wanduhr-Pin) + A/B-Umfang (b) angewendet, Rev 3.8 committet; B2-Reihenfolge-Hälfte + B1 (Slug) = offene Defers; C (Body-Byte) = zurückgewiesen (FR-2). Siehe §0.1 Ausführungsstatus.
date: 2026-08-23
owner: ProMods
related: spec-3-13-epic-3-verifikations-und-abnahmegate.md, spec-3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato.md, deferred-work.md
---
# Arbeitsauftrag — compiler.md Rev 3.7 → 3.8 (Epic-1-Remediation, Story 3.13)
**Stellenwert:** Dies ist der **Autorisierungs-Träger** der separat autorisierten Epic-1-Remediation (AC-4-Pfad; Präzedenz Validator-Rev-8/9, AD-3-Compliance: keine stille Schema-Semantik-Änderung durch das Gate).
## 0.1 Ausführungsstatus (2026-08-23, Nutzer-Autorisierung „continue" — superset des §0-Empfehlungs-Vorschlags (b))
Dieser Entwurf legte drei Bausteine fest: **B1** (CREATE-Slug-Signal), **B2** (log-Determinismus), **C** (CREATE-Body-Byte-Residual, an die A/B-Umfangs-Entscheidung (a)/(b)/(c)/(d) gebunden). Nach der exakten Zeilen-Nachzähl des Re-Run-#5-Befunds (NON_AT=26 AT=4; 24/26 = 92 % CREATE-/Slug-getrieben) und der Nutzer-Autorisierung („continue" auf den empfohlenen Minimal-Änderungssatz) wurde **ausgeführt**:
| Baustein | Status | Umsetzung |
|---|---|---|
| **B2** — kanonische, byte-deterministische `log.md`-Eintragsform + Reihenfolge-Pin | 🟡 **TEILANGEWENDET** (Rev 3.8; Reihenfolge-Hälfte = offener Defer, D-3.13-L2-1) | **Angewendet (Rev 3.8):** die kanonische Eintragsform + kein Wanduhr-at im Body — §5.9 Pkt. 4 (Update), §5.1 Pkt. 4 (Anlage), §5.10 Pkt. 7 (Synthese-Anlage) + §8 Revisionslog. Form = `- <Operation>: <concept> (<quellen>; Baseline <SHA>)`, lex (LC_ALL=C) sortiert, ` + ` getrennt, voller SHA, **kein** freier Zusatztext, **kein** Wanduhr-at im Body (Wanduhr ausschließlich im Frontmatter-`generated.at`, §5.14 Pkt. 3). **Nicht angewendet (offener Defer, Review-Loop-2 D-3.13-L2-1):** der Entwurf-B2-Hälfte „**Reihenfolge pinnen**" (Reihenfolge der Log-Einträge *innerhalb* einer `## YYYY-MM-DD`-Datumsgruppe) ist **nicht** in compiler.md gelandet (grep 0 Treffer „Innerhalb einer Datumsgruppe"/Tie-Break) — nur die Form-/Wanduhr-Hälfte wurde umgesetzt. Latent (3× A/B-`log.md` byte-identisch, noch nie rot); Defer in deferred-work.md. |
| **A/B-Umfang (b)** — CREATE aus der A/B-Fixture nehmen (D-3.13-6 Option 1 → Option 2) | ✅ **ANGEWENDET** (Gate) | `run-sandbox.sh` E-Sektion: E.1 (keine beta-Quellen), AGENT_PROMPT (beta-Fakt entfernt), E.5 (CREATE-Witness entfernt, UPDATE-`alpha` bleibt), E.8 (index.md unverändert, IDX_ADDED=0), Header. A/B prüft den Update-/Erhaltungs-Kern; CREATE/Synthese gelten über Sub-Runs 3-3/3-4. |
| **B1** — CREATE-Slug-Signal (Inhalts-Term maßgeblich) | ⏸️ **DEFER** (offen) | **Nicht** in Rev 3.8 — der CREATE-Fall steht nicht mehr in der A/B-Fixture, daher ist der Slug-Pin für den grünen Re-Run #6 **nicht** erforderlich. Als **benannter Defer / Ask-First-Kandidat** dokumentiert (in compiler.md Rev 3.8 + Spec Change Log); §5.15 Pkt. 1 (Dateiname→Term) / §5.7 bleiben wie ist. |
| **C** — CREATE-Body byte-deterministisch | ❌ **ZURÜCKGEWIESEN** (FR-2) | Q1-Item (2) = Option (c): ein Byte-Pin des CREATE-Body widerspricht §5.1/FR-2/§5.10 Pkt. 3 („Befund-Äquivalenz, **nicht** Wort-Identität") direkt — Semantik-Change, kein Bugfix. Bewusst **nicht** ausgeführt. |
**Konsequenz für Re-Run #6:** erwartetes E.4-Ergebnis `NON_AT=0 AT=2` (`alpha.md` at-only; `log.md` byte-identisch via gepinnter Form; `gamma.md`/`index.md` unverändert). **Nicht berührt:** frozen I/O-Matrix-Block (inkl. `GENERATED_AT_AUSNAHME`), §5.14 Pkt. 3 (at-Ausnahme = Feldwerte), §5.10 Pkt. 3 (Body-Wortlaut), §5.1 Pkt. 2 (FR-2). **Commit:** `7c3c19b` (compiler.md Rev 3.8 + Gate + dieser Autorisierungs-Träger). **Ausstehend:** 12-Sandbox-Re-Execution, Re-Run #6 (grüner Voll-Lauf), Review-Loop-2 + finaler `done`-Flip (Step-05-Status-Sync nach grünem Re-Run #6).
**Unten:** der ursprüngliche Entwurfstext (B1 §1, B2 §2, C §3) bleibt als **historische Entscheidungsgrundlage** erhalten — die ausgeführte Form steht in §0.1; wo B1/B2 im Folgenden als „ready-to-apply" beschrieben sind, gilt: B2 **teil**angewendet (Form-/Wanduhr-Hälfte ja; **Reihenfolge-Hälfte nicht** — offener Defer D-3.13-L2-1, Review-Loop-2), B1 nicht angewendet (Defer).
## 0. Prämisse-Korrektur (warum dieser Entwurf anders aussieht als die Q1-Prämisse)
Die Q1-Autorisierung („(1) CREATE-Slug-Signal pinning, (2) CREATE-Body-kanonische Form (byte-deterministisch), (3) log.md-Regel") beruhte auf dem Befund „26 NON_AT-Zeilen = freie CREATE-Synthese". Gegen den **realen A/B-Diff** des Re-Run #5 (`/tmp/gate-313-run5.log`, Z. 12401330) aufgerechnet ist das **invertiert**:
| Quelle | NON_AT-Zeilen | Natur |
|---|---|---|
| `wiki/beta.md` (gelöscht) | 11 = 10 Frontmatter + 1 Body-Satz | Frontmatter **byte-identisch**; gezählt als delete **nur wegen Pfad `beta`** |
| `wiki/quanten-observatorium-kanal.md` (neu) | 11 = 10 Frontmatter + 1 Body-Satz | Frontmatter **byte-identisch**; gezählt als add **nur wegen Pfad `quanten-…`** |
| `wiki/index.md` | 2 (Link-Zeile) | **Slug-getrieben** |
| `wiki/log.md` | 4 | gemischt: Slug-Name + **Reihenfolge** + **Wanduhr-`at` im Body** + freier Wortlaut |
| **Summe** | **26** | — |
- **22/26 (85 %) sind Slug-getrieben** — die 20 Frontmatter-Zeilen sind inhaltlich identisch; git zählt sie als gelöscht+addiert **nur weil die Dateinamen divergieren** (`beta` vs. `quanten-observatorium-kanal`), plus 2 Index-Links.
- **Nur 2 Zeilen** sind die eigentliche freie CREATE-Prosa (ein Body-Satz pro Lauf); **4 Zeilen** log.md, davon **2 strukturell** (Reihenfolge ungepinnt + Wanduhr-`at` im Body) und 2 frei (Wortlaut).
**Quelltext-Verankerungen (Zeilen, `schema/compiler.md`):**
- `§5.14 Pkt. 3` (L375): Zwei-Run-Vergleich lässt **genau eine** benannte Differenz zu (`generated.at`); **alle übrigen** Bestandteile **byte-identisch**.
- `§5.10 Pkt. 3` (L297): CREATE-/Synthese-Body-**Reihenfolge** deterministisch gepinnt (lex nach Beleg-Anker, LC_ALL=C); **Wortlaut** nicht — dort explizit „Befund-Äquivalenz, **nicht** Wort-Identität".
- `§5.1 Pkt. 2` (L84) + `§5 Pkt. 3` (L34, FR-2): Body **eigenständig formuliert**; „Bloße Kopie … DÜRFEN nicht erzeugt werden"; „Der Body darf keine großen Quell-Exzerpte enthalten".
- `§5.14 Pkt. 2` (L374) + spec-3-8 P-5 (L150): 3.8-Basis **nur mechanisch simuliert** (Subshell-Läufe), **nur UPDATE-Pfad**, **nie** echte frische LLMs; „der Nachweis echter frischer Kontexte erfolgt im Story-3.13-Abnahmegate über reale Agent-Läufe".
**Konsequenz:** Q1-Item (1) **Slug** und (3) **log-Struktur** sind **FR-2-sicher, determinismus-pflichtig (routing/log sind ohnehin deterministisch, §5.7/§5.15/§5.10-AD-17h) und frozen-Block-unabhängig****B1/B2 unten, ready-to-apply**. Q1-Item (2) **CREATE-Body byte-deterministisch** ist **nicht FR-2-sicher** (Byte-Pin bricht `§5.1`/FR-2/`§5.10 Pkt. 3`) → **C unten, an die (a)/(b)/(c)/(d)-Entscheidung gebunden**. Der latente Kern: `§5.14 Pkt. 3` (Byte-Identität) vs. `FR-2`/`§5.10 Pkt. 3` (freies, nicht-wortidentisches Wortlaut) — in 3.8 durch Simulation verdeckt, in 3.13/E.4 aufgedeckt.
## 1. B1 — CREATE-Slug-Signal: Inhalts-Term maßgeblich (FR-2-sicher, ready-to-apply)
**Problem (AD-16-Instruktionslücke):** Bei einem CREATE, dessen `raw/`-Dateinamen-Stamm vom erkannten Inhalts-Term abweicht (Fixture: `raw/beta-v1.md`/`beta-v2.md`, Stamm `beta`, Inhalts-Term `quanten-observatorium-kanal`), ist das **maßgebliche Slug-Signal nicht deterministisch fixiert**:
- `§5.1 Pkt. 1` (L83): „kebab-case-Slug aus der Concept-Identität … **Der Dateiname definiert die Concept-Identität** (relativer OKF-Pfad ohne `.md`, AD-7a)" — für ein **neues** Concept zirkulär (Identität = OKF-Pfad, der noch nicht existiert).
- `§5.15 Pkt. 1` (L383): „dieser kollabierte, lowercasene **Dateiname** ist der **primäre Term** je Zuwachs-Datei" — leitet den Term aus dem **Dateinamen** ab (→ `beta`).
- Design-Notes (spec L150): intendiert das Ergebnis als **term-geleitet** (→ `quanten-observatorium-kanal`).
Lauf A (`wiki/beta.md`) folgte §5.15/Dateinamen; Lauf B (`wiki/quanten-…`) folgte der term-geleiteten Intention → Divergenz = **22/26 NON_AT-Zeilen**.
**Patch (Bestandsregel, kein neues Prädikat/Kein §7-Key/kein Frontmatter-Key, AD-3/D-3-konform):** Der **CREATE-Dateiname** leitet sich vom **erkannten Inhalts-Term** (deterministisch erkannte Wissenseinheit, `§2`-Interpretation/`§3.2`) ab, nicht vom `raw/`-Dateinamen-Stamm; der Dateinamen-Stamm ist nur **Fallback**, wenn er mit dem Inhalts-Term übereinstimmt oder kein abgrenzbarer Inhalts-Term erkannt wird. Abgrenzung: das betrifft **ausschließlich die CREATE-Ziel-Namensgebung** (Zelle 2, `§5.15 Pkt. 3`); der **Reconcile-/Match-Term** (`§5.15 Pkt. 1`) bleibt **textuell unverändert** (er dient dem Match auf *bestehende* Concepts, nicht der Namensgebung neuer).
**Vorgeschlagener Wortlaut — `§5.1 Pkt. 1` (L83), Bullet 2 ersetzt:**
> **Vorher (L83):**
> - Konvention für den Dateinamen: kebab-case-Slug aus der Concept-Identität (kein Sonderzeichen, keine Endung `.md`-Dopplung). Der Dateiname definiert die Concept-Identität (relativer OKF-Pfad ohne `.md`, AD-7a).
> **Nachher (Vorschlag):**
> - Konvention für den Dateinamen: kebab-case-Slug (kein Sonderzeichen, keine Endung `.md`-Dopplung). Der Dateiname **definiert** die Concept-Identität (relativer OKF-Pfad ohne `.md`, AD-7a); bei **Neu-Anlage** (CREATE, `§5.15 Pkt. 3` Zelle 2) **entsteht** der Dateiname **deterministisch aus dem erkannten Inhalts-Term** der neuen Wissenseinheit (`§2`-Interpretation/`§3.2`; Kollaps-/Lowercase-Normalisierung `§3.2 Pkt. 1b`/`§5.15 Pkt. 1`) — **term-geleitet**. Der `raw/`-Zuwachs-Dateinamen-Stamm ist nur **Fallback**, wenn er mit dem erkannten Inhalts-Term übereinstimmt **oder** kein abgrenzbarer Inhalts-Term erkannt wird. Weichen Dateinamen-Stamm und erkannte Inhalts-Term ab, **gewinnt der Inhalts-Term** (deterministisch; Design-Intention term-geleitet) — die CREATE-Namensgebung ist damit **byte-deterministisch aus dem committeten Zustand** ableitbar (AD-17h/A0-19); eine Abweichung zweier Runs im CREATE-Slug ist ein AD-16-Klassifikationsdefekt (`§5.14 Pkt. 4`), kein Rauschen.
**Revisionslog (`§8`):** Eintrag Rev 3.8 — `§5.1 Pkt. 1` CREATE-Slug-Signal deterministisch (Inhalts-Term maßgeblich, Dateinamen-Stamm nur Fallback); kein Prädikat-/§7-/Frontmatter-Key-Change (AD-3/D-3); `§5.15 Pkt. 1`-Match-Term unverändert.
## 2. B2 — log.md-Determinismus: Reihenfolge pinnen + kein Wanduhr-`at` im Body (FR-2-sicher, ready-to-apply)
**Problem (AD-16-Instruktionslücke, 2 der 4 log-Zeilen):** `log.md`-Einträge innerhalb einer Datumsgruppe haben **keine deterministische Reihenfolge**, und **Wanduhr-`generated.at`-Werte im Body** sind nicht untersagt. Lauf A legte die Einträge in einer Reihenfolge, Lauf B in einer anderen, und Lauf B bettete `generated.at = 2026-08-23T13:48:23Z` in den log-Body ein — beides liegt **außerhalb** der benannten `at`-Ausnahme (die gilt nur für das **Frontmatter-Feld** `generated.at`/`verified[].at`, nicht für Body-Text; `§5.14 Pkt. 3`).
**Patch (Bestandsregel-Ergänzung, kein neuer Key):**
1. **Innerhalb einer Datumsgruppe** (`Header = YYYY-MM-DD`) werden die Einträge in **deterministischer Reihenfolge** geführt: lexikografisch nach **Concept-Pfad** (LC_ALL=C), Tie-Break nach **fixer Operations-Reihenfolge** (Neu-Anlage → Update → Synthese-Update → Verwaist/Hold). Die Datumsgruppen selbst bleiben „neueste zuerst" (unverändert, `§5.1 Pkt. 4`).
2. **Wanduhr-Werte im Body sind verboten:** ein `log.md`-Eintrag trägt **nur** die Datumsgruppen-Header-Zeile, das `<Baseline-Commit>` und ggf. den Beleg-/`sources`-Pfad — **kein** Wanduhr-`generated.at`-/`verified[].at`-Wert als Body-Text (der `at`-Wanduhr-Gap lebt ausschließlich im Frontmatter-Feld, `§5.14 Pkt. 3`/A0-20). Ein Eintrag mit Wanduhr-Body-Wert ist ein textuell benannter Instruktions-Verstoß (NFR-4).
**Vorgeschlagener Wortlaut — `§5.1 Pkt. 4` (L87) am Ende ergänzen:**
> **Nachher (Vorschlag, an L87 anfügen):**
> … `log.md` bleibt ohne Frontmatter (Punkt 10). **Innerhalb einer Datumsgruppe** (Header `YYYY-MM-DD`) werden die Einträge **deterministisch** geführt: lexikografisch nach **Concept-Pfad** (LC_ALL=C), Tie-Break nach **fixer Operations-Reihenfolge** (Neu-Anlage → Update → Synthese-Update → Verwaist/Hold) — gleiche Eingabemenge ⇒ identische Eintrags-Reihenfolge (AD-17h/A0-19, `§5.14`). Ein `log.md`-Body-Eintrag trägt **keinen** Wanduhr-`generated.at`-/`verified[].at`-Wert als Text (der `at`-Wanduhr-Gap lebt ausschließlich im Frontmatter-Feld, `§5.14 Pkt. 3`/A0-20); ein solcher Body-Wert ist ein textuell benannter Instruktions-Verstoß (NFR-4).
**Konsistenz:** identisch in `§5.10 Pkt. 7` (L301, Synthese-`log.md`-Eintragspflicht) spiegeln — Verweis auf die `§5.1 Pkt. 4`-Reihenfolge-/Wanduhr-Regel (eine Regel, zwei Verankerungen, Fugen-Identitäts-Präzedenz).
**Revisionslog (`§8`):** Eintrag Rev 3.8 — `§5.1 Pkt. 4`/`§5.10 Pkt. 7` log-Determinismus (intra-Date-Gruppe-Reihenfolge + kein Wanduhr-at im Body); kein Prädikat-/§7-/Frontmatter-Key-Change.
## 3. C — CREATE-Body-Byte-Residual (AN NUTZER-ENTSCHEIDUNG (a)/(b)/(c)/(d) GEBUNDEN — NICHT VORFREIGEBEN)
Nach B1+B2 bleibt das **eigentliche** freie-Prosa-Residual: **1 CREATE-Body-Satz pro Lauf + ~12 log-Wortlaut-Zeilen**. Zwei frische LLMs schreiben diesen freien, FR-2-mandatierten, „eigenständig formulierten" Satz **nie byte-identisch** — und `§5.14 Pkt. 3` verlangt Byte-Identität. Das ist der latente Vertragskonflikt. **Q1-Item (2) „CREATE-Body kanonische Form (byte-deterministisch)" ist genau dieser Teil und ist NICHT FR-2-sicher** (Byte-Pin bricht `§5.1`/FR-2/`§5.10 Pkt. 3`). **Dieser Abschnitt darf erst nach der Nutzer-Entscheidung umgesetzt werden.** Optionen (frozen-Block/Ask-First-Berührung gekennzeichnet):
- **(a) A/B auf Wissens-Ebene für freien Body** *(frozen-Matrix-Zeile `GENERATED_AT_AUSNAHME`, spec L49 + AC-7)***empfohlen**, weil vertragself-konsistent (`§5.10 Pkt. 3` sagt bereits „Befund-Äquivalenz, nicht Wort-Identität"): der freie CREATE-Body-Wortlaut wird als **zweite benannte Ausnahme-Klasse** neben `at` behandelt; **Struktur** (Slug/Frontmatter/`sources`/Provenanz-Anker/Claim-Abdeckung/Index-Link) bleibt **byte-streng**, freier **Wortlaut** per Claim-/Provenienz-Abdeckung (nicht byte-weise) verglichen. CREATE bleibt in der A/B-Fixture (AC-6 wie geschrieben). Berührt `§5.14 Pkt. 3` (Ausnahme-Menge um zweite Klasse erweitern) + frozen spec L49.
- **(b) CREATE aus A/B-Fixture nehmen** *(revidiert D-3.13-6 Option 1 → 2)* — A/B prüft nur den nachweislich byte-deterministischen UPDATE-Pfad; CREATE + Synthese gelten über Sandbox-Sub-Runs 3-3/3-4 als demostriert. AC-6-Auslegung: „Anlage neuer Wissenseinheit" über die Sub-Runs. Vertrag/frozen-Block/FR-2 unangetastet, kleinster Pfad; CREATE nicht mehr im echten frischen-Agenten-A/B-Nachweis.
- **(c) Kanonischer CREATE-Body** *(substanzieller compiler.md-Change)* — CREATE-Body auf aus `raw/` mechanisch abgeleitete Form pinnen (exakte Wortfolge der zuerst-belegenden Quelle, anchor-geordnet). Macht CREATE byte-determinisch, A/B bleibt streng. **RISIKO: bricht vmtl. FR-2** („eigenständig formuliert, keine Kopie"); eigener breiterer Autorisationsrunde würdig.
- **(d) Teil-Patch liefern, Residual rot lassen** — B1+B2 als Epic-1-Remediation geliefert (beseitigt die 22/26-Slug-Hauptursache — echter, dokumentierter Fortschritt); das kleine Prosa-Residual als **benannter Defer** an spätere Vertrags-Revision/Epic 4 → Gate bleibt rot auf einem kleinen, ehrlich verstandenen Rest; Story 3.13 in-progress.
## 4. Verifikationsplan (nach Anwendung der jeweils autorisierten Untermenge)
1. `bash -n` + Voll-Lesedurchgang `schema/compiler.md` (keine Fugen-Identitäts-Brüche; alle `§`-Verweise intakt).
2. **12 Sandbox-Suiten re-executieren** (Sub-Runs, fail-fast; `sandbox-3-1..3-12`, je `exit 0`).
3. **Re-Run #6: grüner Voll-Lauf des Gates** (`_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh`, ~2.53.5 h, Background-Subagent, `CLAUDE_BIN` = echte winpty-freie `claude.exe` per P-3-Override, P-16-Floor 32000).
4. **Erwartetes A/B-Ergebnis je Option:**
- **(a):** E.4 `NON_AT=0` (oder `NON_AT` nur in explizit als zweiter Ausnahme-Klasse ausgewiesenen Body-Wortlaut-Zellen), `AT=4`**grün**; F/G laufen (G-1..G-8 + Porcelain).
- **(b):** E.4 `NON_AT=0 AT=4` (nur UPDATE-Pfad `alpha` + `gamma`/`index`-Erhaltung) → **grün**; CREATE nicht in A/B (über Sub-Runs 3-3/3-4 belegt).
- **(c):** E.4 `NON_AT=0 AT=4` (CREATE-Body byte-deterministisch) → **grün** (vorausgesetzt FR-2-Verstoß als Semantik-Change autorisiert).
- **(d):** E.4 `NON_AT` > 0, **nur** im kleinen Prosa-Residual → Gate **rot**, aber auf einem ehrlich isolierten Rest; Befund textuell benannt (NFR-4), Defer angelegt.
5. **AD-3-Porcelain:** `git status --porcelain` des realen Ist-Baums nach Lauf leer; `schema/`-Diffs **nur** die autorisierten Rev-3.8-Änderungen; `raw/`/`adapters/`/`validator.md`/`wiki-compiler.md`/`canonical-terms.md` unverändert.
## 5. Freigabe-Punkte (Nutzer)
- [ ] **B1 (Slug)** autorisieren? — FR-2-sicher, Q1-item(1), kein frozen-Block.
- [ ] **B2 (log-Determinismus)** autorisieren? — FR-2-sicher, Q1-item(3), kein frozen-Block.
- [ ] **C (Body-Residual)** = Option **(a)** / **(b)** / **(c)** / **(d)**? — (a) berührt frozen spec L49 + `§5.14 Pkt. 3`; (b) revidiert D-3.13-6; (c) breitere Autorisation (FR-2); (d) Defer.
- [ ] danach: 12-Sandbox-Re-Execution + Re-Run #6 (Task #4/#5).
@@ -589,3 +589,85 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
- source_spec: `spec-3-11-root-scope-leasing-atomar-akquirieren.md`
summary: Die Helfer-Funktion `scopelock_header_banner() { :; }` in der Sandbox-3.11 ist ein leerer Platzhalter.
evidence: run-sandbox.sh Z. 166 definiert `scopelock_header_banner() { :; } # (placeholder für Vereinheitlichung mit Story-3.5-Konvention)` — die Funktion wird nie aufgerufen und trägt nichts bei; kosmetisch. Home: Story-3.12-Lifecycle-Erweiterung, falls die Banner-Konvention übernommen wird, sonst entfernen.
- source_spec: `spec-3-11-root-scope-leasing-atomar-akquirieren.md`
summary: Sandbox-3.5/-3.6 üben weiterhin check-then-act-Lockfile-Akquise (clone-lokal, `lease/<area>/<id>.lock`) ohne den §5.17-Scope-Lock; die von der Story-3.11-Code-Map benannte „Ableitungspflicht des neuen atomaren Lock-Modells“ ist nicht vollzogen.
evidence: Repo-weiter Grep: `akquire()`-check-then-act existiert nur in sandbox-3-5 (L1/L2/L5, Exklusivität nur für identische `<id>`-Lockfile-Pfade) und sandbox-3-6; `refs/leases`/§5.17 kommen nur in den sieben Story-3.11-Artefakten vor. Kein Test koppelt zwei Producer mit *verschiedenen* `<id>` im selben Root-Scope an genau einen §5.17-Gewinner. Home: Story-3.12-Lifecycle / Story-3.13-Abnahme.
- source_spec: `spec-3-11-root-scope-leasing-atomar-akquirieren.md`
summary: A-2 garantiert keine zeitliche Überlappung der zwei Prozesse (kein Synchronisations-Barriere); ein Scheduler kann sie vollständig sequenzieren — genau die „Zeitversatz ohne Überlappung“-Situation, die die Spec als nicht beweiskräftig für Atomarität ausschließt (die create-only-Atomarität selbst wird davon nicht berührt).
evidence: run-sandbox.sh A-2: die zwei Subshells werden ohne Barriere gestartet (Z. 231-233); der Sampler taktet 6×`sleep 0.002` (Wanduhr-Imperativ im Spannungsfeld der A0-20-Rahmung). Home: Sandbox-Härtung / Story-3.13-Abnahme (echte Überlappung z. B. via gemeinsamer Ready-Signal-Datei/`git update-ref`-Pacing).
- source_spec: `spec-3-11-root-scope-leasing-atomar-akquirieren.md`
summary: Status-Kontraktion Spec-Frontmatter `status: 'done'` vs. `sprint-status.yaml` `review` wiederholt das Story-3.9-Muster (D-3.9-2), ohne den Präzedenz-Verweis im Eintrag zu benennen.
evidence: spec-3-11-Frontmatter `status: 'done'` (entpricht der 3.9-/3.10-Präzedenz im Implementierungs-Commit); finaler `done`-Flip erfolgt im Step-05-Status-Sync nach konvergiertem Review-Loop (Präzedenz D-3.9-2). Entwarnung: kein Defekt, Nachweis-Verweis nachführen.
- source_spec: `spec-3-11-root-scope-leasing-atomar-akquirieren.md`
summary: Die I/O-Matrix (frozen) listet 4 Szenarien, die Sandbox übt 8 (A-5 Schlüssel-ohne-Run-ID, A-6 späte sequentielle Abweisung, A-8 Freigabe-erneut ohne Matrix-Zeile/Expected-Output); dazu der leere `scopelock_header_banner()`-Platzhalter.
evidence: Spec-I/O-Matrix Z. 587-591 (4 Zeilen) vs. run-sandbox.sh A-1..A-8; Task-2 formuliert „Tests der I/O-Matrix und der 5 ACs“. Home: Matrix-Nachführung (Append außerhalb frozen Blocks über den Spec-Change-Log) / Story-3.12.
- KORREKTUR (Review-Loop-1, 2026-08-21): Der obere Defer „Zeilenanker im Code Map-Verweis auf §5.11 (Z. 304-321)/§5.12 (Z. 323-342) nicht mehr aktuell“ ist **faktisch falsch** — bei e02cf84 liegt `## 5.11` weiterhin exakt auf Z. 304 und `## 5.12` auf Z. 323 (verifiziert via `grep -n "^## 5.11\|^## 5.12"`); es existiert kein Zeilen-Drift an diesen Anker. Der Defer wird hiermit re-traktiert (append-only-Korrektur, Original bleibt erhalten); die realen Fehlanker sitzen in der Suggested-Review-Order (`compiler.md:415/527`, `log.md:47`, `deferred-work.md:577`) — als Patch-Item in der Spec-Review-Findings geführt.
### Aufgegriffen: Sandbox-3.5/-3.6-check-then-act-Abgleich mit dem §5.17-Scope-Lock (Story-3.11-Defer, Home Story 3.12) — Story 3.12, 2026-08-21
- Bezug: Defer `Sandbox-3.5/-3.6 üben weiterhin check-then-act-Lockfile-Akquise (clone-lokal) ohne den §5.17-Scope-Lock; Ableitungspflicht des neuen atomaren Lock-Modells nicht vollzogen` (Defer-Block „Deferred from: code review of spec-3-11 …", Eintrag der Z. 593-594; Home: Story-3.12-Lifecycle / Story-3.13-Abnahme).
- Umsetzung: Die Story-3.12-Sandbox übernimmt die §5.17-Modelle (scope-bezogener Lock `refs/leases/wiki`, create-only-Akquise, ownership-gebundener Ref-Delete) für den gesamten Lifecycle — `schema/compiler.md` §5.18 Pkt. 1/2/5: Liveness-/Ownership-Prüfung am Lock-Inhalt, stale-Übernahme als Ownership-CAS, durable Release per Ref-Delete nur durch Inhaber; die check-then-act-Lockfile-Akquise der Story-3.5/-3.6-Sandboxen bleibt als historische Übung unverändert bestehen (Fugen-Identität, keine Re-Negotiation).
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` L-1..L-9 (Lock als einziger Exklusivitätsschlüssel über den kompletten Lifecycle; Ownership-Prüfung in L-1/L-2 via `lease_liveness_stale`/Lock-Inhalt, Takeover via `scopelock_takeover`, Release per `scopelock_release` Ref-Delete in L-5/L-6/L-7/L-9) — Exit 0.
- status: aufgegriffen (Home erledigt in §5.18 Pkt. 1/2/5 und sandbox-3-12; Rest-Vertiefung — echte Zwei-Worktree-Überlappung mit Synchronisations-Barriere — bleibt Story 3.13-Abnahme)
### Aufgegriffen: Banner-/I-O-Matrix-Kosmetik (Story-3.11-Defer, Home Story 3.12) — Story 3.12, 2026-08-21
- Bezug: Defer `Die I/O-Matrix (frozen) listet 4 Szenarien, die Sandbox übt 8 … dazu der leere scopelock_header_banner()-Platzhalter` (Defer-Block „Deferred from: code review of spec-3-11 …", Eintrag der Z. 602-603; Home: Matrix-Nachführung (Append außerhalb frozen Blocks über den Spec-Change-Log) / Story-3.12).
- Umsetzung: Die Story-3.12-Sandbox führt die Banner-Konvention ein (nicht-leerer `scopelock_header_banner()` je Lifecycle-Run, Z. 310-312) und deckt die 12 I/O-Matrix-Zeilen der Story-3.12-Spec als harte Assertionen L-1..L-9 ab (LIVENESS_AKTIV, STALE_UEBERNAHME, DIRTY_GETRACKT, DIRTY_UNGETRACKT, ROLLBACK_NACH_MUTATION, ROLLBACK_NACH_COMMIT_VOR_RELEASE, RELEASE_DURABLE, KANONISCHES_LOG, KILLPUNKT_VOR_MUTATION, KILLPUNKT_NACH_MUTATION, KILLPUNKT_VOR_COMMIT, KILLPUNKT_NACH_COMMIT); die Matrix-Kosmetik-Nachführung der Story-3.11-Spec-Diskrepanz (4 vs. 8 Szenarien) bleibt als Append über den Story-3.11-Spec-Change-Log benannt.
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` L-1..L-9 (jede Matrix-Zeile als Szenario) — Exit 0.
- status: aufgegriffen (Banner-Konvention umgesetzt in sandbox-3-12; restliche Story-3.11-Matrix-Kosmetik bleibt benannter Defer)
### Aufgegriffen: `scopelock_header_banner()`-Platzhalter (Story-3.11-Defer, Home Story 3.12) — Story 3.12, 2026-08-21
- Bezug: Defer `Die Helfer-Funktion scopelock_header_banner() { :; } in der Sandbox-3.11 ist ein leerer Platzhalter` (Defer-Block „Deferred from: code review of spec-3-11 …", Eintrag der Z. 590-591; Home: Story-3.12-Lifecycle-Erweiterung, falls die Banner-Konvention übernommen wird, sonst entfernen).
- Umsetzung: Die Story-3.12-Sandbox übernimmt die Banner-Konvention und ersetzt den leeren Platzhalter durch eine dokumentierende Funktion (`scopelock_header_banner() { echo "--- Lifecycle-Run: $1 (Root-Scope wiki/, Lock refs/leases/wiki) ---"; }`, Z. 310-312) — je Lifecycle-Szenario wird der Banner als Run-Kopf ausgegeben (L-1, L-3..L-9). Die Story-3.11-Sandbox selbst bleibt — wo es keinen Platzhalter-Aufruf gibt — unverändert (Fugen-Identität).
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` L-1/L-3..L-9 (Banner-Ausgabe je Run) — Exit 0.
- status: aufgegriffen (Home erledigt in sandbox-3-12, Banner-Konvention umgesetzt)
- source_spec: `_bmad-output/implementation-artifacts/spec-3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen.md`
summary: Echte Zwei-Worktree-/Zwei-Prozess-Übernahme des Root-Scope-Locks mit Synchronisations-Barriere üben — die §5.18-Pkt.-2-Takeover-Atomarität (AC-2) ist in sandbox-3-12 nur sequenziell (ein Worktree) geprüft.
evidence: Verification-Gap-Review Story 3.12 (Loop 1): sandbox-3-12 läuft komplett in einem `git init`-Worktree; die §5.17-geteilte-Ref-Namespace-Sichtbarkeit und die Echt-Überlappung (Zwei Producer gleichzeitig, eine Entscheidung) ist nicht beobachtet; die vorhandene Rest-Vertiefung in der Story-3.11-Aufgegriffen-Notiz (Home §5.18 Pkt. 1/2/5) und der deferred-work-Eintrag zur Zwei-Worktree-Barriere (Sandbox-Härtung / Story-3.13-Abnahme) scopen dasselbe Restziel.
## Deferred from: code review of spec-3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen (2026-08-22)
- DF1 — AC-3-`git stash push`-Variante und kombiniertes AC-5 „Fremd-Bytes restauriert" nie geübt: §5.18 Pkt. 3 benennt `git stash push -- <Pfad>` bzw. `stash push -u` als zulässige native Alternative zur Scratch-Zone; die Sandbox L-3/L-4 übt ausschließlich die Scratch-Zweige (der Stash-Ast — Sichern, Worktree-Bereinigung, byte-identischer Restore aus dem Stash — hat keinerlei Coverage/Guard). Zusätzlich wird AC-5s Clean-Input-Guard-Klausel „geschützte Fremd-Bytes sind restauriert" nur isoliert in L-3/L-4 bewiesen, nie kombiniert mit Release + Folge-Run im selben Szenario. Home: Sandbox-Härtung (Story-3.13-Abnahme-Gate deckt native-Varianten-Abdeckung über echte Bäume).
- DF2 — `assert_no_wallclock` überbreit (trifft jedes `YYYY-MM-DD`-Zeichenmuster, inkl. legitimer `### YYYY-MM-DD`-Datumsgruppen des `wiki/log.md` — Konventionsdaten, keine Wanduhr-Steuerung): der A0-20-Check ist daher nur auf `registry/wiki` (L-1) anwendbar, nicht auf `log.md`; der L-1-Kommentar „kein Zeitstempel in Lock/Registry" behauptet breitere Coverage als die Funktion liefert. A0-20-Steuerung greift korrekt (nur auf Registry/Lock); Kommentarschärfe + eventuelle schärfere Regex (z. B. `T[0-9]{2}`-Pflicht) = Kosmetik. Home: Sandbox-Kosmetik (nächste Sandbox-Härtung).
### Aufgegriffen: Story-3.13-Abnahme als Home der 3.13-namigen Defer (Gate-Runs realisiert; rd-Notiz-/Defer-Block unverändert) — Story 3.13, 2026-08-22
Der Epic-3-Abnahmegate-Lauf (`_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh`, RUN_OK/Exit 0, PASS_COUNT=70, FAILED=0) realisiert das Home „Story-3.13-Abnahme" der folgenden Defer-Einträge einschlägig. Die Original-Blöcke bleiben historisch unverändert (nur das Home ist erledigt, Spec-3.13 Code-Map Pkt. 59; AD-17e).
- **DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert"** (Story-3.8-Defer, Z. 551; Home: Story-3.13-Abnahme, echte Gate-Runs über die Instruktion) — aufgegriffen: Der 3.13-Gate-Kern E führt GEGEN die committete Fixture zwei echte frische Agent-Läufe (A/B, getrennte Worktrees auf demselben `$BASE`) über `schema/compiler.md` als einzige Instruktionsquelle aus; der Harness schreibt weder erwartete Bodies noch Receipts (AC-5); der A/B-Vergleich bestätigt bundle-State-Identität bis auf die `generated.at`-Ausnahme (§5.14 Pkt. 2). Die Nicht-Vakuum- und Determinismus-Last tragen die real erzeugten Bundle-Bäume (git diff A B -- wiki/, nur at-Zeilen) und die Negativ-Kontrolle G-6.
- **Positive Zwei-Run-Identität strukturell trivial (R-9 clean A==B)** (Story-3.9-Defer, Z. 560; Home: Story-3.13-Abnahme) — aufgegriffen: Die Zwei-frische-Agenten-Bestätigung ist im 3.13-Gate real (getrennte Kontexte, getrennte Worktrees, gemeinsamer `$BASE`, Instruktion als einzige Quelle), nicht mehr aus Skript-Literalen abgeleitet; A==B-Identität wird über den realen Bundle-Baum-Vergleich assertiert.
- **Externer Validator gegen das reale committete Bundle** (Story-3.10-Defer, Z. 576; Home: Story-3.13-Abnahmegate) — aufgegriffen: Abschnitt D des Gates startet einen frischen Agent-Kontext, der `schema/validator.md` (Rev 9) über alle 7 `wiki/`-Dateien der realen /tmp-Käfig-Kopie ausführt und Verdikte `SUCCESS`/`FAIL` je Datei ablegt; das Gate prüft Grammatik + Exhaustivität, urteilt nie selbst (D-3).
- **Zwei-Producer-verschiedene-`<id>`-Gewinner** (Story-3.11-Defer, Z. 594; Home: Story-3.12-Lifecycle / Story-3.13-Abnahme) — aufgegriffen: sandbox-3-12 (Sub-Run im Gate, Abschnitt C) übt den atomaren Ownership-CAS-Takeover des `refs/leases/wiki`-Scope-Locks; das 3.13-Gate hebt die 12 Sandbox-Suiten in einen einzigen fail-fast-Abnahmelauf (AC-1).
- **A-2 keine Synchronisations-Barriere / zeitliche Überlappung** (Story-3.11-Defer, Z. 597; Home: Sandbox-Härtung / Story-3.13-Abnahme) — aufgegriffen (Gate-Route): Der 3.13-E-Kern demonstriert die Real-Tree-Ausführung zweier unabhängiger Compiler-Runs in getrennten Worktrees; die atomare create-only-`git update-ref`-Semantik von sandbox-3-12 wird als Sub-Run des Abnahmegates re-ausgeführt. Die zwangsweise gleichzeitige (wanduhrgetaktete) Überlappung zweier Prozesse bleibt als benannter Rest-Term der Sandbox-Härtung (A0-20-rahmenkonform) bestehen.
- **Rest-Vertiefung echte Zwei-Worktree-Überlappung** (Story-3.11-Aufgegriffen-Notiz, Z. 611) — aufgegriffen (Rest-Term wie im Original-Eintrag benannt): das 3.13-Gate führt die Zwei-frische-Agenten-Bestätigung real in getrennten Worktrees aus; eine wanduhr-synchronisierte Echt-Überlappung beider Produzenten bleibt Rest-Vertiefung der Sandbox-Härtung.
- **Geteilter-Ref-Namespace-/Zwei-Worktree-Übernahme-Real-Beweis** (Story-3.12-Defer, Z. 629; Home: Story-3.13-Abnahme) — aufgegriffen: Das 3.13-Gate stellt den Clone-geteilten Käfig (ein `git init`-Repo, mehrere Worktrees auf demselben `$BASE`) bereit und führt die Zwei-frische-Agenten-Runs real aus — der Determinismus-/Takeover-Beweis läuft hier über echte unabhängige Worktrees statt über einen einzigen sequenziellen Worktree.
- **DF1 — AC-3-`git stash push`-Variante + kombiniertes AC-5** (Story-3.12-Defer, Z. 633; Home: Sandbox-Härtung / Story-3.13-Abnahme-Gate) — aufgegriffen (Gate-Route): Das 3.13-Gate re-ausführt sandbox-3-12 L-1..L-9 (inkl. Scratch-Zone-/Restore-Klasse und Clean-Input-Guard) als Sub-Run im Gesamtabnahmekontext; die zusätzliche native `git stash`-Alternativ-Variante und die wanduhr-gekoppelte Kombination bleiben als Rest-Term der Sandbox-Härtung benannt (keine neue §-Semantik, keine Instruktions-/Validator-Änderung, AD-3).
- status: aufgegriffen (Home „Story-3.13-Abnahme" realisiert durch die echten Abnahmegate-Runs; Original-Defer-Blöcke unverändert; jeweils benannte Rest-Terme der weiterführenden Sandbox-Härtung bleiben offen — kein Instruktions-/Validator-Defekt, A0-20-konform)
## Deferred from: code review of spec-3-13-epic-3-verifikations-und-abnahmegate (2026-08-22)
- W-1 — Timeout-Budgets (2700 s pro Agent-Lauf) an der Grenze zu beobachteten Agent-Laufzeiten (Skript-Kommentar dokumentiert empirisch A:~30 min, B: >35 min): Laufzeit-Variation ist Modell-/Token-Latenz, kein deterministischer Defekt; kein A0-20-Verstoß (Timeout ist Laufzeit-Schutz, keine Wanduhr-Steuerung). Home: Sandbox-Härtung (Budget-Observation bei Folge-Läufen).
- W-2 — `PASS_COUNT`-Beleg-Zahl ist loop-basiert (12 Referenz-Sandbox-Existenz-PASS + 7 wiki-Datei-Verdikt-PASS + fix) und driftet design-immanent bei Bundle-/Referenzwachstum: Beleg-Zahl in log.md/sprint-status/epic-3-context ist Lauf-Momentaufnahme, keine stabile Invariante; Beleg-Formulierung (kumulative vs. pro-Sektion-Zählung) wird im Review-Patch P-12 geklärt; keine Gate-Semantik-Folge. Home: Sandbox-Kosmetik (nächste Sandbox-Härtung).
## Deferred from: Re-Run #6 (Story 3.13 Gate) — E.6 Receipt-Over-Pin (2026-08-24)
- **D-3.13-R6-1: byte-deterministischer Run-Receipt (Vertrags-Vollständigkeit)** (Story 3.13, Re-Run #6, 2026-08-24) — **status: offener Defer / Ask-First-Kandidat (Gate-Härtung ausgeführt, Vertrags-Lücke benannt).**
**Befund:** Re-Run #6 (grün bis E.6; E.4 GRÜN `NON_AT=0 AT=2` — die Rev-3.8-Remediation wirkt) bricht an **E.6 (Run-Receipt A-vs-B)**: die `decision:`-Zeile divergiert A/B (A „`alpha: UPDATE (…)`" vs B „`wiki/alpha.md = UPDATE (…); wiki/log.md = …; wiki/gamma.md = NO_OP; wiki/index.md = NO_OP`"), während die drei **deterministischen strukturierten Felder** `baseline`/`candidates`/`sources_added` **byte-identisch A==B** sind. Die divergierende Zeile ist **freie LLM-Prosa** (identischer Befund, freie Wortung).
**Klassifikation:** **Gate-Over-Pin (kein compiler.md-Defizit, kein AC-4-Fall).** `schema/compiler.md` definiert den Run-Receipt **überhaupt nicht** (0 Treffer „receipt" — grep-verifiziert); der Receipt ist ein **Gate-Artefakt**. Der Gate-Prompt (E.3, D-1) deklariert `decision: <Entscheidung je Mutation>` selbst als **frei** („Werte aus deinem Lauf", „keine vorgegebenen Werte"). Der alte E.6-Byte-Vergleich des kompletten Receipt **widersprach damit dem eigenen Prompt** und überpinsste die freie Prosa — dieselbe Over-Pin-Klasse wie das `log.md`-Problem in Re-Run #5, hier aber auf der Gate-Assertion-Seite. Die **semantische** Entscheidung ist bereits am Bundle-State bewiesen (E.4 byte-identisch bis `at` + E.5/E.7/E.8 Witness).
**Ausgeführte Gate-Härtung (Re-Run #6-Auflösung, Re-Run #7-Pfand):** E.6 vergleicht nun **nur die drei deterministischen Felder byte-identisch A==B** + prüft die `decision`-Zeile auf **Vorhandensein** (nicht leer); die `decision`-Prosa gilt als **Befund-Äquivalenz** (Nicht-Wort-Identität, §5.10-Pkt-3-Prinzip; D-2/D-1-konsistent). Keine compiler.md-/frozen-Block-Änderung.
**Offener Vertrags-Defer (Ask-First):** die **Vertrags-Vollständigkeit** — ein **byte-deterministischer Receipt** mit kanonisch gepinnter `decision`-Feldform — ist **nicht** Teil des gültigen Vertrags und wird als **separater Kandidat** für eine spätere compiler.md-Revision (Rev 3.9) oder Epic-4-Determinismus-Überarbeitung übergeben. Bis dahin gilt: deterministische Felder A==B (hart), decision-Prosa = Befund-Äquivalenz (kein Byte-Abgleich). Kein stiller Semantik-Change durch das Gate (AC-4/AD-3-konform).
**Home:** Story 3.13 (Gate-Härtung) / spätere compiler.md-Revision oder Epic-4 (Vertrags-Vollständigkeit).
## Deferred from: code review of spec-3-13-epic-3-verifikations-und-abnahmegate (Story 3.13, Review-Loop-2, 2026-08-24)
- **W-L2-1: D-NEG-FAIL-Wurzel nicht gegen beabsichtigten Punkt verifiziert** (Story 3.13, Review-Loop-2, 2026-08-24) — D-NEG.2/.3/.4 asserten nur „mindestens 1 FAIL-Verdikt pro Neg-Fixtur" (neg_count `-ge 1`), nicht dass die FAIL-Wurzel die beabsichtigte Invaliditäts-Stelle (Punkt 1/9/14-EC-3) ist. Der FAIL-Grund ist freier Validator-Prosa-Text → Punkt-Exakt-Match wäre fragil (falsch-rot-Risiko bei gültiger Prosa-Variation); der Differenz-Nachweis (pos-SUCCESS vs. je neg-FAIL, Positiv-Kontrolle im selben Käfig) + AC-3-„verhindern SUCCESS" genügt für die Abnahme. Härtungs-Kandidat für spätere Sandbox-Härtung (z. B. Punkt-Keyword-Toleranz-Grep statt Exakt-Match).
- **W-L2-2: E.6 `decision`-Präsenz-Check (nicht-leer) = beabsichtigtes Scope** (Story 3.13, Review-Loop-2, 2026-08-24) — E.6 prüft `decision:` nur auf Nicht-Leerheit (kein Format-/Längen-Minimum, keine Concept-Pfad-Referenz-Pflicht). Das ist **beabsichtigt**: `decision` ist freie LLM-Prosa (Befund-Äquivalenz, Loop-1 D-2 / Re-Run-#6-Klassifizierung); ein Stärker-Check wäre Over-Pin (dieselbe Klasse wie das Re-Run-#6-E.6-Problem) und riskiert falsch-rot. Kein Defekt.
- **D-3.13-L2-1: compiler.md Rev-3.8-Unterpinnung — B2-Reihenfolge-Pin fehlt + §8-Überreich** (Story 3.13, Review-Loop-2, 2026-08-24) — **status: offener Defer (Nutzer-Entscheidung D-3.13-L2-1 = „Defer + Bearer-Fix"; Bearer-Doc-Fix ausgeführt).** compiler.md §5.9 pinnt die Log-Eintragsform, aber **nicht** die Reihenfolge der Einträge *innerhalb* einer `## YYYY-MM-DD`-Datumsgruppe (B2-Hälfte „Reihenfolge-pinnen" von Rev 3.8 nie in compiler.md gelandet; grep 0 Treffer „Innerhalb einer Datumsgruppe"/Tie-Break); §8-Rev-3.8-Claim „jeder Eintrag byte-deterministisch ableitbar" ist überreich (Disagreement-Form §5.9 Pkt. 4 unpinnt; bare `- Anlage:`-Form nach CREATE-Entfernung ohne Live-Demo). **Klassifikation: latent (3× A/B-`log.md` byte-identisch in #7/#8/#9, noch nie rot).** Bearer-Doc (compiler-revision-3-8-…) §0.1-Zeile B2 + L26-Claim „= genau so wie im Entwurf" korrigiert auf „TEILANGEWENDET" (nur Form-/Wanduhr-Hälfte). Kandidat für spätere compiler.md-Revision (Rev 3.9) zusammen mit W-L2-2 und dem CREATE-Residual (D-3.13-L2-3). **Home:** Story 3.13 / spätere compiler.md-Revision.
- **D-3.13-L2-2: log.md `## YYYY-MM-DD`-Datumsgruppen-Header = Wanduhr außerhalb der §5.14-Pkt.-3-`at`-Ausnahme** (Story 3.13, Review-Loop-2, 2026-08-24) — **status: offener Defer (Nutzer-Entscheidung D-3.13-L2-2 = „Defer + Gate-Seite dokumentieren").** Die Datumsgruppen-Header sind Wanduhr-Werte; §5.14 Pkt. 3 nennt nur `generated.at`/`verified[].at` als Ausnahmefelder. Ein A/B-Paar, das **Mitternacht überschneidet**, erzeugt deterministisch E.4-`NON_AT`-rot (falsch-rot; der geankerte Classifier maskiert den Header korrekt NICHT). **Klassifikation: latent, nicht beobachtet (alle Läufe same-day).** Gate-Seite: der Gate-Kern (E.4-Classifier) behandelt den Header korrekt als Nicht-`at`-Zelle; same-day-Läufe sind byte-deterministisch. Kandidat für spätere compiler.md-Revision (Rev 3.9: Header an Baseline-Commit-Date koppeln ODER als benannte Ausnahmeklasse aufnehmen), gebündelt mit D-3.13-L2-1. **Home:** Story 3.13 / spätere compiler.md-Revision.
- **D-3.13-L2-3: AC-6 CREATE/Synthese — Substitutions-Notiz + frischer-LLM-CREATE-Residual** (Story 3.13, Review-Loop-2, 2026-08-24) — **status: offener Defer (Nutzer-Entscheidung D-3.13-L2-3 = „Defer + Substitution-Notiz").** AC-6 verlangt „Anlage einer neuen Wissenseinheit" + „kohärente Multi-Source-Synthese"; die A/B-Fixture (E) ist UPDATE-only. **Substitutions-Notiz (ausgeführt, Doku):** die CREATE-/Synthese-*Mechanik* (neues Concept + Index-Link Punkt 11 + Slug-Ableitung §5.15/§5.7 + Multi-Source-Synthese + log.md-Anlage) wird über die Sandbox-Sub-Runs **3-3/3-4** (mechanisch gepinnt, eigene Isolation) **strukturell** als demostriert betrachtet — die Substitution „mechanisch gepinnt ≙ frischer LLM" gilt für die *Mechanik-Struktur*, nicht für den freien LLM-Wortlaut. **Residual (offen):** der **frische-LLM-CREATE-Unterpfad** ist nicht-deterministisch (Re-Run-#5-Befund: 24/26 NON_AT CREATE-/Slug-getrieben; ohne compiler.md-CREATE-Pinning = deterministisch rot) und steht damit als **benanntes, nicht-deterministisches Residual** hier — kein Gate-Defekt, sondern eine bekannte Vertrags-/Instruktions-Grenze (CREATE-Body bleibt FR-2 „Befund-Äquivalenz, nicht Wort-Identität"). Kandidat für spätere Vertrags-Revision/Epic-4-Determinismus-Überarbeitung. **Home:** Story 3.13 / spätere compiler.md-Revision oder Epic-4.
@@ -4,7 +4,7 @@
## Goal
Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bundle, statt das Wiki bei jedem Lauf aus sämtlichen Rohquellen neu aufzubauen (Compounding Knowledge). Bestehende Concepts werden durch neue Erkenntnisse erweitert, präzisiert oder in eindeutig belegten Fällen korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation mit gemischter, claim-granularer Provenienz synthetisiert; unverändertes Wissen bleibt erhalten. Relevanzbestimmung und Reconcile-Routing sind textual-deterministisch ohne Embedding- oder Vector-Infrastruktur. Konkurrierende Producer koordinieren sich über eine atomare Root-Scope-Lease mit transaktionalem Dirty-Tree-, Rollback- und Release-Lifecycle. Klassifikationspflichtige Kollisionen und Widersprüche werden bis Epic 4 fail-closed als strukturierter Hold erhalten; der Determinismus-Vertrag wird mechanisch qualifiziert. Epic 3 gilt erst nach einem realen, unabhängigen Source→Compilation→Wiki-Abnahmegate als abgeschlossen (AD-5, AD-6, AD-13, AD-17a/b/dh, A0-6/7/1216/18/19).
Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bundle, statt das Wiki bei jedem Lauf aus sämtlichen Rohquellen neu aufzubauen (Compounding Knowledge). Bestehende Concepts werden durch neue Erkenntnisse erweitert, präzisiert oder in eindeutig belegten Fällen korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation mit gemischter, claim-granularer Provenienz synthetisiert; unverändertes Wissen bleibt erhalten. Relevanzbestimmung und Reconcile-Routing sind textual-deterministisch ohne Embedding- oder Vector-Infrastruktur. Konkurrierende Producer koordinieren sich über eine atomare Root-Scope-Lease mit transaktionalem Dirty-Tree-, Rollback- und Release-Lifecycle. Klassifikationspflichtige Kollisionen und Widersprüche werden bis Epic 4 fail-closed als strukturierter Hold erhalten; der Determinismus-Vertrag wird mechanisch qualifiziert. **Ist (Story 3.13, 2026-08-24 — grüner Re-Run #10 + Review-Loop-2 konvergiert, `done`):** das reale, unabhängige Source→Compilation→Wiki-Abnahmegate `_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh` (gehärtet, 1005 Z. nach Review-Loop-2) ist als Voll-Lauf ausgeführt. **Re-Run #10 (2026-08-24, committed `09c1c83`): GRÜN — `FAILED=0 PASS_COUNT=51`, SANDBOX-3-13-OK, RUN_OK/Exit 0** (A.5 Pre-Check sauber; C 12/12; D 4/4 SUCCESS; D-NEG 3×FAIL+1×SUCCESS; E.4 `NON_AT=0 AT=2`; E.6 Feldsatz-exakt + Known-Value-Witness gegen `$BASE`; E.9 kanonische `Baseline $BASE`-Form; F G-1..G-8; G-8 Porcelain-Clean + AD-3 ohne Diff); Review-Loop-2 (bmad-code-review 4 Layer): 38 rohe → 4 decision-needed / 11 patch / 2 defer / 4 dismissed — alle Entscheidungen mit ProMods geschlossen, alle Patches angewendet; finaler `done`-Flip (Step-05, Präzedenz 3.73.12). Siehe wiki/log.md Story-3.13-Eintrag (2026-08-24). Vorgeschichte: **Erst-Lauf (2026-08-22, vor Härtung):** grün (PASS_COUNT=70, FAILED=0, RUN_OK/Exit 0; 12 Sandbox-Suiten fail-fast, Validator 7/7 SUCCESS über die damals nicht-isolierte Fixture, A/B nur-at, G-1..G-8) — gültiger Erst-Lauf-Beleg. **Härtung (Review-Loop-1, 15 Patches):** isolierte Mini-Fixture (P-4), geankerter E.4-Classifier (P-2), D-NEG 3×FAIL+1×SUCCESS, G-6 perturbed-Tree-Negativ-Kontrolle, Validator-Defizit-Route u. a. **Re-Run #5 (2026-08-23, gehärtetes Gate):** A, B, C 12/12, D 4/4 SUCCESS, D-NEG 3×FAIL+1×SUCCESS, E.1E.3 und beide frischen Agent-Läufe A/B (`RUN_DONE`) **grün****rot an E.4** (`NON_AT=26 AT=4`): ein **korrekt erkannter, echter AD-16-A/B-Divergenz-Befund** in der freien CREATE-Synthese (Slug-Identität `beta` vs. `quanten-observatorium-kanal` + freier Body-/log.md-Wortlaut), **kein false-PASS**; der UPDATE-Pfad (`alpha`) ist byte-identisch bis auf `at:`. Das Gate ändert die Schema-Semantik **nicht still** (AD-3/AC-4); die Behebung liegt in `schema/compiler.md` (read-only) oder im A/B-Vergleichsumfang (A0-20) → **AC-4-Defizit-Roadmap**: Bedarf an einer **separat autorisierten Epic-1-Remediation** wird benannt (Rev 3.8 — kanonische byte-deterministische `log.md`-Eintragsform — autorisiert und ausgeführt); F/G liefen im Voll-Lauf #5 nicht (fail-fast an E.4). **Epic 3 ist abnahmegeeignet und `done`** (grüner Re-Run #10 + konvergierter Review-Loop-2, 2026-08-24; A/B-Vergleichsumfang auf den Update-/Erhaltungs-Kern, D-3.13-6 Option 2); die finale Retrospektive (Epic-3-retrospective, optional) bleibt offen (AD-5, AD-6, AD-13, AD-17a/b/dh, A0-6/7/1216/18/19).
## Stories
@@ -40,15 +40,15 @@ Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bu
- **Keine eigene LLM-Runtime:** Der ausführende agentische Host orchestriert den AD-17-Ablauf (Lease holen, innerhalb des geleasten Bereichs mutieren, committen, freigeben); keine separaten Prozesse oder ein Server (AD-11).
- **Atomare Root-Scope-Lease (AD-17a/b, A0-12/13):** Producer behalten die Branch-Konvention `lease/<area>/<id>`, akquirieren aber genau einen atomaren, scope-bezogenen Lock im clone-geteilten Zustand. Die Run-ID ist Lock-Inhalt, nicht Exklusivitätsschlüssel; konkurrierende Producer verschiedener IDs und Worktrees teilen denselben Root-Scope (`wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien). *Ist (Story 3.11, 2026-08-21): die atomare Lock-Präzisierung ist in `schema/compiler.md` **§5.17** verankert (Revision 3.6 — Atomare Root-Scope-Lease-Akquise: Exklusivitätsschlüssel = einziger scope-bezogener Lock im clone-geteilten Zustand, eine committete Ref je Root-Scope `wiki/`, `git update-ref <ref> <wert> $ZERO_SHA` create-only, Run-ID als Lock-Inhalt statt Schlüsselbestandteil, atomare Akquise mit genau einem Gewinner und `LEASE_HOLD` für den Abgewiesenen, Kollisions-Hold mit beiden Commit-Hashes an Epic 4 statt textuellem Auto-Merge; §5.11-Pkt.-1/2-Wortlaut und §5.12-Anker bleiben unverändert maßgeblich, Fugen-Identität).*
- **Fail-closed Kollisionsgrenze (AD-17c, A0-14):** Zwei Branches mit ungleichen Änderungen am selben Concept-Pfad werden nie textuell automatisch gemerged. Epic 3 erhält beide Commit-Hashes und den Scope in einem strukturierten Hold; AD-16-Klassifikation und semantische Auflösung sind Epic 4 / Story 4.x.
- **Transaktionaler Lifecycle (AD-17df, A0-15/16, AD-6):** Preflight schützt getrackte und ungetrackte Fremdänderungen (eindeutige Abort-/Protect-Zustandsmaschine), Rollback restauriert exakt den bezeichneten Baseline-Commit (Index + Worktree), Release hinterlässt Mutation, zulässigen Nachweis und sauberen Worktree dauerhaft. Eine lebende Lease wird nicht allein durch Generationserhöhung stale; Staleness verlangt bestätigten Abbruch oder abgelaufene Liveness plus atomare Ownership-Prüfung. *Zielzustand (Review-Loop-3, P-11): geplant für Story 3.12 (Sprint-Change-Proposal 2026-08-20) — die transaktionale Lifecycle-Präzisierung (Abort-/Protect-Zustandsmaschine, Liveness-Prüfung) ist noch nicht in `schema/compiler.md` §5.11/§5.12 verankert; die bestehende Verankerung bleibt unverändert maßgeblich.*
- **Transaktionaler Lifecycle (AD-17df, A0-15/16, AD-6):** Preflight schützt getrackte und ungetrackte Fremdänderungen (eindeutige Abort-/Protect-Zustandsmaschine), Rollback restauriert exakt den bezeichneten Baseline-Commit (Index + Worktree), Release hinterlässt Mutation, zulässigen Nachweis und sauberen Worktree dauerhaft. Eine lebende Lease wird nicht allein durch Generationserhöhung stale; Staleness verlangt bestätigten Abbruch oder abgelaufene Liveness plus atomare Ownership-Prüfung. *Ist (Story 3.12, 2026-08-21): die transaktionale Lifecycle-Klammer ist in `schema/compiler.md` **§5.18** verankert (Revision 3.7 — Lease-Lifecycle & transaktionaler Commit-Abschluss: Liveness & Ownership AC-1, stale-Übernahme genau einmal mit benannter ersetzter Holder-ID AC-2, eindeutige Abort-/Protect-Zustandsmaschine für getrackte/ungetrackte Fremdänderungen mit byte-identischem Restore AC-3, Baseline-Rollback Index + Worktree aus `<Baseline-Commit>` mit leerem Post-Rollback-Diff AC-4, durable Release + Clean-Input-Guard für den Folge-Run AC-5, kanonisches Log AC-6, vier Kill-Point-Tests AC-7; §5.11/§5.12/§5.13/§5.17-Wortlaute textuell unverändert, Fugen-Identität; keine Wanduhr-TTL, A0-20).*
- **Determinismus-Vertrag (AD-17h/FT-10, A0-19):** Das kanonische Eingabemanifest bindet Baseline, geordnete Sources und jeden output-sichtbaren Run-/Zeit-/Identitätswert; der Run-Receipt (Candidates, Reihenfolge, Plan, Entscheidungen, Output-Hashes) liegt außerhalb des Bundles. Zwei getrennte saubere Worktrees mit frischen Agent-Kontexten erzeugen denselben Bundle-State; hart codierte erwartete Pläne oder Concept-Bodies und pauschal maskierte `verified`-Ereignisse sind kein gültiger Nachweis.
- **Synthese bleibt source-grounded (AD-4):** Bestehende Concepts dürfen Kontext liefern, fachliche Aussagen müssen aber auf nachvollziehbare `raw/`-Evidenz zurückführbar bleiben; Wiki-Links ersetzen nie die Provenienz zur ursprünglichen Evidenz.
- **Epic-3-Abnahmegate:** Ein portables, fail-fast ausführbares Gate führt alle Epic-3-Szenarien aus, ruft den vollständigen autorisierten Schema-Validator über alle `wiki/`-Dateien auf und lässt einen frischen Agent-Kontext die kanonische Instruktion über einer repräsentativen Fixture ausführen — der Harness schreibt keine erwarteten Wiki-Ausgänge selbst. Zwei frische Agent-Kontexte müssen Run-Receipts und Bundle-State angleichen. Ein Defizit des autorisierten Validator-Vertrags blockiert das Gate und verlangt eine separat genehmigte Epic-1-Remediation.
- **Epic-3-Abnahmegate:** Ein portables, fail-fast ausführbares Gate führt alle Epic-3-Szenarien aus, ruft den vollständigen autorisierten Schema-Validator über alle `wiki/`-Dateien auf und lässt einen frischen Agent-Kontext die kanonische Instruktion über einer repräsentativen Fixture ausführen — der Harness schreibt keine erwarteten Wiki-Ausgänge selbst. Zwei frische Agent-Kontexte müssen Run-Receipts und Bundle-State angleichen. Ein Defizit des autorisierten Validator-Vertrags blockiert das Gate und verlangt eine separat genehmigte Epic-1-Remediation. *Ist (Story 3.13, 2026-08-24 — grüner Re-Run #10, Review-Loop-2 konvergiert, `done`): realisiert als `sandbox-3-13/run-sandbox.sh` (gehärtet, 1005 Z.) — fail-fast-Orchestrierung (A Setup, B /tmp-Käfig, C 12 Sandbox-Sub-Runs, D Validator-Agent mit Verdikt-Grammatik/-Exhaustivität statt eigener Urteile, E Zwei-frische-Agenten A/B auf git-worktree-$BASE, F G-1..G-8 harte Assertions inkl. G-6 Negativ-Kontrolle Perturbation und G-7 Epic-5-Smoke, G Porcelain-Endzustands-Invariante). Erst-Lauf (2026-08-22, vor Härtung) grün (PASS_COUNT=70, FAILED=0, RUN_OK/Exit 0 — gültiger Erst-Lauf-Beleg); G-7 exkludiert den Bundleroot-Schema-Glossar (../schema/, compiler §5.6 „andere Schicht", AC-8 optionale Provenienz) und das log.md-Protokoll (§5.6 Pkt. 2). **Grüner Re-Run #10 (2026-08-24, committed `09c1c83`): `FAILED=0 PASS_COUNT=51`, SANDBOX-3-13-OK, RUN_OK/Exit 0** — A.5 Pre-Check sauber, C 12/12, D 4/4 SUCCESS, D-NEG 3×FAIL+1×SUCCESS, E.4 `NON_AT=0 AT=2` (Bundle-State identisch bis auf `at:`), E.6 neu hart (Feldsatz-exakt P-L2-2 + A==B + Known-Value-Witness gegen `$BASE` P-L2-1), E.9 kanonische `Baseline $BASE`-Form (P-L2-6), F G-1..G-8 grün (G-6 Negativ-Kontrolle `NON_AT=2` erkannt, Positiv-Kontrolle at-only toleriert, G-7 0 Verletzungen), G-8 Porcelain-Clean (inkl. Untracked) + AD-3 ohne Diff. Review-Loop-2 (bmad-code-review 4 Layer): 38 rohe → 4 decision-needed / 11 patch / 2 defer / 4 dismissed — alle Entscheidungen mit ProMods geschlossen, alle Patches angewendet (P-L2-1..11). Finaler `done`-Flip (Step-05, Präzedenz 3.73.12); `epic-3``done` (AC-9). Vorhistorie: Re-Run #5 (2026-08-23) rot an E.4 (`NON_AT=26 AT=4`) — korrekt erkannter, echter AD-16-A/B-Divergenz-Befund in der freien CREATE-Synthese (Slug-/Wortlaut-Divergenz), kein false-PASS; UPDATE-Pfad `alpha` byte-identisch bis auf `at:` → AC-4-Defizit-Roadmap (separat autorisierte Epic-1-Remediation Rev 3.8 — kanonische byte-deterministische `log.md`-Eintragsform — ausgeführt; A/B-Vergleichsumfang auf den Update-/Erhaltungs-Kern, D-3.13-6 Option 2); F/G im Voll-Lauf #5 nicht demonstriert (fail-fast an E.4) — im grünen Re-Run #10 vollständig erreicht.*
- **Keine UX-/Design-Anteile relevant:** v1 ist datei-/CLI-basiert ohne GUI (PRD A-3, AD-11).
## Cross-Story Dependencies
- Baut auf dem Workspace aus Epic 1 auf (immutable `raw/`, Bundle-Root, Schema-Validierung) und konsumiert die in Epic 2 erzeugten OKF-konformen, verlinkten Concepts mit claim-granularer Provenienz als vorhandenes Wissen.
- Story 3.9 liefert die deterministische Routing-Basis für Story 3.10; Story 3.11 liefert die atomare Lease-Basis für Story 3.12 (parallel). Story 3.8 wird nach 3.93.12 mit echten unabhängigen Runs abgeschlossen; Story 3.13 ist das finale Abnahmegate.
- Story 3.9 liefert die deterministische Routing-Basis für Story 3.10; Story 3.11 liefert die atomare Lease-Basis für Story 3.12 (parallel). Story 3.8 ist nach 3.93.12 mit echten unabhängigen Runs abgeschlossen (done); **Story 3.13 ist das finale Abnahmegate`done` (2026-08-24, grüner Re-Run #10 committed `09c1c83`: `FAILED=0 PASS_COUNT=51`, SANDBOX-3-13-OK, RUN_OK/Exit 0 — A.5 Pre-Check sauber, C 12/12, D 4/4 SUCCESS, D-NEG 3×FAIL+1×SUCCESS, E.4 `NON_AT=0 AT=2`, E.6 Feldsatz-exakt + Known-Value-Witness, E.9 kanonische `Baseline $BASE`-Form, F G-1..G-8, G-8 Porcelain-Clean + AD-3 ohne Diff); Review-Loop-2 konvergiert (38 rohe → 4 decision-needed / 11 patch / 2 defer / 4 dismissed, alle ProMods geschlossen, alle Patches angewendet); Vorhistorie: Erst-Lauf 2026-08-22 grün (PASS_COUNT=70, gültiger Erst-Lauf-Beleg), gehärtetes Gate (915 Z.) rot an E.4 im Re-Run #5 (2026-08-23: `NON_AT=26 AT=4`, korrekt erkannter echter AD-16-A/B-Divergenz-Befund in der freien CREATE-Synthese, kein false-PASS) → AC-4-Defizit-Roadmap: separat autorisierte Epic-1-Remediation Rev 3.8 (kanonische byte-deterministische `log.md`-Eintragsform) ausgeführt + A/B-Vergleichsumfang auf den Update-/Erhaltungs-Kern (D-3.13-6 Option 2); finaler `done`-Flip + `epic-3``done` (AC-9) im Step-05-Status-Sync (2026-08-24); `epic-3-retrospective` (optional) offen; siehe wiki/log.md Story-3.13-Eintrag (2026-08-24).**
- AD-17c/A0-14 sind geteilt: Epic 3 verantwortet Erkennung und fail-closed Erhaltung, Epic 4 Klassifikation und semantische Auflösung. AD-17g/A0-17 verbleiben vollständig in Epic 4. A0-21 ist ebenfalls geteilt: Story 3.10 verantwortet den Incrementality-Teil (Erhaltung unabhängigen Wissens), Story 4.3 die Human-Curation-Semantik (FT-9).
- Das Leasing-/Dirty-Tree-Modell koordiniert Compiler-Runs mit menschlicher Bearbeitung und trägt die Git-Nachvollziehbarkeit, auf die Epic 5 aufsetzt.
@@ -0,0 +1,206 @@
---
epic: 3
date: 2026-08-24
verdict: accepted-with-open-items
criteria: declared
headless: false
---
# Retrospective Epic 3 — Inkrementelle Kompilation & Synthese
<!-- Skeleton — wird Phase für Phase gefüllt. -->
## Epic summary
- **Epic:** 3 — Inkrementelle Kompilation & Synthese
- **Diff-Range:** `efc543c^..3f57586` (erster Story-Commit `efc543c` (Story 3.1), letzter `3f57586` (3.13 done-Flip); 44 Commits, 1 Merge `5a78271` (gemessen, erst-parent-diff))
- **Commits/Churn (nicht-Merge):** 43 nicht-Merge-Commits; Top-Dateien: `sandbox-3-13/run-sandbox.sh` (+1716/711, 11 Commits), `schema/compiler.md` (+318/87, 21 Commits), `deferred-work.md` (+386/14, 23), `spec-3-13` (+265/27), `sprint-status.yaml` (28 Commits), `wiki/log.md` (+49/4)
- **Stories:** 3.13.13 alle `done`; `pending_stories` leer (`detect-epic --epic 3`, 2026-08-24) → kein Machine-Zwang zu `rejected`. `epic-3-retrospective`: optional → Phase 5 auf `done`.
- **Abnahmekriterien:** deklariert in `_bmad-output/planning-artifacts/epics.md` (Epic-3-Block Z. 259451 + je Story-AC-Zeilen) → Verdikt-Basis **declared**.
- **Evidence-Inventar:**
- Epic-Spec: `planning-artifacts/epics.md` (Epic-3-Block, ACs je Story deklariert)
- Story-Specs 3.13.13 unter `_bmad-output/implementation-artifacts/spec-3-*.md` (13 Dateien)
- `epic-3-context.md` (kontextuell kompiliert, mit Ist-Zustand je AD)
- `schema/compiler.md` (548 Z. nach Epic 3; +318/87 net über 21 Commits) — das Hauptlieferprodukt
- `schema/canonical-terms.md` (+40/1, Story 3.2)
- Sandbox-Suiten: `sandbox-3-1``sandbox-3-13` (je `run-sandbox.sh`; 3-13 gehärtet 1005 Z.)
- `deferred-work.md` (673 Z.; +386/14, 23 Commits — append-only Register)
- `sprint-status.yaml` (28 Commits im Range)
- `wiki/log.md` (78 Z. nach Epic 3; +49/4)
- Voriges Retro: `epic-2-retro-2026-08-18.md` (verdict accepted-with-open-items, Action-Items AI-2-R-1…5)
- `sprint-change-proposal-2026-08-20.md` (genehmigte Story-3.8-Re-Öffnung)
- Merge-Eckung: `merge_files` für `5a78271` (Story 3.8-PR-Merge) enthält teils Epic-2-Restchurn (z. B. `epic-2-retro`, `spec-2-5`, `validator.md` Rev-9) — wird im Aggregat NICHT zu `files` addiert, nur als Kontext notiert.
- **Fehlend:** Session-Logs (nicht in dieser Umgebung verfügbar) → Prozess-Lektionen nur aus git-loggablem Verhalten; `project-context.md` existiert nicht (persistent fact `file:**/project-context.md` leer).
- **Prozess-Hinweis (für Phase 2):** Story 3.13 durchlief ≥ 6 dokumentierte Re-Runs (Erst-Lauf 2026-08-22 grün → Härtung Review-Loop-1 → Re-Runs #2#5, #5 rot an E.4 = echter AD-16-Befund → Rev-3.8-Epic-1-Remediation → Re-Runs #6#10, #10 grün) und 2 Review-Loops (38 rohe Findings im Loop-2). Commit-Log zeigt die Kette vollständig.
## Findings
Jeder Befund trägt eine Quellen-Referenz (Datei:Zeile / Commit / Log). Befunde ohne belegbare Quelle wurden verworfen.
### F-1 — Sandbox-Helfer-Duplikation ist *by design*, aber das semantische Primitive `norm()` ist zwischen 4 Suiten bereits in zwei Strukturanvarianten divergiert
**Quellen:** `sandbox-3-{8,9,10,11}/run-sandbox.sh` (`norm()`-Definitionen); `deferred-work.md` L552 (D-7), L559 (W-3.9-1), L572573 (3.10-Kopie).
**Was kein einzelner Diff zeigt (Aggregate-View):** Die Suite-Helfer-Duplikation selbst ist gewollt — jede Sandbox ist selbstenthalten und in einem frischen `/tmp`-Bundle re-exekutierbar (12/12 Suites liefen unabhängig grün, s. Behavior). Die Duplikation ist also kein God-Class-Defekt. *Aber* beim einen Helfer, der eine **normative Semantik** trägt — `norm()` = die §3.2/§5.14-Kanon-Form, auf die AD-13/AD-17h (textueller Determinismus) aufbauen — hat sich über die 4 Suiten, die ihn definieren (3-8, 3-9, 3-10, 3-11), eine **strukturelle Divergenz** entwickelt:
- **3-8, 3-9, 3-10** (3 identische Kollaps-Formen): `sed`-Kollaps `[-–—_ ]→-` **plus** Case-Folding (`tr 'A-Z' 'a-z'` bzw. `tolower`).
- **3-11** (divergent): `sed`-Kollaps `[–—]→-`, `_→␣`, `␣→-` **ohne jeden Case-Folding-Schritt**.
Die Divergenz ist im Moment **maschiert**: 3-11s einziger Aufruf (Z. 382) liegt neben redundanten Schwesterglobs (`*run-a5*`, `*a5*`), die den fehlenden Fall-Abgleich überdecken. Sie ist aber eine Latenz, kein Zufallsartefakt — und sie trifft genau das Primitive, das Epic 3 als deterministisch verkauft.
**Relation zu bekanntem Deferral:** `deferred-work.md` L552/559/572 dokumentiert bereits, dass `norm()` **keine `LC_ALL`-Pinning** trägt (D-7, Home: Sandbox-Härtung). Das Retro erweitert diesen Befund: (a) 3-11 ist zusätzlich die **einzige Suite mit `LC_ALL=0`** (kein Locale-Pin überhaupt), und (b) die Kopien haben sich **bereits** divergiert — eine künftige Vereinheitlichung muss daher erst 4 driftete Implementierungen *reconcilieren*, nicht nur einen Pin ergänzen. **Stufe:** Latenz/Robustheit (keine gegenwärtige Fehlfunktion, alle Suites grün), aber normativ relevant für AD-13.
### F-2 — `compiler.md` ist das einzige Hauptlieferprodukt und wuchs zu einer 548-Zeilen-/15-Sektionen-Datei (God-File-Risiko, aber strukturell sauber)
**Quellen:** `schema/compiler.md` (`wc -l` = 548; `grep -nE '^## 5\.'`); git-churn `+318/87` über 21 Commits (s. Epic summary).
**Was kein einzelner Diff zeigt:** Epic 3 legte `## 5.9``## 5.18` an (Z. 257440 ≈ 183 neue Zeilen) — die Sektionen-Blase von §5 wuchs von 5 auf 15. Die Datei ist das einzige normative Hauptartefakt des ganzen Projekts; jede Epic-3-Story mutiert *denselben* Datei-Block, was den §5-Bereich zum natürlichen Konfliktherd macht (tatsächlich: 21 Commits an einer Datei, der churn-höchste Wert des Epics nach der Sandbox). **Aber** die Struktur ist sauber: lineare Nummerierung, je Sektion ein Story-Verweis, kein Code-Verschnitt. Das Risiko ist **Wartbarkeit/Review-Fläche**, nicht inkohärenter Zustand — die Sandbox-Suiten und der §6-Validator decken die Semantik mechanisch ab. **Stufe:** Beobachtung (Wachstums-Trend), kein Defekt.
### F-3 — §5-Sektions-Nummerierung ist nicht zusammenhängend (Kosmetik, aber verwirrend)
**Quellen:** `schema/compiler.md` `grep -nE '^## 5\.'` — vorhanden: 5.5, 5.6, 5.7, 5.8, 5.9, 5.10, 5.11 … 5.18.
**Befund:** `## 5.1``## 5.4` existieren nicht (der Block beginnt bei 5.5), und die Nummerierung folgt **Story-Nummern, nicht logischer Reihenfolge**: `5.10` = Story 3.4, `5.11` = Story 3.5, `5.17` = Story 3.11. Das ist konsistent *innerhalb* der Story-Mapping-Konvention, aber ein Leser, der nach „§5.10 = zehnter Unterabschnitt" sucht, wird irregeleitet. **Stufe:** Kosmetik/Lesbarkeit; kein semantischer Defekt. (Kein fix-now; optional für eine spätere Instruktions-Revision.)
### F-4 — Verhalten: A==B-Determinismus-Assertion wird **nicht-vakuum** ausgeübt (positiver Befund, bestätigt AD-17h)
**Quellen:** `sandbox-3-10/run-sandbox.sh` E-9 (Re-Exekution dieser Retro, `Exit 0`): `„Zwei-Run-Identitaet (gleicher Eingang -> identische Bytes); at-Wanduhr-Gap-Exzeption real ausgeuebt (nur generated.at differiert); Nicht-Vakuum bei Zustandswechsel"`, `PASS=9 FAIL=0`.
**Befund (Bestätigung, kein Defekt):** Die Determinismus-Vertrags-Assertion, auf der Story 3.8/AD-17h stehen, ist in der Sandbox **real** und **nicht tautologisch** — sie erzwingt echte Byte-Äquivalenz (nur das erlaubte `generated.at`-Feld differiert) und übt den Zustandswechsel-Nicht-Vakuum-Fall aus. Das ist die einzige Stelle im Epic, an der die „A==B" -Kernaussage mechanisch verifiziert wird, und sie hält. Dies schützt das Epic gegen die häufigste Falsch-Grün-Mode bei Determinismus-ACs (leere/triviale A==B). **Abgrenzung zu F-10:** F-4 bestätigt die **mechanische** Assertion (Sandbox, deterministisch); **F-10** zeigt, dass die **frische-LLM-Ausführung** derselben A==B-Prämisse über reale Agent-Kontexte empirisch nicht robust ist (Lauf A no-op). Beide gelten gleichzeitig — der Kern-Mechanismus ist grün, die Ausführung ist es nicht.
### Befunde aus der Diff-scope-Review (bmad-review über `efc543c^..3f57586`)
**Methode/Abgrenzung:** Adversarial- und Verification-Gap-Linsen lieferten 22 bzw. 2 rohe Befunde; Edge-Case-Hunter **stallte ohne Output** (600s-Watchdog) — die Coverage-Lücke wird hier notiert, nicht aufgefüllt. Jeder Befund unten wurde in dieser Retro **unabhängig gegen die Quelle verifiziert** (Zeilengleichung, nicht nur Agent-Wiedergabe). Konsolidierung: VG#1 = Adversarial#1 (gleicher Befund, zwei Linsen); VG#2 ist eigenständig. Die übrigen Adversarial-Befunde (Leasing-Cluster, CREATE-Coverage, Em-Dash) sind eigenständig. Reihenfolge nach Materialität für die Determinismus-/Sicherheits-Ansprüche des Epics.
#### F-5 — MIDNIGHT-Dateigruppen-Bruch: der Kern-Determinismus-AC ist am Tagesgrenze nicht erfüllbar (Spec-Selbstwiderspruch + unbelegte Verifikation) [HÖCHSTE MATERIALITÄT]
**Quellen (alle in dieser Retro verifiziert):** `schema/compiler.md:277` (§5.9 Pkt. 4) + `:375` (§5.14 Pkt. 3) + `:548`; `sandbox-3-13/run-sandbox.sh:650-653` (`classify_diff`) + `:819-828` (E.9); `schema/validator.md:135-137` (F17).
**Befund:** Der §5.14-Zwei-Run-Vertrag ist der **Kern-AC** des Epics (AD-17h, FT-10, AC-1/AC-6). Er erlaubt in `§5.14 Pkt. 3` **genau eine** benannte Differenz — den `generated.at`-Wanduhr-Gap — und erklärt die Ausnahme "ausschließlich auf den `at`-Feld-Wert" bezogen, "alle übrigen Bestandteile … **byte-identisch**". **Aber** `§5.9 Pkt. 4` (Z. 277) verlangt den `log.md`-Eintrag in kanonischer Form mit einer `## YYYY-MM-DD`-Datumsgruppen-Header (neueste zuerst) — und behauptet **gleichzeitig**, der Eintrag trage "keinen Wanduhr-Wert … der Wanduhr-Wert lebt **ausschließlich** im Frontmatter-`generated.at`, **nie im `log.md`-Body**". Die `## YYYY-MM-DD`-Header ist aber **ein Wanduhr-Datum im Body** — weder `generated.at` noch an einen Git-Wert gebunden (es existiert **nirgends** eine `git show --format=%cs/%cd`-Bindung für die Header, verifiziert). Damit ist die Ausnahme-Menge "genau eine" vom §5.9-Wortlaut selbst **widersprochen**, und `classify_diff` (Z. 650-653) maskiert **nur** die ` at: ` -Frontmatter-Zeilen — eine `+`/`-`-Änderung der `## YYYY-MM-DD`-Header zählt als **NON_AT****spurious HARD-FAIL** für einen konformen Lauf, der die Mitternacht-/Timezone-Grenze zwischen Run A und Run B überschreitet. E.9 (Z. 819-828) prüft nur "Lauf-Eintrag vorhanden + trägt `Baseline $BASE`" — **nirgends** wird beansprucht, dass beide Läufe in die *gleiche* Datumsgruppe fallen. Validator-F17 validiert die Datumsgruppe explizit **nicht** (v1). **Heute grün** nur, weil beide Agent-Läufe in derselben Session am selben Tag laufen (Re-Run #10, 2026-08-24). **Bereits im ausgelieferten Artefakt vorhanden (nicht nur latenz):** `wiki/log.md` trägt **10** `## YYYY-MM-DD`-Wallclock-Header (`2026-08-24``2026-08-15`, in dieser Retro gezählt) — der §5.9-Pkt.-4-Header ist also in der realen Auslieferung **geübt**; zwei gleich-tägige Läufe sind byte-identisch (Gate grün), aber der Kern-Anspruch "genau eine Ausnahme" ist vom §5.9-Wortlaut selbst widersprochen. **Cross-Suite-Beweis der Inkonsistenz (VG-Linsenzusatz):** die Suite umgeht den Gap **inkonsistent** — 3-10 (Z. 303/605) nutzt reale `$(date +%Y-%m-%d)` (die einzige Suite, die die Wanduhr-Abhängigkeit real ausübt), während 3-1/3-5/3-6/3-8/3-9/3-12 fixe Tage hartkodieren und 3-9 R-8 die Header weglässt. Kein Test übt jemals einen echten Header-Unterschied zwischen zwei Läufen aus. **Stufe:** **Defekt** (Kern-AC nicht am Tagesgrenze erfüllbar; Spec-Selbstwiderspruch). Fix-Optionen (Vorschlag, nicht auto-applied): (a) die `## `-Header-Zeile in `classify_diff` als benannte Ausnahme wie `at`-Zeilen klassifizieren, **oder** (b) die Header auf einen Git-Wert pinnen (`git show -s --format=%cs $BASE`), sodass "identische Datumsgruppe" (§5.9/§5.14) verifizierbar statt ungetestet ist; **und** §5.9-Z.277-Wortlaut ("keinen Wanduhr-Wert … nie im Body") mit der Header-Realität in Einklang bringen.
#### F-6 — Unreconciliertes Leasing-Cluster: drei Lock-Modelle koexistieren; §5.18 invertiert §5.12s Gen-only-Stale-Regel ohne Supersession-Notiz
**Quellen (verifiziert):** `schema/compiler.md:327` (§5.12 Pkt. 1), `:419` (§5.17 Pkt. 1), `:428`/`:430` (§5.18 Pkt. 0/Pkt. 1), `:419-424` (§5.17 Pkt. 2).
**Befund:** Drei Lock-Modelle stehen nebeneinander, ohne dass ein sie eindeutig ersetzt: **§5.11** (worktree-lokales Lockfile, **nicht-atomares** check-then-act, TOCTOU), **§5.17** (atomare create-only-Root-Scope-Ref `git update-ref <lock-ref> <wert> $ZERO_SHA`), **§5.18** (Lifecycle/Übernahme). `§5.17` (Z. 417/419) demotiert das §5.11-Lockfile explizit ("trägt aber **keine Exklusivität mehr allein** … TOCTOU-Race … geschlossen"). **Aber** `§5.12 Pkt. 1` (Z. 327) definiert Staleness **allein** generationen-basiert ("Gen kleiner = älter … gilt als **stale** … blockiert keinen nachfolgenden Run"), während `§5.18 Pkt. 1` (Z. 430) dies **invertiert**: eine lebende Lease ist "durch eine höhere sichtbare Generation **allein nicht stale****nie allein aus der Gen-Registry** (§5.12), sondern aus **bestätigtem Abbruch oder abgelaufener Liveness**". `§5.18` (Z. 428) beansprucht zugleich "Fugen-Identität: §5.12 (Pkt. 17) … bleiben **textuell unverändert**" — die **behaviorale** Supersession hat also **keine** Supersession-Notiz, und §5.12s "TTL-Ablauf-Äquivalent"-Wortlaut bleibt wörtlich intakt (leserisch widersprüchlich). Zusätzlich: der **Area-Scope**-Pfad behält das nicht-atomare §5.11-TOCTOU-Modell (nur der Root-Scope bekommt §5.17). **Stufe:** **Defekt** (inkohärentes Sicherheitsmodell; zwei widersprüchliche Staleness-Regeln ohne Supersession). Fix-Option: §5.12 Pkt. 1 um eine Supersession-Notiz zu §5.18 ergänzen (bzw. §5.18 als normative Staleness-Autorität kennzeichnen) + die Root-vs.-Area-Asymmetrie explizit machen.
#### F-7 — Leasing wird von **keinem realen Agent-Lauf** ausgeübt: das Gate (3-13) hat **null** Leasing-Verifikation (missing-adoption)
**Quellen (verifiziert):** `sandbox-3-13/run-sandbox.sh``grep -cE 'update-ref|refs/leases|scopelock|\.lock|lease/|root-scope'` = **0** (in dieser Retro ausgeführt); AGENT_PROMPT (Z. 567-582) verlangt nur `wiki/`-Mutationen + 4-Feld-Receipt, **kein** Lease-Lock-Wert, **keinen** Shared-Ref-Namespace-Check; `sandbox-3-11` (eigene `scopelock_acquire`-Helfer, Z. 124-129), `sandbox-3-12` (`scopelock_`-Helfer, L-2 CAS Z. 367-434), `sandbox-3-5` (check-then-act `acquire()` Z. 206 — verifiziert das **demotierte** Modell).
**Befund:** Die §5.17-atomare Root-Scope-Akquise ist der eigentliche Sicherheitsgewinn des Leasing-Clusters (schließt das TOCTOU-Race). Sie wird aber **nur** von Sandbox-internen Bash-Helfern (3-11/3-12) bewiesen — **kein** realer Agent-Kontext führt sie aus. Das Abnahmegate 3-13, das die realen Agent-Läufe (E.1-E.9) enthält, referenziert **nirgends** `update-ref`/`refs/leases`/`scopelock`/`.lock` (0 Treffer) — seine Agent-Läufe mutieren ausschließlich `wiki/`. Fehlt also in der realen Ausführung der §5.17-Pfad, **schweift** das supersedierte check-then-act-TOCTOU-Modell (§5.11) zurück, ohne dass ein roter Test es auffängt: 3-11 A-2 beweist nur die Atomarität des Git-Ref-Primitivs **in seinem eigenen** Bash-Helfer, nicht, dass der Agent den §5.17-Pfad **annimmt** statt des §5.11-Lockfiles. **Stufe:** **Defekt** (Sicherheits-Invariante "genau ein aktiver Root-Scope-Holder" unverified im realen Execution-Pfad). Fix-Option: ein Gate-Szenario (analog E.9, mit Known-Value-Witness), das in wt-a verifiziert, dass `refs/leases/wiki` existiert **und** `git rev-parse 'refs/leases/wiki^{}'` == `git hash-object` des Run-IDs ist — d. h. der reale Agent hat die scope-bezogene Ref mit Run-ID-Inhalt im geteilten Ref-Namespace gesetzt.
#### F-8 — Gate-Coverage-Gap: der reale Agent-A/B übt nur den UPDATE-Kern; CREATE/Synthese delegiert an agent-less Sub-Runs
**Quellen (verifiziert):** `sandbox-3-13/run-sandbox.sh:23,56-59,477-480` (D-3.13-6 Option 2, Re-Run #5-Befund).
**Befund:** Die einzige reale Agent-A/B (E.1-E.9, AC-6/AC-7) übt den **UPDATE-/Erhaltungs-Kern** (Zuwachs `raw/alpha-v2.md` = bestätigender UPDATE). Der **CREATE-/Synthese-Kern** ist **bewusst** nicht in der A/B-Fixture — aus gutem dokumentierten Grund (Z. 57: "CREATE-Fall 24/26 der NON_AT-Abweichungen treibend", Re-Run #5). CREATE/Synthese gelten stattdessen über die **agent-less** Sub-Runs 3-3/3-4. Das ist eine **bewusste** Scope-Entscheidung (D-3.13-6 Option 2), kein Still-Vorbeilass — **aber** es bedeutet, dass die CREATE-/Synthese-Determinismus-ACs (genau der Fall, der historisch die meisten NON_AT-Abweichungen trieb) **nur mechanisch, nie von einem realen Agent** verifiziert werden. Kombiniert mit F-5 (Dateigruppen-Bruch) und F-7 (kein realer Leasing-Lauf) ergibt sich ein Muster: die real-Agent-Abdeckung ist schmaler, als es die AD-17h-"zwei unabhängige Runs"-Kernclaim des Epics nahelegt. **Stufe:** **Befund/Scope-Decision** (dokumentiert, aber material für die Akzeptanz — die Determinismus-ACs hängen teils an mechanischen Surrogaten). Kein Fix-now; in das Verdict als offene Coverage-Frage einzureichen (s. Open questions).
#### F-9 — Em-Dash-Kollaps-Klasse: Cross-File-Divergenz zwischen `canonical-terms.md` (ohne Em-Dash) und `compiler.md` (mit Em-Dash); append-only-Registry kann sich nicht selbst heilen
**Quellen (verifiziert):** `schema/canonical-terms.md:18,21` (Kollaps-Klasse `[-_ ]`**ohne** Em-Dash) vs. `schema/compiler.md:51,54a` (`[-–— _]`**mit** Em-Dash; "die Kollaps-Klasse ist um den Em-Dash `—` **ergänzt**", Story 3.8).
**Befund:** `canonical-terms.md` ist die **committete, append-only** Registry des Schreibweisen-Resolvers (§3.2) und dokumentiert den Binde-Varianten-Kollaps als `[-_ ]` (En-Dash, Bindestrich, Unterstrich, Leerzeichen — **kein** Em-Dash). Die Instruktion, auf die es sich normativ stützt (`compiler.md` §3.2 Pkt. 1b / §5.14 Pkt. 5, Story 3.8), erweitert die Kollaps-Klasse **um den Em-Dash `—`** (`[-–— _]`) und erklärt die Alt-Fassung für superseded. Weil `canonical-terms.md` **append-only** ist (bestehende Einträge "nie editiert") und die Leer-Registry-Format-Zeile (Z. 18/21) die ältere, Em-Dash-freie Klasse trägt, dokumentiert die Registry eine **schmalere** Kollaps-Klasse als die Instruktion — und kann sich (append-only-Regel) **nicht** selbst korrigieren. Ein Term mit Em-Dash (`wissen — relevanz`) wird von der Instruktion zur selben canonischen Form normalisiert wie seine En-Dash-Variante, aber die Registry-Dokumentation zeigt nicht, dass das so ist. **Stufe:** **Defekt** (normative Cross-File-Inkonsistenz; deterministisch, aber inkonsistent dokumentiert). Fix-Option: `canonical-terms.md`-Formatzeile um den Em-Dash ergänzen (append-only-konform als neue Format-Zeile / Korrekturnotiz) — erfordert eine autorisierte Instruktions-/Registry-Runde (kein stiller Edit, AD-3/D-3).
#### F-10 — Live-Re-Exekution des Abnahmegates ist **ROT** am Kern-AC: ein frischer LLM-Agent no-op-ppte (silent non-execution); der Fail-Closed-Mechanismus hat die Divergenz korrekt erfasst [NEU, empirisch]
**Quellen (in dieser Retro ausgeführt/verifiziert):** Live-Gate-Log `/tmp/epic3-gate-rerun-1787575069.log` — Z. 1234 (`$BASE=6150f943…`), Z. 1242 (`HEAD-A=6150f943…` = **identisch** zu `$BASE`), Z. 1240 („(a) keine Mutationen staged — kein Commit nötig" = **nur** bei wt-a), Z. 1248 (`A/B-Classifier: NON_AT=6 AT=2`), Z. 1249 (`HARD-FAIL (E.4)`); `GATE_EXIT=1` (Task-Output). Committed Gegenbeleg: `/tmp/gate-313-run10.log` Z. 1230 (`NON_AT=0 AT=2`), Z. 1275 (`FAILED=0 PASS_COUNT=51`), Z. 1277 (`SANDBOX-3-13-OK`).
**Befund:** Die **unabhängige Live-Re-Exekution** des Abnahmegates 3-13 (zwei frische Agent-Kontexte A/B via `claude.exe -p --bare`; `CLAUDE_BIN` korrekt auf die echte `claude.exe` v2.1.198, **nicht** der winpty-Shim — s. Log-Z. 5) ist **ROT** an **E.4** (dem **Kern-Determinismus-AC**) — dem **Kern-Determinismus-AC** (AD-17h/FT-10, §5.14): `NON_AT=6 AT=2`, `HARD-FAIL`, `GATE_EXIT=1`. Die Ursache ist **weder F-5** (kein Midnight-/Datumsgruppen-Bruch: beide Läufe am selben Tag 2026-08-24, keine Tagesgrenze) **noch F-8** (kein CREATE-/Synthese-Subpfad — es ist der UPDATE-Kern). Die Ursache ist ein **frischer-LLM-Non-Execution-Fehlschlag**: **Lauf A hat gar keine Mutation ausgeführt** (`HEAD-A` == `$BASE`, „keine Mutationen staged") und **trotzdem** `RUN_DONE a` emittiert, während **Lauf B** den vollen UPDATE ausführte (sources-Zeile `raw/alpha-v2.md`, Body-Änderung, log.md `## 2026-08-24`-Eintrag mit `Baseline $BASE`). Alle 6 NON_AT-Zeilen liegen **einseitig** auf B — es ist **keine** A-gegen-B-Inhaltsdivergenz, sondern **A=leer vs. B=voll**. Entscheidend: **A und B liefen in derselben Umgebung, mit demselben Prompt, auf derselben Worktree-Baseline** — B lief korrekt, A no-op-ppte. Das ist **fresher-LLM-Non-Determinismus auf der Ausführungs-Ebene**: die AD-17h-Aussage „zwei frische Agent-Kontexte … produzieren byte-identische Bundle-States" gilt **nur, wenn beide Kontexte die Arbeit auch ausführen** — und das ist **nicht** garantiert. **Positiver Gegenbefund (Gate-Integrität):** der Fail-Closed-Mechanismus (E.4 `classify_diff``HARD-FAIL`, `exit 1`) hat die Divergenz **korrekt und prompt** erfasst — das Gate ist **nicht** grün-gewaschen; es tut, was §5.14/AD-17h verlangen. **Root-Cause offen:** das Sandbox-Root / `run-a.out`-Transkript des Live-Runs ist **nicht** erhalten (Root aufgeräumt, `find` ohne Treffer), daher **kann** die **warum**-Frage (Agent-„keine Änderung nötig"-Fehleinschätzung, `--bare`-Verhalten, Timeout, Kontext-Verlust) **nicht** abschließend beantwortet werden — der Befund steht auf der **beobachteten** Gate-Evidenz; die Root-Cause-Diagnose bleibt **offen**. **Stufe:** **Defekt (neuer, empirischer)** — der Kern-A/B-Determinismus-AC ist **empirisch nicht robust** über frische LLM-Runs (Committed-Grün ≠ Live-Grün); zusätzlich **Observability-Lücke** (kein erhaltener Agent-Transkript → nicht erklärbar). **Abgrenzung:** F-5 = *Spec-Selbstwiderspruch* der Ausnahme-Menge + ungetestete Tagesgrenze; F-8 = *delegierter* CREATE-Subpfad (agent-less). **F-10** ist **beides nicht**: es trifft den **tatsächlich vom Gate geübten UPDATE-Kern** an der **Ausführungs-Ebene** (Agent no-op) und ist **empirisch beobachtet**, nicht spekulativ. **Fix-Optionen (Vorschlag, nicht auto-applied):** (a) **Run-Vollständigkeits-Guard**: `RUN_DONE` nur, wenn ≥ 1 Mutation staged ist, sonst `RUN_ABORTED` — schließt die A-leer-Maske, dass ein no-op-Agent für „erledigt" gilt; (b) **Divergenz-Retry** bei E.4-HARD-FAIL (divergierenden Lauf max. N× neu, mit expliziter Retry-Protokollierung — keine stille Wiederholung); (c) die **AD-17h-Claim-Schärfe** um die **Ausführungs-Voraussetzung** erweitern („zwei frische Kontexte, *die die Mutation beide ausführen*"); (d) **Agent-Transkript-Erhaltung** im Gate (`run-a.out`/`run-b.out` im Sandbox-Root sichern, nicht aufräumen) für Root-Cause-Analyse.
## Behavior verification
**Methode:** Die 12 rein-shell-Subsuiten (3-1…3-12) wurden in dieser Retro **unabhängig re-executiert** (frisch, kein Committed-Beleg) — jede mit `Exit 0`. Das Abnahmegate `sandbox-3-13` (LLM-gesteuert: Sektion D Validator-Agent + Sektion E zwei frische Agent-Kontexte A/B via `timeout 2700 "$CLAUDE_BIN" -p --bare`) wurde als End-to-End-Re-Run dieser Retro **ausgeführt** und ist **abgeschlossen**; `CLAUDE_BIN` auf die echte `claude.exe` (v2.1.198) gesetzt, da der winpty-Shim `C:\Users\mita\bin\claude` in headless `-p`-Runs schlägt (Gate hat P-3-Override; siehe Memory `wow2-claude-shim-winpty-headless`). **Ergebnis: ROT — `HARD-FAIL (E.4)`, `GATE_EXIT=1`** (s. F-10 und unten).
**Subsuiten — harte Re-Exekution (alle `Exit 0`):**
| Suite | Szenarien | Ergebnis | Bemerkung |
|---|---|---|---|
| 3-1 | V-1 (Vertrag §2) + 13 weitere | PASS 14/14 | Run-FAIL bei fehlender Bundleroot korrekt ausgelöst; FAILED=0 |
| 3-2 | 7 Tests | PASS | abgeschlossen |
| 3-3 | U1U9 | PASS | unbefugter Frontmatter-Key → Struktur-HARD-FAIL (Pkt. 8) |
| 3-4 | S1S6 + N1N3 + Form-Wahl | PASS | Duplikat/Ghost-Diff-??-Negativkontrolle nicht vacuous |
| 3-5 | L1L6 + N1 + D1 + D2 | PASS | Leasing |
| 3-6 | STALE-1…STALE-6 | PASS | Leasing-Staleness |
| 3-7 | CONSIST-1…7 | PASS | VALIDATION_FAIL → §6-Pkt.-3-Rollback, AD-17f Teilerfolg-Never |
| 3-8 | DET-1…DET-8 | PASS | ORPHAN datumsgruppierter log-Eintrag, Erhaltungs-Invariante (nur log.md mutiert) |
| 3-9 | R-1, R-1b, R-2…R-9 | PASS | 0 HARD-FAIL |
| 3-10 | E-1…E-9 (Zwei-Run-Identität) | PASS 9/0 | **E-9: A==B byte-identisch, nur `generated.at` differiert; „Nicht-Vakuum bei Zustandswechsel"** — A==B-Assertion ist real, nicht tautologisch |
| 3-11 | A-1…A-8 | PASS | atomare Root-Scope-Lease-Akquise (§5.17 Rev 3.6) |
| 3-12 | L-1…L-9 | PASS | 9 harte Assertions, self-contained /tmp-Bundle |
**LLM-Gate 3-13 — Live-Re-Exekution dieser Retro (ABGESCHLOSSEN, ROT):**
| Phase | Ergebnis |
|---|---|
| A Setup (A.2A.4) | PASS (A.5 Pre-Check `FAIL` = mein uncommittetes Retro-Dokument hat das real-Repo-Porcelain verschmutzt; `fail()` ist **nicht** fatal — Zähler-Bemerkung, Gate läuft weiter) |
| BC (12 Sub-Runs) | PASS — 3-1…3-12 alle `OK`, C.12 „ALLE 12 Sub-Runs grün" |
| D Validator (D.1D.6) | PASS — 4/4 `SUCCESS` |
| D-NEG | PASS — 3×`FAIL` + 1×`SUCCESS` (Positiv-Kontrolle) |
| E.1E.3 (Fixture, $BASE, Worktrees) | PASS — `$BASE=6150f943…` |
| E.4 A/B-Vergleich | **HARD-FAIL**`NON_AT=6 AT=2`; `HEAD-A==BASE` (Lauf A no-op), Lauf B voll UPDATE; **`GATE_EXIT=1`** |
**Verhaltens-Fazit (korrigiert nach Live-Ergebnis):** Das **deterministische Rückgrat** des Epics (inkrementeller Dataflow, textuelle Relevance, atomare Lease, deterministischer Rollback) ist **unabhängig reproduzierbar grün** (12/12 Subsuiten `Exit 0`; im Gate: C 12/12, D, D-NEG) und — entscheidend für AD-13/AD-17h — die A==B-Determinismus-Assertion wird in 3-10 **nicht-vakuum** ausgeübt (Zustandswechsel erzwingt echte Byte-Äquivalenz statt Trivials). **Aber** die **live** End-to-End-Abnahme (zwei *frische LLM-Agent-Kontexte* auf dem UPDATE-Kern) ist **an E.4 ROT**: Lauf A no-op-ppte, Lauf B lief korrekt (F-10). Das heißt konkret: **das deterministic Kernel ist grün, die frische-LLM-Ausführung des Kern-ACs ist es empirisch nicht robust** — und zwar **am selben Tag, ohne Midnight-Bruch** (also **nicht** F-5) und **im UPDATE-Kern, nicht im delegierten CREATE-Subpfad** (also **nicht** F-8). **Das authoritative grüne Gate bleibt der committed Re-Run #10 (2026-08-24, `09c1c83`): `FAILED=0 PASS_COUNT=51`, `SANDBOX-3-13-OK`, Exit 0** (`/tmp/gate-313-run10.log` Z. 1230/1275/1277). Die Live-Re-Exekution ist eine **unabhängige Wiederholung** — und sie **bestätigt** das Kern-Risiko, das F-5/F-8 spekulativ benannt haben: der frische-LLM-Pfad ist nicht deterministisch robust. Sie **ersetzt** aber nicht den grünen Committed-Beleg, auf dem das Verdikt steht; sie **verstärkt** jedoch die Argumentation, dass F-5 (und neu F-10) als *blocking* eingestuft werden sollten.
## Action items
Jedes Item: Befund-Ref + Disposition (fix-now → Action-Item; defer → mit Kontext nachführbar) + Owner. **Remediation und Spec-Reconciliation sind hier *proponiert*, nicht auto-applied** — ProMods entscheidet über die Ausführung (AD-3/D-3: `compiler.md`/`validator.md`/`canonical-terms.md` sind read-only; eine Spec-Änderung erfordert eine autorisierte Revision).
| ID | Befund | Action | Owner | Art |
|---|---|---|---|---|
| **AI-3-R-1** | F-5 | **Dateigruppen-Determinismus heilen:** die `## YYYY-MM-DD`-Header in `wiki/log.md` **entweder** auf einen Git-Wert pinnen (`git show -s --format=%cs $BASE` in §5.9 Pkt. 4 / AGENT_PROMPT) **oder** die `## `-Header-Zeile in `classify_diff` (3-13:650-653) als benannte Ausnahme wie `at`-Zeilen klassifizieren; **und** den §5.9-Z.277-Wortlaut („keinen Wanduhr-Wert … nie im `log.md`-Body") mit der Header-Realität in Einklang bringen. Schließt den §5.14-„genau eine Ausnahme"-Selbstwiderspruch. | ProMods (Autorisierung) + dev | **Spec-Reconciliation (fix-now-Vorschlag)** |
| **AI-3-R-2** | F-6 | **§5.12↔§5.18-Staleness-Reconciliation:** in §5.12 Pkt. 1 (Z. 327, „TTL-Ablauf-Äquivalent" = Gen-only-stale) eine Supersession-Notiz zu §5.18 Pkt. 1 (Z. 430) + Story 3.12-AC (Z. 426: Stale verlangt bestätigten Abbruch/abgelaufene Liveness) ergänzen; §5.18 als normative Staleness-Autorität kennzeichnen. | ProMods + dev | Spec-Reconciliation (defer) |
| **AI-3-R-3** | F-7 | **Gate-Leasing-Verifikation ergänzen:** ein 3-13-Szenario (analog E.9, Known-Value-Witness), das im realen Agent-Lauf (wt-a) verifiziert: `refs/leases/wiki` existiert **und** `git rev-parse 'refs/leases/wiki^{}'` == `git hash-object` der Run-ID — beweist, dass der reale Agent den §5.17-atomaren Pfad annimmt (nicht das supersedierte §5.11-check-then-act-Lockfile). | dev+review | Remediation (Gate-Härtung, defer) |
| **AI-3-R-4** | F-9 | **Em-Dash-Kollaps-Klasse Cross-File-Reconciliation:** `canonical-terms.md`-Formatzeile (Z. 18/21, `[-_ ]`) append-only-konform um den Em-Dash `—` ergänzen (Korrekturnotiz auf die §3.2/§5.14-Z.51/54a-Klasse `[-–— _]`). | ProMods + dev | Spec-Reconciliation (defer) |
| **AI-3-R-5** | F-1 | **`norm()`-Vereinheitlichung + `LC_ALL`-Pinning:** die 4 drifteten `norm()`-Kopien (3-8/3-9/3-10/3-11) auf eine kanonische Implementierung (mit Case-Folding) vereinheitlichen + `LC_ALL=C`-Pinning (3-11 ist heute `LC_ALL=0`). Sharpening des bekannten D-7/W-3.9-1-Defers. | dev | **Defer** (Home: Sandbox-Härtung) |
| **AI-3-R-6** | F-8 | **Fresh-LLM-CREATE-Residual** (Nutzer-Defer D-3.13-L2-3): der nicht-deterministische CREATE-/Synthese-Unterpfad (Re-Run-#5: 24/26 NON_AT) bleibt benanntes Residual; Substitutions-Notiz (mechanisch gepinnt ≙ frischer LLM für die *Mechanik-Struktur*) ist ausgeführt. Kandidat für spätere compiler.md-Revision oder Epic-4-Determinismus-Überarbeitung. | ProMods / Epic 4 | **Defer (Nutzer-Entscheidung, bereits dokumentiert)** |
| **AI-3-R-7** | F-2/F-3 | **Beobachtung (kein fix-now):** `compiler.md` §5-Wachstum (5→15 Sektionen, 548 Z.) + nicht zusammenhängende §5-Nummerierung (5.10 = Story 3.4 …). Für eine spätere strukturelle Revision notiert. | — | **Accept-as-is / observe** |
| **AI-3-R-8** | **F-10** (NEU, empirisch) | **Frischer-LLM-Non-Execution absichern (Kern-AC-Robustheit):** (a) **Run-Vollständigkeits-Guard** im Gate — `RUN_DONE` nur, wenn ≥ 1 Mutation staged, sonst `RUN_ABORTED` (schließt die „no-op-Agent gilt als erledigt"-Maske); (b) **Divergenz-Retry** bei E.4-HARD-FAIL (max. N×, explizit protokolliert, keine stille Wiederholung); (c) **AD-17h-Claim-Schärfe** um die Ausführungs-Voraussetzung erweitern; (d) **Agent-Transkript-Erhaltung** im Gate (`run-a.out`/`run-b.out` nicht aufräumen) → Root-Cause-Diagnose (aktuell offen, Root aufgeräumt). | dev+review | **Remediation (Gate-Härtung; fix-now-Vorschlag, da empirisch am Kern-AC)** |
**Carry-forward (nicht neue Epic-3-Items):** die 4 offenen Epic-2-Items **AI-2-R-1…4** (`epic-2-retro-item-10…13`, s. Previous-retro follow-through) bleiben `open` — sie betreffen §5.5/§5.6/§5.8 (Bestands-Sektionen, nicht im Epic-3-Scope). Empfehlung: in einer Folge-Story „Instruktion-/Sandbox-Vereinheitlichung" **gebündelt** mit AI-3-R-5 (`norm()`/`LC_ALL`) abarbeiten (s. Open questions).
## Acceptance verdict
**Verdikt: `accepted-with-open-items`** · **Kriterien: `declared`** (ACs je Story in `planning-artifacts/epics.md` Epic-3-Block Z. 259451) · **pending_stories: leer** (alle 13 Stories 3.13.13 `done`, `detect-epic --epic 3`, 2026-08-24) → **kein Machine-Zwang zu `rejected`**.
**Begründung (Evidenz):**
1. **Kriterien demonstrativ erfüllt:**
- **Abnahmegate (Story 3.13) grün auf Re-Exekution:** Committed **Re-Run #10 (2026-08-24, `09c1c83`): `FAILED=0 PASS_COUNT=51`, SANDBOX-3-13-OK, Exit 0** — A.5 Pre-Check, C 12/12, D 4/4 SUCCESS, D-NEG 3×FAIL+1×SUCCESS, E.4 `NON_AT=0 AT=2` (Bundle-State identisch bis auf `at:`), E.6 Feldsatz-exakt + Known-Value-Witness, E.9 kanonische `Baseline $BASE`-Form, F G-1..G-8, G-8 Porcelain-Clean + AD-3 ohne Diff. **In dieser Retro zusätzlich:** alle 12 deterministischen Subsuiten (3-1…3-12) **unabhängig re-executiert → alle `Exit 0`**; das LLM-Gate 3-13 wurde als End-to-End-Re-Run **ausgeführt** und ist **abgeschlossen — aber ROT an E.4** (`NON_AT=6 AT=2`, `GATE_EXIT=1`; Lauf A no-op, Lauf B korrekt — **F-10**). Der grüne Committed-Beleg (Re-Run #10) steht weiterhin; die Live-Re-Exekution **widerlegt** ihn nicht, **bestätigt** aber das Kern-Risiko.
- **Determinismus-Kern nicht-vakuum (F-4):** die A==B-Assertion (3-10 E-9) erzwingt echte Byte-Äquivalenz, nur `generated.at` differiert, Zustandswechsel-Nicht-Vakuum ausgeübt — AD-17h/FT-10 halten mechanisch.
- **Leasing-Semantik (3-5/3-6/3-11/3-12) grün:** atomare Akquise (3-11 A-1..A-8), Staleness/Recovery (3-6 STALE-1..6), Lifecycle/Rollback/Release (3-12 L-1..L-9).
- **Textuelle Determinismus-Relevance, kein Embedding (3-2/3-8/3-9/3-10):** AD-13 in allen Läufen gewahrt.
2. **Offene, benannte, getrackte Befunde (kein `accepted` — deshalb `-with-open-items`):** F-5, F-6, F-7, F-9 sind **keine** „Epic erledigt, kleine Nachträge" — sie sind reale Defekte/Verifikations-Lücken, alle mit Quelle, Owner und Disposition (AI-3-R-1…6). **F-10 (neu, empirisch):** die Live-Re-Exekution des Gates ist ROT am Kern-AC — frischer-LLM-Non-Execution auf der Ausführungs-Ebene (AI-3-R-8). F-8 ist ein **Nutzer-Defer** (D-3.13-L2-3). F-1/F-2/F-3 sind Latenz/Beobachtung.
**Die zwei material-entscheidenden Items — F-5 (Midnight-Dateigruppen-Bruch, Spec) + F-10 (Live-Kern-AC-ROT, empirisch):** Der §5.14-Determinismus-Vertrag ist der **Kern-AC** des Epics (AD-17h, FT-10, AC-1/AC-6). F-5 zeigt, dass er im **Wortlaut selbst widersprüchlich** ist (erlaubt „genau eine" Ausnahme = `generated.at`, verlangt aber eine zweite, ungepinnte Wanduhr-Wand im `log.md`-Body via §5.9 Pkt. 4) und an der Tagesgrenze **ungeschützt/ungetestet**. F-10 zeigt, dass der **Mechanismus** über frische LLM-Runs **empirisch nicht robust** ist (Live-Run: Lauf A no-op, Lauf B korrekt, E.4 HARD-FAIL, `GATE_EXIT=1`). Zusammen: der Kern-AC hat **sowohl** eine **Spec-Lücke** (F-5) **als auch** eine **Ausführungs-Verlässlichkeits-Lücke** (F-10). Beide sind durch autorisierte Revisionen (AI-3-R-1) bzw. Gate-Härtung (AI-3-R-8) zu schließen. Das rechtfertigt **`accepted-with-open-items`** (nicht `accepted`): die Befunde sind getrackt, bequellen, haben Owner und Fix-Pfade — aber sie sind **nicht** „kleine Nachträge".
**Eskalationsgrenze (ProMods-Entscheidung):** F-5 **und** F-10 sind die Befunde, die dieses Verdikt von `accepted-with-open-items` auf **`rejected`** kippen *könnten* — wenn ProMods sie als **blocking** einstuft. F-5 = Selbstwiderspruch im *Kern*-Determinismus-AC; F-10 = der *Kern*-Determinismus-AC ist **empirisch** (nicht nur spekulativ) nicht robust über frische LLM-Runs. Dazu F-7: die sicherheitskritische Leasing-Invariante „genau ein aktiver Root-Scope-Holder" wird nur durch Sandbox-interne Bash-Helfer, nicht durch den realen Agent-Lauf verifiziert. Das ist eine bewusste Abwägung: der **deterministische Kern** ist grün (12/12 Subsuiten, Re-Run #10 grün) und das Gate ist **fail-closed-korrekt** (F-10 wurde *erfasst*, nicht verschwiegen); die Befunde sind getrackt, bequellen und haben Fix-Pfade. **Entscheidung ProMods (2026-08-24):** F-5/F-10 = **`tracked gap`** → das Verdikt **`accepted-with-open-items`** ist final. Konsequenz: AI-3-R-8 (F-10-Ausführungs-Robustheit) ist **Voraussetzung** dafür, dass Epic 4 auf deterministischem CREATE/Synthese aufbaut (Open question 3) — ein spätes Umstufen auf *blocking* würde Epic 4 halten.
## Open questions
1. **F-5 + F-10: `gap` oder `blocking`? (gemeinsam die Eskalationsgrenze)****F-5** = Selbstwiderspruch im Kern-Determinismus-AC (§5.14 „genau eine Ausnahme" vs. §5.9 Pkt. 4 zweite Wanduhr-Wand) + ungeschützte Tagesgrenze (*Spec-Ebene*). **F-10** = derselbe Kern-AC ist **empirisch** nicht robust über frische LLM-Runs (Live-Re-Exekution ROT an E.4, Lauf A no-op) (*Ausführungs-Ebene*). **Diese Einstufung ist eine ProMods-Entscheidung:** als *blocking* → Verdikt **`rejected`** (Epic 4 wird gehalten, da es an den §5.14-Vertrag andockt); als *tracked gap***`accepted-with-open-items`** (Empfehlung dieser Retro, da der deterministische Kern grün ist — 12/12 Subsuiten + Re-Run #10 — und das Gate **fail-closed** die Divergenz *erfasst* hat, nicht verschwiegen; F-5 liegt in der *Spec-Schärfe*, F-10 in der *Ausführungs-Verlässlichkeit*, beide mit Fix-Pfaden AI-3-R-1 / AI-3-R-8). — **Entscheidung ProMods (2026-08-24):** ***tracked gap*** („Ich folge Deiner Empfehlung") → Verdikt **`accepted-with-open-items`** ist **final**; Bedingung: AI-3-R-8 (F-10, Ausführungs-Robustheit) **vor dem CREATE-Aufbau von Epic 4** abarbeiten.
2. **F-10 Root-Cause offen** — Der Lauf-A-Non-Execution-Treiber ist **nicht** abschließend bestimmbar (Sandbox-Root / `run-a.out` aufgeräumt, kein erhaltener Agent-Transkript). Soll (a) das Gate **Agent-Transkripte erhalten** (AI-3-R-8 d), damit ein nächster roter Live-Run **diagnostizierbar** ist, und/oder (b) ein **erneuter kontrollierter Live-Run** (mit Transkript-Erhaltung) als Root-Cause-Nachweis gefahren werden? Empfehlung: (a) sofort (Gate-Härtung, klein), (b) optional als Verifikation.
3. **F-8: CREATE-/Synthese-Coverage** — Der fresh-LLM-CREATE-Unterpfad (24/26 NON_AT, Re-Run #5) ist als **Nutzer-Defer (D-3.13-L2-3)** dokumentiert. Soll das Residual in **Epic 4** (Determinismus-Überarbeitung) oder in einer **separaten compiler.md-Revision** vor Epic 4 gelandet werden? (Empfehlung: vor Epic 4, da Epic 4 auf deterministischem CREATE aufbauen wird.) — *Verstärkt durch F-10:* da die frische-LLM-Ausführung empirisch nicht robust ist (F-10), ist die CREATE-Coverage-Lücke (F-8) **nicht** nur eine delegierte Mechanik, sondern teilt dieselbe Ausführungs-Verlässlichkeits-Riskoklasse.
4. **Gebündelte Spec-Vereinheitlichung:** die 4 offenen **Epic-2-Items (AI-2-R-1…4)** + **AI-3-R-5** (`norm()`/`LC_ALL`) + **AI-3-R-4** (Em-Dash) betreffen alle die **§5-Formel-/Normalisierungs-Fläche** bzw. die Kollaps-Klasse. Empfehlung: **eine** „Instruktion-/Sandbox-Vereinheitlichungs"-Story in Epic 4 (oder als Epic-3.5-Hotfix) bündelt sie — statt je eigener Revision. ProMods: bündeln oder einzeln?
5. **Session-Logs fehlen:** Prozess-Lektionen (warum Story 3.13 ≥ 6 Re-Runs + 2 Review-Loops brauchte) sind nur aus dem git-loggbaren Verhalten ableitbar (Commit-Kette vollständig); keine Session-/Transcript-Evidenz in dieser Umgebung. Kein Befund davon abgeleitet.
## Previous-retro follow-through
**Basis:** `sprint-status.yaml``action_items` (Epic 2), gekreuzt gegen `schema/compiler.md` (Ist-Zustand, `efc543c^..3f57586`-Range) und `epic-2-retro-2026-08-18.md`. Die 5 Epic-2-Aktionsitems (AI-2-R-1…5) sind in `sprint-status.yaml` als `epic-2-retro-item-10…14` / `code-review-2-1-item-*` gebucht.
| Epic-2-Item | AI-Ref | Inhalt | Ist-Zustand (Epic 3-Ende) | Status |
|---|---|---|---|---|
| `epic-2-retro-item-10` | AI-2-R-1 (F-01) | `§5.5`-Selbsttest-Formel rekursiv machen (`grep -rnE (raw/` über `wiki/` inkl. Area-Concepts) | **nicht umgesetzt**`compiler.md:104`/`127` steht weiterhin bei `grep -nE '\(raw/' wiki/*.md` (Nicht-rekursiv, `wiki/*.md` greift keine `wiki/<area>/*.md` an) | **OPEN** (übernommen, nicht gelöst) |
| `epic-2-retro-item-11` | AI-2-R-2 (F-03) | `§5.8`-Lauf A um Area-Concept-ohne-`index.md` (Tiefe-2, `wiki/<a>/concept.md` ohne `wiki/a/index.md`) erweitern | **nicht umgesetzt**`compiler.md:239` Lauf (A) scannt weiterhin nur `-name index.md`; Lauf (B) (`:239`, `-mindepth 3`) erfasst nur Tiefe ≥ 3 → ein Tiefe-2-Concept ohne `index.md` ist beiden Läufen unsichtbar | **OPEN** (übernommen, nicht gelöst) |
| `epic-2-retro-item-12` | AI-2-R-3 (F-04) | `§5.6`-Formel-4-Filter-Asymmetrie heilen (`--exclude=log.md` vs `grep -v log.md$` auf eine Semantik) | **nicht umgesetzt**`compiler.md:185` (Ist) `--exclude=log.md` vs `:186` (Baseline) `grep -v "log.md$"` — die Asymmetrie (Basename-Basename-Exklusion vs Anker-Basename-Filter) steht weiterhin side-by-side | **OPEN** (übernommen, nicht gelöst) |
| `epic-2-retro-item-13` | AI-2-R-4 (F-05) | `§5.8`-Reachability als echte Markdown-Links prüfen + `./`-Variante mit `§5.6`-Pin vereinheitlichen | **teilweise** — „echte Markdown-Links"-Hälfte umgesetzt (Loop-3-Fix, `:244`: prüft jetzt Linksyntax `](` statt bloßem Klammer-Paar); **`./`-Vereinheitlichung nicht umgesetzt** — `:239` Lauf A akzeptiert weiterhin `](./$a/index.md)`, während der `§5.6`-Form-Check (`:169`) `./`-Präfix-Ziele via `grep -vE '^\.'` aus der Form-Zählung nimmt → die `./`-Asymmetrie bleibt | **OPEN** (Halbteil gelöst, Kern-`./`-Teil übernommen, nicht gelöst) |
| `epic-2-retro-item-14` | AI-2-R-5 (F-02) | Autorisierte Validator-Rev-9 für Punkt-11-Area-Lesart | **umgesetzt**`validator.md` Revision 9, Punkt-11-Area-Lesart formalisiert (file-relative Referenz in `index.md`); zertifiziert | **done** (closed 2026-08-18) |
**Fazit:** Von den 5 Epic-2-Retro-Items ist **1 (AI-2-R-5) geschlossen** (bereits 2026-08-18, vor dem Epic-3-Range) und **4 (AI-2-R-1…4) sind nach wie vor offen** — alle vier betreffen **Bestands-Sektionen** (`§5.5`/`§5.6`/`§5.8`), die Epic 3 **nicht im Scope hatte** (Epic 3 = neue Sektionen `§5.9``§5.18` + Determinismus-Vertrag `§5.14`/`§6.5`). Der Churn an `compiler.md` in Epic 3 (21 Commits) traf diese drei Self-Test-Formeln nicht, daher blieben die Lücken strukturell erhalten. Das ist **kein** Epic-3-Defekt (die Items waren korrekt an Epic 3 *angehängt* als `open`), aber es bestätigt den in F-2 genannten Wartungspunkt: die **§5-Formel-Fläche** ist der natürliche Ort, an dem Epic-übergreifende Self-Test-Lücken akkumulieren — eine Folge-Story/Revision sollte die 4 offenen AI-2-R-Items gebündelt abarbeiten (natürliche „Sandbox-/Instruktion-Vereinheitlichung"-Home, s. `deferred-work.md`).
**Kein Status-Transition-Vorschlag an `sprint-status.yaml` für die Epic-2-Items** — die 4 offenen bleiben `open` (nachweislich nicht gelöst), `item-14` ist bereits `done`.
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
# Story 3.1 — Sandbox-Edge-Tests der I/O-Matrix (fünf Szenarien) + D-3-Abbruch-Kontrolle
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb31)
# Review-Loop-2-Härtung (Story 3.13, D/Patch P-1): harte Assertions mit Exit-Pfaden —
# jede Erwartung wird geassertet (Abweichung = Non-Zero-Exit), konsistent mit 3-2..3-12.
set -u
ROOT=$(mktemp -d /tmp/sb31-XXXXXX)
SB="$ROOT/sb"
@@ -9,6 +11,12 @@ cd "$SB"
git init -q
git config user.email "sandbox@test"
git config user.name "Sandbox"
FAILED=0
PASS_COUNT=0
pass() { PASS_COUNT=$((PASS_COUNT+1)); echo "PASS: $1"; }
fail() { FAILED=$((FAILED+1)); echo "FAIL: $1" >&2; }
# hardfail: Setup-/Infrastrukturfehler brchen sofort ab (kein Szenario-Exit).
hardfail() { echo "HARD-FAIL: $1" >&2; exit 1; }
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit) ----------
cat > wiki/index.md <<'EOF'
@@ -50,8 +58,9 @@ cat > raw/beta-v1.md <<'EOF'
Evidenz v1: Beta-Thema (Stelle S-1).
EOF
git add -A
git commit -qm "Baseline"
git commit -qm "Baseline" || hardfail "Baseline-Commit fehlgeschlagen"
BASE=$(git rev-parse HEAD)
[ -n "$BASE" ] || hardfail "Baseline-Commit nicht auflösbar"
echo "BASELINE-COMMIT: $BASE"
echo
runlabel() { echo; echo "########## $1 ##########"; }
@@ -61,6 +70,12 @@ isolate() { git checkout -q -b "$1" "$BASE" 2>/dev/null || git checkout -q "$1";
probe() { # Pkt. 5-Probe (D-1-Form): Baseline-Diff + porcelain, normalisiert (wiki/-Praefix + .md gestrippt)
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
# expect_eq: $1=Beschreibung, $2=erwartet, $3=ist (jeweils mehrzeilig)
expect_eq() {
if [ "$2" = "$3" ]; then pass "$1"; else
fail "$1 — erwartet: [$2], ist: [$3]"
fi
}
# =====================================================================
runlabel "S1: HAPPY_PATH_UPDATE (bestehendes Concept wird im bestehenden Pfad aktualisiert)"
@@ -68,7 +83,7 @@ runlabel "S1: HAPPY_PATH_UPDATE (bestehendes Concept wird im bestehenden Pfad ak
cat > raw/alpha-v2.md <<'EOF'
Evidenz v2: quanten-protocol-schlüssel erfaehrt eine Rotation (Stelle S-2).
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md" || hardfail "Zuwachs-Commit fehlgeschlagen"
# Reconcile: Kandidatenliste (textuell-deterministisch, AD-13)
echo "--- Kandidaten-Erhebung: rg -l 'quanten-protocol-schlüssel' wiki/ ---"
RG -l 'quanten-protocol-schlüssel' wiki/ || true
@@ -81,11 +96,17 @@ cat >> wiki/log.md <<EOF
- Story 3.1-Update: alpha (raw/alpha-v2.md; Baseline $BASE)
EOF
echo "--- Probe (git diff --name-only <BASE> -- wiki/ + git status --porcelain -- wiki/, normalisiert) ---"
probe
echo "--- Erwartet: log, alpha (betroffen); beta NICHT; keine neue Datei (porcelain o.??) ---"
git status --porcelain -- wiki/
echo "--- Duplikat-Check: existiert alpha.md weiterhin exakt 1x? ---"
ls wiki/alpha.md; git status --porcelain -- wiki/ | grep -c '^??' || echo "0 untracked"
S1_PROBE=$(probe)
echo "$S1_PROBE"
expect_eq "S1 Probe: genau alpha + log betroffen, beta NICHT" "$(printf 'alpha\nlog')" "$S1_PROBE"
# Keine neue Datei (Duplikat-Kontrolle): kein ?? unter wiki/
S1_UNTRACKED=$(git status --porcelain -- wiki/ | grep -c '^??' || true)
[ "$S1_UNTRACKED" -eq 0 ] && pass "S1 keine neue Datei unter wiki/ (kein Duplikat)" \
|| fail "S1 unerwartete untracked wiki/-Datei(en): $S1_UNTRACKED"
# alpha.md existiert exakt 1x
S1_ALPHA_COUNT=$(git ls-files wiki/alpha.md | wc -l | tr -d ' ')
[ "$S1_ALPHA_COUNT" -eq 1 ] && pass "S1 alpha.md ist exakt 1x getrackt" \
|| fail "S1 alpha.md getrackt: $S1_ALPHA_COUNT (erwartet 1)"
# =====================================================================
runlabel "S2: UNTOUCHED_CONCEPT (neue Evidenz betrifft kein bestehendes Concept)"
@@ -96,10 +117,13 @@ EOF
git add -A; git commit -qm "Zuwachs raw/gamma.md"
echo "--- Kandidaten-Erhebung: rg -l 'gamma-observatorium-thema' wiki/ ---"
RG -l 'gamma-observatorium-thema' wiki/ || echo "(leer — kein Kandidat)"
echo "--- Folge: keine Mutation, kein log.md-Zusatz (UNTOUCHED_CONCEPT) ---"
echo "--- Probe ---"
probe
echo "(leere Ausgabe = kein Ghost-Diff; leere Menge ist Teilmenge jeder erlaubten Menge)"
# Folge: keine Mutation, kein log.md-Zusatz (UNTOUCHED_CONCEPT)
S2_PROBE=$(probe)
echo "--- Probe: [$S2_PROBE] ---"
expect_eq "S2 Probe: leer (kein Ghost-Diff, UNTOUCHED_CONCEPT)" "" "$S2_PROBE"
S2_LOG_DIFF=$(git diff "$BASE" -- wiki/log.md | wc -l | tr -d ' ')
[ "$S2_LOG_DIFF" -eq 0 ] && pass "S2 log.md unverändert (kein Eintrag ohne Kandidat)" \
|| fail "S2 log.md mutierte ohne Kandidat (S2_LOG_DIFF=$S2_LOG_DIFF)"
# =====================================================================
runlabel "S3: CONCEPT_COLLISION_BESTEHEND (Zielpfad belegt -> Update-Routing, kein Duplikat)"
@@ -109,16 +133,26 @@ Evidenz v2: quanten-protocol-schlüssel (Stelle S-2, ergaenzend).
EOF
git add -A; git commit -qm "Zuwachs"
echo "--- Vor-Mutation-Check: Ziel-Pfad wiki/alpha.md belegt? ---"
test -f wiki/alpha.md && echo "JA — Update-Routing (kein Duplikat, kein stummer Ueberschreiben)"
test -f wiki/alpha.md && echo "JA — Update-Routing (kein Duplikat, kein stummer Ueberschreiben)" \
|| fail "S3 Zielpfad wiki/alpha.md nicht belegt (Szenario-Setup verletzt)"
echo "--- Vor-Mutation-Zustand (muss leer sein) ---"
git status --porcelain -- wiki/; echo "(leer)"
echo "--- Mutation im bestehenden Pfad (Update) ---"
S3_PRE=$(git status --porcelain -- wiki/)
echo "$S3_PRE"
[ -z "$S3_PRE" ] && pass "S3 Vor-Mutation-Zustand leer" || fail "S3 Vor-Mutation-Zustand nicht leer: $S3_PRE"
# Mutation im bestehenden Pfad (Update)
echo "Ergaenzung (raw/alpha-v2.md#S-2)." >> wiki/alpha.md
echo "## 2026-08-19" >> wiki/log.md; echo "- Story 3.1-Update: alpha (raw/alpha-v2.md)" >> wiki/log.md
echo "--- Nach-Mutation: nur M-Eintraege, KEIN ?? (keine neue Datei = kein Duplikat) ---"
git status --porcelain -- wiki/
echo "--- Probe ---"
probe
S3_PORC=$(git status --porcelain -- wiki/)
echo "$S3_PORC"
S3_UNTRACKED=$(echo "$S3_PORC" | grep -c '^??' || true)
[ "$S3_UNTRACKED" -eq 0 ] && pass "S3 keine ??-Einträge (kein Duplikat)" \
|| fail "S3 unerwartete ??-Einträge: $S3_PORC"
S3_ONLY_M=$(echo "$S3_PORC" | grep -cv '^ M ' || true)
[ -n "$S3_PORC" ] && [ "$S3_ONLY_M" -eq 0 ] && pass "S3 nur M-Einträge (Update im bestehenden Pfad)" \
|| fail "S3 Porcelain enthält Einträge außerhalb ' M ' (nur-$S3_ONLY_M-Andere)"
S3_PROBE=$(probe)
expect_eq "S3 Probe: genau alpha + log" "$(printf 'alpha\nlog')" "$S3_PROBE"
# =====================================================================
runlabel "S4: CHANGE_DETECTION (R-1: nur der Zuwachs wird als Evidenz interpretiert)"
@@ -128,22 +162,35 @@ Evidenz v2: nur diese Datei ist neu (Stelle S-2).
EOF
git add -A; git commit -qm "Zuwachs"
echo "--- git diff --name-only <BASE> -- raw/ ---"
git diff --name-only "$BASE" -- raw/
echo "--- Erwartet: AUSSCHLIESSLICH raw/alpha-v2.md (alpha-v1.md/beta-v1.md bleiben aussen) ---"
S4_ADD=$(git diff --name-only "$BASE" -- raw/)
echo "$S4_ADD"
expect_eq "S4 Zuwachs: ausschließlich raw/alpha-v2.md" "raw/alpha-v2.md" "$S4_ADD"
echo "--- SHA-256-Abgleich (Sekundaer-Fingerprint, D-2): alpha-v1 unveraendert? ---"
sha256sum raw/alpha-v1.md
git show "$BASE:raw/alpha-v1.md" | sha256sum
echo "--- (identische Summen = unveraendert; bei Diskrepanz gewinnt git diff, D-2) ---"
S4_SHA_NOW=$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)
S4_SHA_BASE=$(git show "$BASE:raw/alpha-v1.md" | sha256sum | cut -d' ' -f1)
echo "$S4_SHA_NOW / $S4_SHA_BASE"
[ -n "$S4_SHA_NOW" ] && [ "$S4_SHA_NOW" = "$S4_SHA_BASE" ] \
&& pass "S4 raw/alpha-v1.md byte-identisch zur Baseline (Immutabilität)" \
|| fail "S4 raw/alpha-v1.md weicht von der Baseline ab ($S4_SHA_NOW != $S4_SHA_BASE)"
# =====================================================================
runlabel "S5: PRE_RUN_RECONCILE (Check-Block vor Mutation; fehlende Bundleroot -> Run-FAIL V-1)"
isolate s5
rm wiki/index.md # Szenario-Setup: Bundleroot fehlt (simuliert, kein Run-Zustand)
echo "--- Check-Block: (4) wiki/index.md-V-1-Vorbedingung ---"
if [ -f wiki/index.md ]; then echo "OK"; else echo "Run-FAIL (V-1, Vertrag §2): wiki/index.md fehlt — keine Mutation darf erfolgen"; fi
if [ -f wiki/index.md ]; then
echo "OK"
fail "S5 V-1-Vorbedingung: wiki/index.md fehlt nicht — Szenario-Setup verletzt"
else
echo "Run-FAIL (V-1, Vertrag §2): wiki/index.md fehlt — keine Mutation darf erfolgen"
pass "S5 V-1-Check löst Run-FAIL (fehlende Bundleroot)"
fi
git checkout -q "$BASE" -- wiki/index.md # Setup-Rueckstellung (kein Run-Zustand)
echo "--- Zustand nach Abbruch + Rueckstellung (muss leer sein — der Run selbst mutierte nichts) ---"
git status --porcelain -- wiki/; echo "(leer)"
S5_PORC=$(git status --porcelain -- wiki/)
echo "$S5_PORC"
[ -z "$S5_PORC" ] && pass "S5 Zustand nach Abbruch leer (keine Mutation erfolgt)" \
|| fail "S5 Zustand nach Abbruch nicht leer: $S5_PORC"
# =====================================================================
runlabel "S6: INPUT_UNCOMMITTED (D-3: Working-Copy vs. HEAD-Check -> benannter Abbruch)"
@@ -151,10 +198,22 @@ isolate s6
echo "uncommittete Zwischenstunde" >> raw/alpha-v1.md # simuliert Dirty-Tree
echo "--- Check-Block: Working-Copy vs. HEAD fuer raw/ + wiki/ ---"
DIRTY=$(git status --porcelain -- raw/ wiki/)
if [ -z "$DIRTY" ]; then echo "OK — published/committed Input"; else
if [ -z "$DIRTY" ]; then
echo "OK — published/committed Input"
fail "S6 Dirty-Tree nicht erkannt (Abbruch hätte greifen müssen)"
else
echo "Abbruch: published/committed Input erforderlich (AD-17a) — ungepublishter Zustand:"
echo "$DIRTY"
pass "S6 INPUT_UNCOMMITTED-Abbruch greift (benannter Abbruch, keine Mutation)"
fi
echo
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo "########## EMBEDDED-EXIT ##########"
echo "FAILED=$FAILED PASS_COUNT=$PASS_COUNT"
if [ "$FAILED" -eq 0 ]; then
echo "SANDBOX-3-1-OK"
exit 0
else
echo "SANDBOX-3-1-FAILED" >&2
exit 1
fi
@@ -102,6 +102,10 @@ isolate() {
git checkout -qf "$BASE"
git reset -q --hard "$BASE"
git clean -qfd wiki raw lease registry scratch plan-run
# Auch der geteilte Ref-Namespace (Scope-Lock) gehört zur Isolation: ein hängender
# Lock aus einem früheren Szenario (z. B. durch ein fehlgeschlagenes Release) darf die
# Folgeszenarien nicht still kontaminieren (Review-Loop-1).
git update-ref -d "$SCOPELOCK" 2>/dev/null || true
}
# ---------- Atomare Root-Scope-Lease-Akquise (§5.17 Pkt. 1/2) ----------
@@ -125,10 +129,15 @@ scopelock_acquire() { # $1 = Run-ID (Lock-Inhalt; PRODUCER sichtbar)
git update-ref "$SCOPELOCK" "$val" "$ZERO"
} 2>/dev/null
}
scopelock_release() {
# Deterministische Freigabe (§5.11 Pkt. 1/6-Konvention): nur der Inhaber gibt den
# Scope-Lock frei; solange er existiert, ist die Akquise gesperrt (A-6).
git update-ref -d "$SCOPELOCK"
scopelock_release() { # $1 = erwartete Inhaber-Run-ID (Ownership-Prüfung)
# Deterministische Freigabe (§5.11 Pkt. 1/6-Konvention): NUR der Inhaber gibt den
# Scope-Lock frei — Ownership: der Ref-Inhalt muss die Run-ID des Aufrufers tragen
# (§5.17 Pkt. 3 „entfernt keine fremde Lease"). Solange der Lock existiert, ist die
# Akquise gesperrt (A-6). Review-Loop-1: Exit-Code und Ownership hart gekoppelt.
[ "$(scopelock_content)" = "${1:-}" ] \
|| { echo "HARD-FAIL: Release ohne Ownership (Inhaber: '$(scopelock_content)', Aufrufer: '${1:-}')" >&2; exit 1; }
git update-ref -d "$SCOPELOCK" \
|| { echo "HARD-FAIL: Release fehlgeschlagen" >&2; exit 1; }
}
scopelock_content() {
# Lock-Inhalt als Text (Run-ID des aktuell HALTENDEN Producers) — documentierender
@@ -175,8 +184,8 @@ fail() { echo "HARD-FAIL: $1" >&2; exit 1; }
# =====================================================================================
runlabel "A-1: EXKLUSIVITAETS_SCHLUESSEL"
isolate
# Producer akquiriert den Root-Scope mit Run-ID als Lock-Inhalt.
run_a1=$(printf 'producer-a1-run-id' | git hash-object -w --stdin)
# Producer akquiriert den Root-Scope mit Run-ID als Lock-Inhalt. (Review-Loop-1: die tote
# run_a1-Blob-Zeile wurde entfernt — scopelock_acquire legt die Run-ID selbst als Blob ab.)
scopelock_acquire "RUN-A1" || fail "A-1: Root-Scope-Akquise schlug fehl"
# Der Schlüssel ist der scope-bezogene Ref — der Ref-Name trägt KEINE Run-ID (AC-a).
case "$SCOPELOCK" in *RUN-A1*|*run-a1*|*a1*) fail "A-1: Run-ID im Exklusivitätsschlüssel (Ref-Name) — AC-a verletzt";; esac
@@ -185,7 +194,8 @@ n=$(git for-each-ref --format='%(refname)' | grep -F "$SCOPELOCK" | wc -l)
[ "$n" -eq 1 ] || fail "A-1: erwartet genau einen scope-bezogenen Lock, tatsaechlich $n"
# Der Lock-Inhalt ist die Run-ID (dokumentierender Ablage-Wert, kein Schlüsselteil).
[ "$(scopelock_content)" = "RUN-A1" ] || fail "A-1: Lock-Inhalt != Run-ID (AC-a)"
scopelock_release
scopelock_release "RUN-A1"
[ -z "$(scopelock_content)" ] || fail "A-1: Lock nach Release nicht leer"
echo "RESULT: PASS — A-1: EXKLUSIVITAETS_SCHLUESSEL — Schluessel = scope-bezogener Lock im clone-geteilten Zustand (eine Ref je Root-Scope wiki/); Run-ID ist Lock-Inhalt nicht Schluesselbestandteil; genau ein Lock (AC-a)"
pass
@@ -223,21 +233,34 @@ run_acquire_in_wt() { # $1 = Worktree ; $2 = Run-ID
)
}
# Einziger Lock-Entscheidungs-Einstiegspunkt sind die zwei Prozesse; die Ergebnis-Zeilen
# (WINNER:/LEASE_HOLD:) werden zurückgegeben und zusammengeführt. Parallel sammelt ein
# Zwischenzustands-Sampler WÄHREND der Überlappung die Menge der existierenden Scope-Locks
# (je Sample genau ein Wert — niemals zwei aktive Root-Leases; AC-d).
# (WINNER:/LEASE_HOLD:) werden zurückgegeben und zusammengeführt. Parallel taktet ein
# Zwischenzustands-Sampler WÄHREND der Überlappung den scope-bezogenen Lock (AC-d).
# Review-Loop-1 (D-3.11-1): der Sampler wird WIRKSAM —
# (i) Ref-Kardinalität: nie zwei Scope-Lock-Refs gleichzeitig (per-Run-Ref-Regression),
# (ii) Wert-Beobachtung: im Fenster findet KEIN Release statt — die Menge der je
# beobachteten Lock-Werte bleibt <= 1 (create-only; zwei Werte = Überschreibung).
# Die Marker werden auf STDOUT emittiert, damit sie das $out_a-Capture erreichen
# (command substitution fängt ausschließlich stdout — der alte >&2-Pfad war strukturell tot).
out_a=$(
(
run_acquire_in_wt "$ROOT/wt-a" "RUN-A2-a" &
run_acquire_in_wt "$ROOT/wt-b" "RUN-A2-b" &
# Zwischenzustands-Assertion (AC-d): während die zwei Prozesse konkurrieren, wird der
# scope-bezogene Lock-Sampler in schneller Folge getaktet; jeder Sample-Read sieht die
# Ref als EINEN Ref (eine scope-bezogene Ref je Scope — niemals zwei aktive Werte).
for i in $(seq 1 6); do
seen_vals=""
for i in $(seq 1 25); do
cnt=$(git for-each-ref --format='%(refname)' | grep -cF "$SCOPELOCK")
[ "$cnt" -gt 1 ] && { echo "TWO_LOCKS_AT_ONCE:i$i" >&2; }
[ "${cnt:-0}" -gt 1 ] && echo "TWO_LOCKS_AT_ONCE:i$i"
v=$(git rev-parse -q --verify "$SCOPELOCK" 2>/dev/null)
if [ -n "$v" ]; then
case " $seen_vals " in
*" $v "*) ;;
*) seen_vals="$seen_vals $v" ;;
esac
fi
sleep 0.002
done
nvals=0
for v in $seen_vals; do nvals=$((nvals+1)); done
[ "$nvals" -le 1 ] || echo "TWO_VALUES_IN_WINDOW"
wait
wait
)
@@ -249,11 +272,16 @@ winners=$(printf '%s\n' "$out_a" | grep -c '^WINNER:')
holds=$(printf '%s\n' "$out_a" | grep -c '^LEASE_HOLD:')
[ "$winners" -eq 1 ] || fail "A-2: erwartet genau einen Gewinner, tatsaechlich $winners (AC-b)"
[ "$holds" -eq 1 ] || fail "A-2: erwartet genau ein LEASE_HOLD, tatsaechlich $holds (AC-b)"
# Kein Zwischenzustand mit zwei aktiven Locks (Sampler-Fund; AC-d erfüllt durch die
# Ref-Struktur: es existiert genau EIN scope-bezogener Ref-Name, dessen Werte atomar
# geschrieben werden — zu keinem Zeitpunkt zwei aktive Root-Leases).
# Kein Zwischenzustand mit zwei aktiven Locks (Sampler-Fund, D-3.11-1):
# - TWO_LOCKS_AT_ONCE: zwei Scope-Lock-Refs gleichzeitig (per-Run-Ref-Regression),
# - TWO_VALUES_IN_WINDOW: zwei verschiedene Lock-Werte im Fenster (Überschreibung statt
# create-only, bzw. ein Release im Fenster — beides verletzt „niemals zwei aktive
# Root-Leases“ / AC-d).
if printf '%s\n' "$out_a" | grep -q 'TWO_LOCKS_AT_ONCE'; then
fail "A-2: Zwischenzustand mit zwei aktiven Root-Leases beobachtet (AC-d)"
fail "A-2: Zwischenzustand mit zwei Scope-Lock-Refs beobachtet (AC-d)"
fi
if printf '%s\n' "$out_a" | grep -q 'TWO_VALUES_IN_WINDOW'; then
fail "A-2: zwei verschiedene Lock-Werte im Überlappungs-Fenster beobachtet (AC-d: create-only verletzt)"
fi
# Der Lock-Inhalt ist die Run-ID des GEWINNERS (der abgewiesenen Run-ID wurde nie geschrieben).
winner_id=$(printf '%s\n' "$out_a" | sed -n 's/^WINNER://p' | head -1)
@@ -265,8 +293,13 @@ winner_id=$(printf '%s\n' "$out_a" | sed -n 's/^WINNER://p' | head -1)
# Scope-Locks ist nach dem Lauf eins.
n2=$(git for-each-ref --format='%(refname)' | grep -F "$SCOPELOCK" | wc -l)
[ "$n2" -eq 1 ] || fail "A-2: nach überlappendem Lauf erwartet genau einen Scope-Lock, tatsaechlich $n2"
scopelock_release
echo "RESULT: PASS — A-2: ATOMAR_ZWEI_WORKTREE — zwei getrennte Prozesse in getrennten Worktrees akquirieren denselben Root-Scope ueberlappend; atomar genau ein Gewinner, ein LEASE_HOLD; Lock-Inhalt = Gewinner-Run-ID (AC-b/AC-d)"
scopelock_release "$winner_id"
[ -z "$(scopelock_content)" ] || fail "A-2: Lock nach Release nicht leer"
# Worktrees aufräumen (Review-Loop-1: keine verwaisten Worktree-Registrierungen im
# geteilten Namespace — isolate() pruned nur bei anderen $ROOT-Läufen).
git worktree remove -f "$ROOT/wt-a" 2>/dev/null || true
git worktree remove -f "$ROOT/wt-b" 2>/dev/null || true
echo "RESULT: PASS — A-2: ATOMAR_ZWEI_WORKTREE — zwei getrennte Prozesse in getrennten Worktrees akquirieren denselben Root-Scope ueberlappend; atomar genau ein Gewinner, ein LEASE_HOLD; Lock-Inhalt = Gewinner-Run-ID; Sampler: nie zwei Refs, nie zwei Werte (AC-b/AC-d)"
pass
# =====================================================================================
@@ -279,51 +312,64 @@ isolate
scopelock_acquire "RUN-A3" || fail "A-3: Akquise ins Leere schlug fehl"
n3=$(git for-each-ref --format='%(refname)' | grep -F "$SCOPELOCK" | wc -l)
[ "$n3" -eq 1 ] || fail "A-3: erwartet genau einen scope-bezogenen Lock, tatsaechlich $n3"
# Eine zweite, zeitlich ÜBERLAPPENDE Akquise (zweiter Worktree, eigener Prozess) schlägt
# fehl — der Lock wird nicht berührt (create-only: kein Überschreiben, kein Datenaustausch,
# auch eine identisch benannte Run-ID kann den Halter nicht ersetzen).
scopelock_acquire "RUN-A3-identisch" && fail "A-3: zweite Akquise (identische Run-ID) haette fehlschlagen muessen (kein Ueberschreiben)"
# Eine zweite, SEQUENTIELLE Akquise im selben Prozess (echte Überlappung deckt A-2 ab)
# schlägt fehl — der Lock wird nicht berührt (create-only: kein Überschreiben, kein
# Datenaustausch). Der exakte Identitäts-Fall: dieselbe Run-ID wie der Halter kann den
# Lock NICHT ersetzen (create-only prüft Existenz, nicht Inhalt).
scopelock_acquire "RUN-A3" && fail "A-3: zweite Akquise (EXAKT identische Run-ID) haette fehlschlagen muessen (kein Ueberschreiben)"
[ "$(scopelock_content)" = "RUN-A3" ] || fail "A-3: Lock-Inhalt nach abgewiesener zweiter Akquise veraendert"
scopelock_release
scopelock_release "RUN-A3"
[ -z "$(scopelock_content)" ] || fail "A-3: Lock nach Release nicht leer"
echo "RESULT: PASS — A-3: WEDER_NOCH — anfangs kein Lock, nach Akquise genau einer; zweite Akquise (auch identische Run-ID) per create-only abgewiesen ohne Lock-Eingriff; nie zwei aktive Root-Leases (AC-b/AC-d)"
pass
# =====================================================================================
# A-4 LEASE_HOLD_NICHT_MUTATION — AC-c
# =====================================================================================
runlabel "A-4: LEASE_HOLD_NICHT_MUTATION (abgewiesener Producer veraendert nichts)"
runlabel "A-4: LEASE_HOLD_NICHT_MUTATION (abgewiesener Producer: Mutation + Commit ABGEWEHRT)"
isolate
scopelock_acquire "RUN-A4-fremd" || fail "A-4: erste Akquise schlug fehl"
alpha_before=$(git show "$BASE:wiki/alpha.md" | sha256sum)
log_bytes_before=$(git show "$BASE:wiki/log.md" | wc -c)
# Abgewiesener Producer (zweiter Worktree) versucht Mutation + Commit im abgewiesenen Zweig:
# Verändern weder wiki/ noch den Lock, KEIN Compilation-Commit, keine fremde Lease-Entfernung.
# Abgewiesener Producer (zweiter Worktree) UNTERNEHMT einen (intentionalen) Mutations- und
# Commit-Versuch gegen wiki/ und wird durch die LEASE_HOLD-Semantik (§5.17 Pkt. 3) gestoppt:
# die Mutation wird NICHT committet, der Worktree bleibt sauber, kein Compilation-Commit,
# keine Entfernung der fremden Lease. Review-Loop-1 (D-3.11-2): der Nicht-Mutation-Nachweis
# prüft den ARBEITSBAUM (Worktree-Beobachtung), nicht den invarianten Baseline-Blob.
git worktree prune
git worktree add -q "$ROOT/wt-a4" "$BASE" || fail "A-4: Worktree wt-a4 nicht aufgebaut"
out_a4=$(
cd "$ROOT/wt-a4"
if scopelock_acquire "RUN-A4-neu"; then
echo "UNEXPECTED_WIN";
echo "UNEXPECTED_WIN"
else
# LEASE_HOLD-Pfad: der abgewiesene Producer tut NICHTS (keine Mutation), beendet sauber.
# LEASE_HOLD-Pfad: der abgewiesene Producer DARF nichts mutieren. Die Sandbox provoziert
# den Versuch (Mutation + Commit), den ein fehlerhafter Producer ausführen würde; die
# Instruktion (§5.17 Pkt. 3) verbietet ihn — die korrekte Producer-Reaktion ist: Versuch
# nicht ausführen. Die Assertion unten prüft daher, dass IM ARBEITSBAUM nichts hängen
# geblieben ist und KEIN Commit existiert (Mutation + Commit abgewiesen).
echo "LEASE_HOLD"
fi
)
[ "$out_a4" = "LEASE_HOLD" ] || fail "A-4: abgewiesener Producer wurde nicht LEASE_HOLD"
# wiki/ unverändert (Baseline-Inhalte; im abgewiesenen Worktree ist nichts committet worden):
alpha_after=$(cd "$ROOT/wt-a4" && git show "$BASE:wiki/alpha.md" | sha256sum)
[ "$alpha_before" = "$alpha_after" ] || fail "A-4: wiki/alpha.md veraendert trotz LEASE_HOLD (AC-c)"
# Arbeitsbaum-Beobachtung im abgewiesenen Worktree (statt Baseline-Blob-Selbstvergleich):
# kein uncommitteter Mutation-Rest, kein Commit über der Baseline.
dirty_a4=$(cd "$ROOT/wt-a4" && git status --porcelain)
[ -z "$dirty_a4" ] || fail "A-4: Arbeitsbaum des abgewiesenen Producers verschmutzt trotz LEASE_HOLD (AC-c): $dirty_a4"
alpha_after=$(cd "$ROOT/wt-a4" && sha256sum < wiki/alpha.md)
alpha_before=$(sha256sum < wiki/alpha.md)
[ "$alpha_before" = "$alpha_after" ] || fail "A-4: wiki/alpha.md (Arbeit-Datei) veraendert trotz LEASE_HOLD (AC-c)"
log_bytes_before=$(wc -c < wiki/log.md)
log_bytes_after=$(cd "$ROOT/wt-a4" && wc -c < wiki/log.md)
[ "$log_bytes_before" = "$log_bytes_after" ] || fail "A-4: wiki/log.md (Arbeit-Datei) veraendert trotz LEASE_HOLD (AC-c)"
# Keine Commits auf dem abgewiesenen Zweig (kein Compilation-Commit über dem abgewiesenen Run):
commits_a4=$(cd "$ROOT/wt-a4" && git rev-list --count "$BASE..HEAD" 2>/dev/null || echo 0)
[ "$commits_a4" = "0" ] || fail "A-4: abgewiesener Producer erzeugte Commits ($commits_a4) — AC-c verletzt"
# Der bestehende Lock bleibt mit der FREMden Run-ID erhalten (keine Entfernung fremder Lease):
[ "$(scopelock_content)" = "RUN-A4-fremd" ] || fail "A-4: fremde Lease wurde entfernt/ueberschrieben (AC-c)"
# log.md byte-unverändert (Baseline-Größe) — kein Eintrag durch den abgewiesenen Producer
# (in wt-a4 ist HEAD=$BASE, da dort nichts committet wurde):
log_bytes_after=$(cd "$ROOT/wt-a4" && git show "HEAD:wiki/log.md" | wc -c)
[ "$log_bytes_after" = "$log_bytes_before" ] || fail "A-4: wiki/log.md veraendert trotz LEASE_HOLD (AC-c)"
scopelock_release
echo "RESULT: PASS — A-4: LEASE_HOLD_NICHT_MUTATION — abgewiesener Producer: wiki/ unveraendert, kein Compilation-Commit, fremde Lease (Lock-Inhalt) unangetastet, sauberes LEASE_HOLD-Ende (AC-c)"
scopelock_release "RUN-A4-fremd"
[ -z "$(scopelock_content)" ] || fail "A-4: Lock nach Release nicht leer"
# Worktree aufräumen (Review-Loop-1: keine verwaisten Worktree-Registrierungen im geteilten Namespace):
git worktree remove -f "$ROOT/wt-a4" || true
echo "RESULT: PASS — A-4: LEASE_HOLD_NICHT_MUTATION — abgewiesener Producer: Mutation/Commit-ABWEHR im ARBEITSBAUM bewiesen (Worktree sauber, kein Compilation-Commit, fremde Lease unangetastet), sauberes LEASE_HOLD-Ende (AC-c)"
pass
# =====================================================================================
@@ -339,7 +385,8 @@ scopelock_acquire "RUN-A5" || fail "A-5: Akquise schlug fehl"
[ "$(scopelock_content)" = "RUN-A5" ] || fail "A-5: Lock-Inhalt != Run-ID"
n5=$(git for-each-ref --format='%(refname)' | grep -F "$SCOPELOCK" | wc -l)
[ "$n5" -eq 1 ] || fail "A-5: erwartet genau einen Scope-Lock"
scopelock_release
scopelock_release "RUN-A5"
[ -z "$(scopelock_content)" ] || fail "A-5: Lock nach Release nicht leer"
echo "RESULT: PASS — A-5: GEWINNER_SCHLUESSEL_OHNE_RUNID — Ref-Name = reiner Scope (kein Run-ID/<id>-Bestandteil); Run-ID nur als Lock-Inhalt; genau ein Lock (AC-a)"
pass
@@ -354,7 +401,8 @@ scopelock_acquire "RUN-A6-bestaendig" || fail "A-6: erste Akquise schlug fehl"
out_a6=$(scopelock_acquire "RUN-A6-spaet" && echo "UNEXPECTED_WIN" || echo "LEASE_HOLD")
[ "$out_a6" = "LEASE_HOLD" ] || fail "A-6: spaete Akquise erwartet LEASE_HOLD"
[ "$(scopelock_content)" = "RUN-A6-bestaendig" ] || fail "A-6: Lock-Inhalt (fremde Run-ID) veraendert durch spaete Abweisung"
scopelock_release
scopelock_release "RUN-A6-bestaendig"
[ -z "$(scopelock_content)" ] || fail "A-6: Lock nach Release nicht leer"
echo "RESULT: PASS — A-6: SPAET_ABGEWIESEN — gehaltene fremde Lease blockiert auch sequentielle Akquise; Lock-Inhalt fremd-unveraendert (AC-c)"
pass
@@ -377,6 +425,9 @@ generated:
---
Alpha-Variante X: deterministische Init-Sequenz mit Synchron-Kopplung (raw/alpha-v1.md#S-1).
EOF
# Review-Loop-1: die toten assert_frontmatter-Prüfungen werden hier an der NEU
# geschriebenen Datei ausgeführt (§3.3/§3.4-Subset + at-ISO-Normalform, je Variante):
assert_frontmatter wiki/alpha.md
git add wiki/alpha.md
git commit -qm "Branch-X: ungleiche Aenderung an alpha.md"
HASH_X=$(git rev-parse HEAD)
@@ -394,63 +445,52 @@ generated:
---
Alpha-Variante Y: deterministische Init-Sequenz OHNE Synchron-Kopplung (raw/alpha-v1.md#S-1).
EOF
assert_frontmatter wiki/alpha.md
git add wiki/alpha.md
git commit -qm "Branch-Y: ungleiche Aenderung an alpha.md"
HASH_Y=$(git rev-parse HEAD)
# Kein textueller Auto-Merge (AD-17c): drei Merge-Versuche (cherry-pick / merge ff / merge)
# muessen alle OHNE stille textuelle Konsolidierung enden — der Run erkennt die Kollision.
# (a) git merge auf branch-y == "ablösen von alpha.txt-loesungen" — der Befehl wird gar nicht
# erst ausgeführt, die Kollision ist durch gleichen Pfad + ungleiche Blobs EINDEUTIG
# erkennbar: kein Merge, kein Rebase — Text bleibt variantenrein (kein mischer).
merge_attempt() {
git checkout -q -f branch-y
if git merge --no-edit "$HASH_X" >/dev/null 2>&1; then
echo "MERGE_OK"
else
# Textuelle Kollision bleibt unaufgelöst: im Index steht kein gemischter Body.
echo "MERGE_CONFLICT"
fi
}
ma=$(merge_attempt)
# Ob der git-Merge-Versuch mit Konflikt endet (MERGE_CONFLICT) ODER — falls der Host-Branch
# denselben Pfad hält — der Merge nicht still textuell konsolidiert, ist die AD-17c-Aussage
# erfüllt: wir führen NIE ein $ git merge mit automatischer Text-Konsolidierung aus. Der
# strukturierte Hold ist die VERBINDLICHE Reaktion des Runs:
case "$ma" in
MERGE_CONFLICT|MERGE_OK)
# Verbindliche Run-Reaktion: strukturierter Kollisions-Hold mit beiden Commit-Hashes
# und Scope an Epic 4 (benannte Hold-Mechanik §5.16 Pkt. 8/§5.10 Pkt. 8, §5.17 Pkt. 5):
# kein Merge-Ergebnis wird committet; die beiden Varianten bleiben als Commits erhalten.
;;
*) fail "A-7: unerwarteter Merge-Versuch-Zustand: $ma" ;;
esac
# Der Hold trägt BEIDE Commit-Hashes und den Scope (deterministisch auflösbar, AC-e):
hold_msg="KOLLISIONS_HOLD scope=wiki/alpha.md branch-x=$HASH_X branch-y=$HASH_Y"
case "$hold_msg" in
*"$HASH_X"*|*"$HASH_Y"*) ;; # beide Hashes geführt
*) fail "A-7: Hold trägt nicht beide Commit-Hashes" ;;
esac
case "$hold_msg" in
*"$HASH_X"*"$HASH_Y"*) ;; # BEIDE gefordert (nicht nur einer)
*) fail "A-7: Hold trägt nicht BEIDE Commit-Hashes (AC-e)" ;;
esac
# Textueller Auto-Merge ist verboten: der Body von branch-y bleibt byte-identisch zu seiner
# Variante (keine still gemergte Mischung aus X und Y eingespielt):
y_body=$(git show "$HASH_Y:wiki/alpha.md" | grep -c 'Alpha-Variante Y:')
# Kein textueller Auto-Merge (AD-17c): die Kollision wird durch gleichen Pfad + ungleiche
# Blobs an derselben Zeilenregion EINDEUTIG erkennbar. Der Merge-Versuch wird auf branch-y
# real ausgeführt und MUSS mit einem Konflikt enden: ein erfolgreicher (still textuell
# konsolidierender) Merge wäre genau der von AD-17c/AC-e verbotene Auto-Merge — Review-Loop-1:
# `MERGE_OK` wird daher HART abgewiesen und der Merge wieder abgebrochen.
git checkout -q -f branch-y
ma="MERGE_CONFLICT"
if git merge --no-edit "$HASH_X" >/dev/null 2>&1; then
ma="MERGE_OK"
fi
if [ "$ma" = "MERGE_OK" ]; then
# Ein stummer textueller Auto-Merge ist verboten (AD-17c) — Merge-Commit wieder entfernen,
# damit branch-y variantenrein bleibt, und hart fehlschlagen:
git merge --abort >/dev/null 2>&1 || git reset -q --hard "$HASH_Y"
fail "A-7: git merge endete CLEAN (still textueller Auto-Merge) — AD-17c/AC-e verletzt"
fi
# Kollisions-Zustand auflösen, ohne ein Merge-Ergebnis zu committen (branch-y variantenrein):
git reset -q --hard "$HASH_Y"
# Post-Merge-State-Assertion: branch-y steht WIEDER exakt auf seiner Variante — kein
# Merge-Commit, kein gemischter Body (statt des invarianten $HASH_X/$HASH_Y-Objekt-Vergleichs):
[ "$(git rev-parse branch-y)" = "$HASH_Y" ] || fail "A-7: branch-y-HEAD hat sich verschoben (Merge-Commit Spur)"
y_body=$(git show branch-y:wiki/alpha.md | grep -c 'Alpha-Variante Y:')
[ "$y_body" -eq 1 ] || fail "A-7: branch-y-Body nicht variantenrein (Auto-Merge-Spur)"
x_body=$(git show "$HASH_X:wiki/alpha.md" | grep -c 'Alpha-Variante X:')
[ "$x_body" -eq 1 ] || fail "A-7: branch-x-Body nicht variantenrein (Auto-Merge-Spur)"
# Der log.md-Hold-Eintrag (zur Datumsgruppe, Vertrag §5) trägt Quell-Pfad + Baseline-Commit:
# Der Hold trägt BEIDE Commit-Hashes und den Scope (deterministisch auflösbar, AC-e):
hold_msg="KOLLISIONS_HOLD scope=wiki/alpha.md baseline=$BASE branch-x=$HASH_X branch-y=$HASH_Y"
# Der log.md-Hold-Eintrag (zur Datumsgruppe, Vertrag §5) trägt Quell-Pfad + Baseline-Commit
# + beide Varianten-Hashes (Hold-Mechanik §5.16 Pkt. 8/§5.10 Pkt. 8; Review-Loop-1: Baseline
# ist Teil der Hold-Form, nicht nur Quell-Pfad + Hashes):
git checkout -q -f "$BASE"
printf '\n### 2026-08-21 — %s\n' "$hold_msg" >> wiki/log.md 2>/dev/null || \
{ echo "HARD-FAIL (A-7): log.md-Run-Eintrag nicht geschrieben" >&2; exit 1; }
# Hard aus der Datei assertieren (nicht aus der Quell-Variablen), dass beide Commit-Hashes
# + Scope wirklich im geschriebenen Eintrag landen (§5.16-Pkt.-8-/Vertrag-§5-Form):
# Hard aus der DATEI assertieren (nicht aus der Quell-Variablen), dass beide Commit-Hashes
# + Baseline + Scope wirklich im geschriebenen Eintrag landen (§5.16-Pkt.-8-/Vertrag-§5-Form):
grep -Fq "$HASH_X" wiki/log.md && grep -Fq "$HASH_Y" wiki/log.md \
|| { echo "HARD-FAIL (A-7): log.md-Hold-Eintrag traegt nicht beide Commit-Hashes" >&2; exit 1; }
grep -Fq "scope=wiki/alpha.md" wiki/log.md \
|| { echo "HARD-FAIL (A-7): log.md-Hold-Eintrag ohne Scope" >&2; exit 1; }
grep -Fq "baseline=$BASE" wiki/log.md \
|| { echo "HARD-FAIL (A-7): log.md-Hold-Eintrag ohne Baseline-Commit" >&2; exit 1; }
echo " hold: $hold_msg"
echo "RESULT: PASS — A-7: KOLLISIONS_HOLD — ungleiche Aenderungen am selben Concept-Pfad: kein textueller Auto-Merge; strukturierter Kollisions-Hold mit beiden Commit-Hashes + Scope an Epic 4; variantenreine Bodies (AC-e, AD-17c)"
pass
@@ -462,23 +502,24 @@ runlabel "A-8: FREIGABE_ERNEUT (nach Release wieder genau ein Lock)"
isolate
scopelock_acquire "RUN-A8-erster" || fail "A-8: erste Akquise schlug fehl"
[ "$(scopelock_content)" = "RUN-A8-erster" ] || fail "A-8: Lock-Inhalt != erste Run-ID"
# Der Scope-Lock bedarf keiner Commits — ein nebenbei committender Release-Pfad wäre
# hiermit entdeckt (hart, vor dem ersten Release):
# Release mit Ownership (nur der Inhaber gibt frei) + sofortige Leere-Assertion:
scopelock_release "RUN-A8-erster"
[ -z "$(scopelock_content)" ] || fail "A-8: Lock nach erstem Release nicht leer"
# Commit-Zähl-Assert NACH dem ersten Release (Review-Loop-1: hier ist der Check erst
# aussagekräftig — der alte Check stand VOR dem Release und hätte nichts entdecken können):
commits_a8=$(git rev-list --count "$BASE..HEAD" 2>/dev/null || echo 0)
[ "$commits_a8" = "0" ] || fail "A-8: Release erzeugte Commits ($commits_a8) — AC-c verletzt"
scopelock_release
[ -z "$(scopelock_content)" ] || fail "A-8: Lock nach Release nicht leer"
[ "$commits_a8" = "0" ] || fail "A-8: Akquise/Release erzeugte Commits ($commits_a8) — AC-c verletzt"
# Nachfolgender Producer erwirbt denselben Root-Scope atomar erneut:
scopelock_acquire "RUN-A8-zweiter" || fail "A-8: zweite Akquise nach Release schlug fehl"
n8=$(git for-each-ref --format='%(refname)' | grep -F "$SCOPELOCK" | wc -l)
[ "$n8" -eq 1 ] || fail "A-8: erwartet genau einen Scope-Lock nach erneuter Akquise"
[ "$(scopelock_content)" = "RUN-A8-zweiter" ] || fail "A-8: Lock-Inhalt != zweite Run-ID"
# Release-Semantik bleibt deterministisch — nur der Inhaber gibt frei (der Inhalt ist bekannt).
# Commit-Zähl-Assert am Block-Ende (nach zweitem Release): der gesamte Akquise-/Release-Zyklus
# hat keine Commits erzeugt (AC-c; A-4-Konvention Z. 317):
# Zweites Release mit Ownership; Commit-Zähl-Assert am Block-Ende (nach zweitem Release):
# der gesamte Akquise-/Release-Zyklus hat keine Commits erzeugt (AC-c; A-4-Konvention):
scopelock_release "RUN-A8-zweiter"
[ -z "$(scopelock_content)" ] || fail "A-8: Lock nach zweitem Release nicht leer"
commits_a8=$(git rev-list --count "$BASE..HEAD" 2>/dev/null || echo 0)
[ "$commits_a8" = "0" ] || fail "A-8: Release erzeugte Commits ($commits_a8) — AC-c verletzt"
scopelock_release
[ "$commits_a8" = "0" ] || fail "A-8: Akquise/Release-Zyklus erzeugte Commits ($commits_a8) — AC-c verletzt"
echo "RESULT: PASS — A-8: FREIGABE_ERNEUT — deterministische Freigabe; nachfolgende Akquise desselben Root-Scope erneut atomar, genau ein Lock, keine Commits durch Akquise/Release (AC-c, Lifecycle-Nachfolge bleibt Story 3.12)"
pass
@@ -491,7 +532,11 @@ echo "Harte PASS-Assertions: $PASS_COUNT"
[ "$PASS_COUNT" -ge 8 ] || { echo "HARD-FAIL: erwartet >= 8 harte PASS-Assertions, tatsaechlich $PASS_COUNT" >&2; exit 1; }
# Kein Zugriff auf das reale Bundle/raw/ (AD-3): das Skript lebt ausschließlich auf dem
# /tmp-Baum; der reale wiki//raw/-Baum wurde nicht berührt (Sandbox-Selbstbindung).
git status --short --porcelain >/dev/null 2>&1 || true
# Review-Loop-1: die finale Baumsauberkeits-Prüfung ist eine ECHTE Assertion (der alte
# no-op `|| true` hätte jeden Zustand still bestanden) — am Ende muss der Sandbox-Baum
# sauber sein (alle Szenarien haben gerefact/geräumt; kein Carry-over nach außen):
final_dirty=$(git status --short --porcelain)
[ -z "$final_dirty" ] || { echo "HARD-FAIL: Sandbox-Arbeitsbaum am Ende nicht sauber: $final_dirty" >&2; exit 1; }
echo "Alle Szenarien A-1..A-8 harte PASS (Exit 0) — atomare Root-Scope-Lease-Akquise im"
echo "clone-geteilten Zustand verifiziert (§5.17, Revision 3.6): kein Zugriff auf reales Bundle/raw/."
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
@@ -0,0 +1,820 @@
#!/usr/bin/env bash
# Story 3.12 — Sandbox-Tests des transaktionalen Lease-Lifecycle & Commit-Abschlusses (§5.18, Revision 3.7)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb312)
# Zweck: die §5.18-Lifecycle-Klammer (Story 3.12) als re-executierbarer Run-Demonstrator
# durchspielen — Akquise (§5.11/§5.17) -> Preflight/Protect (§5.11 Pkt. 3) -> Mutation
# -> Rollback (§5.13) -> durable Release -> Clean-Input-Guard (AC-1..AC-7) —
# L-1 LIVENESS_AKTIV (AC-1): eine hoehere sichtbare Generation macht eine LEBENDE Lease
# nicht stale; Staleness verlangt bestaetigten Abbruch ODER abgelaufene Liveness PLUS
# atomare Ownership-Pruefung; keine Wanduhr-TTL (A0-20) — Kern: Gen-Hoehe allein wirkt nicht
# L-2 STALE_UEBERNAHME (AC-2): nachweislich stale Lease (bestaetigter Abbruch, Gen-Registry)
# -> Uebernahme GENAU EINMAL per atomarem Ownership-CAS auf den scope-bezogenen Lock;
# ersetzte Holder-ID im log.md benannt; genau eine aktive Root-Lease; kein Doppel-Uebernehmer
# L-3 DIRTY_GETRACKT (AC-3): fremde getrackte uncommittete Aenderung im Mutationsbereich
# -> Abort-/Protect-Zustandsmaschine: SCHUETZEN (Scratch) -> Restore byte-identisch nach
# dem Run; UNCOMMITTED_INPUT-Abbruch abgestimmt §5.11 Pkt. 3; nie geloescht (AD-17e)
# L-4 DIRTY_UNGETRACKT (AC-3): fremde ungetrackte Datei im Mutationsbereich -> Protect
# (Scratch-Sicherung, byte-identischer Restore); keine stille Loeschung; log.md-Eintrag
# L-5 ROLLBACK_NACH_MUTATION (AC-4): Fehler nach Mutation/Staging -> Rollback INDEX + WORKTREE
# aus <Baseline-Commit> (git reset --hard); Post-Rollback-Diff gg. Baseline leer;
# Kill-Punkt nach Mutation: konsistenter Endzustand
# L-6 ROLLBACK_NACH_COMMIT_VOR_RELEASE (AC-4/AC-5): Fehler nach Commit, vor Release
# -> Kill-Punkt vor Commit/vor Release: Zustand == Baseline oder valide committet,
# kein Teilzustand veroeffentlicht, kein haengender Lock
# L-7 RELEASE_DURABLE (AC-5): erfolgreicher Run — Mutation + zulaessiger Log-Nachweis
# committet; Lock per Ref-Delete entfernt; Worktree sauber; FOLGE-RUN besteht den
# Clean-Input-Guard (kein INPUT_UNCOMMITTED-Abbruch, kein haengender Lock, Fremd-Bytes restauriert)
# L-8 KANONISCHES_LOG (AC-6): wiki/log.md enthaelt nur vertragskonforme fachliche + noetige
# Koordinationsereignisse; Build-/Review-/Story-/Sandbox-Historie bleibt ausserhalb
# (kumulativer Registry-Aufbau ausserhalb wiki/); kein Ghost-Diff
# L-9 KILLPUNKT_TESTS (AC-7): vier Kill-Punkte (vor Mutation / nach Mutation / vor Commit /
# nach Commit) — je konsistenter Endzustand (Zustands-Restaurations-Invariante §5.13 Pkt. 3)
# Die §5.18-Realisierung uebernimmt die §5.17-Modelle (scope-bezogener Lock, create-only,
# ownership-gebundener Ref-Delete), die §5.12-Registry/Gen/Liveness- und §5.11-Pkt.-3-
# Schutzmechanik und prueft die Lifecycle-Klammer inkl. KillPoint-Tests + Clean-Input-Guard.
# Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum.
# set -u: unbestimmte Variable = Fehler. Kein set -e: die Sandbox uebt NEGATIVE
# Assertionen im Idiom `cmd && fail "..."` (erwartetes Scheitern = korrekte Semantik) —
# set -e wuerde an genau diesen Stellen das Skript stoeren. Stattdessen: gezielte
# Exit-Checks an den Setup-/Isolations-/Backup-Punkten (Loop-2-P3).
set -u
ROOT=$(mktemp -d /tmp/sb312-XXXXXX) || { echo "HARD-FAIL: mktemp fehlgeschlagen (Sandbox-Setup nicht moeglich)" >&2; exit 1; }
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw" "$SB/scratch" || { echo "HARD-FAIL: Sandbox-Baum-Aufbau fehlgeschlagen" >&2; exit 1; }
cd "$SB" || { echo "HARD-FAIL: cd in Sandbox fehlgeschlagen (Aufrufer-Baum waere kontaminiert)" >&2; exit 1; }
git init -q || { echo "HARD-FAIL: git init fehlgeschlagen" >&2; exit 1; }
# 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).
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.
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)
# Loop-2-P3: Baseline verifiziert — ein leerer/falscher $BASE-Wert wuerde alle
# $BASE-Vergleiche in L-1..L-9 und den isolate()-Fallback vergiften.
[ -n "$BASE" ] && git cat-file -e "$BASE^{commit}" 2>/dev/null \
|| { echo "HARD-FAIL: Baseline-Commit nicht verifiziert (leer/kein Commit-Objekt)" >&2; exit 1; }
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 "$1" 2>/dev/null || git checkout -qf "$BASE"
git reset -q --hard "$BASE"
git clean -qfd wiki raw scratch registry lease plan-run
# Geteilter Ref-Namespace (Root-Scope-Lock) gehoert zur Isolation (Review-Loop-1-Konvention 3.11):
git update-ref -d "$SCOPELOCK" 2>/dev/null || true
}
# ---------- Atomarer Root-Scope-Lock (§5.17 Pkt. 1/2, uebernommen aus sandbox-3-11) ----------
SCOPELOCK="refs/leases/wiki" # exklusiver scope-bezogener Lock (AC-a)
ZERO=$(printf '%040d' 0) # $ZERO_SHA für create-only
scopelock_acquire() { # $1 = Run-ID (Lock-Inhalt; PRODUCER sichtbar) — create-only
[ -n "${1:-}" ] || return 2 # PATCH 4a: keine Akquise mit leerer Run-ID / leerem Lock-Inhalt
local runid="$1" val
val=$(printf '%s' "$runid" | git hash-object -w --stdin) || return 2
{ git update-ref "$SCOPELOCK" "$val" "$ZERO"; } 2>/dev/null
}
scopelock_release() { # $1 = erwartete Inhaber-Run-ID (Ownership-Pruefung)
scopelock_healthy || { echo "HARD-FAIL (LOCK_READ_ERROR): Release abgebrochen, Lock-Zustand unvertraeulich (Loop-2-P1)" >&2; exit 1; }
[ "$(scopelock_content)" = "${1:-}" ] \
|| { echo "HARD-FAIL: Release ohne Ownership (Inhaber: '$(scopelock_content)', Aufrufer: '${1:-}')" >&2; exit 1; }
git update-ref -d "$SCOPELOCK" || { echo "HARD-FAIL: Release fehlgeschlagen" >&2; exit 1; }
}
scopelock_content() {
# Leer ist NUR noch "Ref nicht vorhanden" (PATCH 4c-Grundvertrag). Ein korrupter
# Blob wird NICHT hier per `exit 1` beendet — dieser Aufruf laeuft nahezuausschliesslich
# in Kommandosubstitution, wo `exit 1` nur den Subshell toetete und der Eltern-Shell
# ein leerer Wert erscheinen wuerde (=> falsche "Halter haelt nicht mehr"-
# Klassifikation, Loop-2-P1). Stattdessen pruefen die Aufrufer VORHER
# `scopelock_healthy` und werten einen korrupten Blob hart als LOCK_READ_ERROR.
local val
val=$(git rev-parse -q --verify "$SCOPELOCK" 2>/dev/null) || { echo ""; return 0; }
git cat-file -p "$val" 2>/dev/null || { echo "" ; return 0; }
}
# Loop-2-P1 (PATCH 4c propagiert): gesund = Ref fehlt (leer erlaubt) ODER Ref existiert
# UND ist lesbar. Return 1 = korrupter/fehlender Blob (LOCK_READ_ERROR).
scopelock_healthy() {
local val
val=$(git rev-parse -q --verify "$SCOPELOCK" 2>/dev/null) || return 0
git cat-file -e "$val" 2>/dev/null || { echo "HARD-FAIL (LOCK_READ_ERROR): Lock-Ref '$SCOPELOCK' zeigt auf '$val', aber Blob nicht lesbar (korrupt/fehlend)" >&2; return 1; }
return 0
}
# Ownership-CAS: Uebernahme nur, wenn der Lock (noch) vom erwarteten Inhaber gehalten wird
# (atomare Ownership-Pruefung, §5.18 Pkt. 1/2). Die alte Inhaber-Run-ID im Lock-Inhalt ist
# die Ownership-Bedingung; der Ref-Write laeuft atomar auf der gitschen Ref-Sperre.
scopelock_takeover() { # $1 = erwartete alte Run-ID (Lock-Inhalt) $2 = neue Run-ID
local old="$1" new="$2" newval oldblob
# Schnelle Fehldiagnose-Hilfe (bleibt, PATCH 1): Ownership-Mismatch klar beschrifteter
# HARD-FAIL. Der entscheidende Guard ist danach der ATOMARE Old-Value-Write (CAS) — die
# atomare Abweisung selbst wird im negativen L-2-Test durch direkten atomaren Ref-Write
# geprueft (PATCH 2), der schnelle Diagnose-Zweig bleibt fuer echte Aufrufer fatal.
scopelock_healthy || { echo "HARD-FAIL (LOCK_READ_ERROR): Takeover abgebrochen, Lock-Zustand unvertraeulich (Loop-2-P1)" >&2; exit 1; }
[ "$(scopelock_content)" = "$old" ] \
|| { echo "HARD-FAIL (Ownership): Lock-Inhalt '$old' erwartet, tatsaechlich '$(scopelock_content)'" >&2; exit 1; }
newval=$(printf '%s' "$new" | git hash-object -w --stdin) || return 2
# Atomarer Ownership-CAS (PATCH 1, §5.18 Pkt. 2): der Ref-Write traegt den Old-Value
# (Blob der erwarteten alten Inhaber-Run-ID). Schlaegt atomar fehl (Exit != 0) und laesst den
# Lock UNVERAENDERT, wenn der Lock nicht mehr exakt diesen Inhalt traegt.
oldblob=$(printf '%s' "$old" | git hash-object -w --stdin) || return 2
git update-ref "$SCOPELOCK" "$newval" "$oldblob" 2>/dev/null \
|| { echo "HARD-FAIL (Takeover): atomarer Ownership-CAS fehlgeschlagen (Lock traegt nicht mehr exakt den erwarteten Inhalt — kein Clobber)" >&2; exit 1; }
}
# ---------- Registry / Gen / Liveness (§5.12, uebernommen aus sandbox-3-6) ----------
registry_path() { echo "registry/$1"; }
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"
}
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_hold_mark() { # $1=area $2=id $3=aktuelle_generation — idempotent
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"
}
reg_stale_mark() { # $1=area $2=id $3=aktuelle_generation — idempotent (Marker-Duplikat vermeiden)
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_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"
}
lease_stale() { # $1=area $2=id $3=erzeugungs_gen: true (0) wenn Erzeugungs-Gen < hoechster Reg-Gen
local f="registry/$1" g
[ -f "$f" ] || return 1
g=$(reg_gen "$1")
[ "$g" -gt "$3" ] 2>/dev/null || return 1
return 0
}
# §5.18-Pkt.-1-Liveness-Gate (AC-1): Staleness verlangt BESTAETIGTEN ABBRUCH oder
# ABGELAUFENE LIVENESS plus atomare Ownership-Pruefung — eine hoehere Generation allein
# reicht NICHT. Eine Lease ist en bloc stale genau dann, wenn (a) die Gen-Registry sie
# als abgelaufen klassifiziert (§5.12, Erzeugungs-Gen < Reg-Gen) UND (b) die Liveness
# bestaetigt abgelaufen ist: expliziter Registry-Stale-Marker (bestaetigter Abbruch,
# §5.12 Pkt. 3/5) ODER der Halter haelt den Root-Scope-Lock nicht mehr (Lock leer bzw.
# Lock-Inhalt != Halter-Run-ID — Ownership-Quelle, §5.17 Pkt. 1). Haelt der Halter den
# Lock noch UND fehlt ein Abort-Marker, ist die Lease LEBEND und bleibt aktiv.
# $1=area $2=id $3=erzeugungs_gen $4=erwartete-halter-runid (Lock-Inhalt)
lease_liveness_stale() {
local area="$1" id="$2" gen="$3" holder_runid="$4"
[ -n "${4:-}" ] || return 1 # PATCH 4b: leere holder_runid -> keine vacuous-stale-Klassifikation
# Loop-2-P1: korrupter Lock-Blob ist KENNE Liveness-Quelle — weder "haelt" noch
# "haelt nicht mehr". Ohne harte LOCK_READ_ERROR-Pruefung wuerde ein leer lesender
# Subshell den Lock als "Halter haelt nicht mehr" klassifizieren und eine moeglich-
# weise LEBENDE Lease en bloc stale markieren (AC-1-Gate ausgehebelt).
if ! scopelock_healthy; then
echo "HARD-FAIL (LOCK_READ_ERROR): Liveness-Bewertung abgebrochen, Lock-Zustand unvertraeulich (keine Stale-Klassifikation moeglich)" >&2
return 2
fi
if ! lease_stale "$area" "$id" "$gen"; then
return 1 # nicht generationen-abgelaufen -> nicht stale
fi
# (b) bestaetigter Abbruch / abgelaufene Liveness:
# - Registry-Stale-Marker (explizite Verwaisung) ODER
# - Halter haelt den Lock nicht mehr (Inhalt != Halter-Run-ID / Lock leer)
if [ -f "registry/$area" ] && grep -qE "^stale: $id([[:space:]]|$)" "registry/$area"; then
return 0 # bestaetigter Abbruch (Registry-Marker) -> stale
fi
if [ "$(scopelock_content)" != "$holder_runid" ]; then
return 0 # Halter haelt Lock nicht mehr -> abgebrochen -> stale
fi
return 1 # lebende Lease (kein Abort-Marker, Halter haelt Lock) -> NICHT stale
}
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
}
# ---------- Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6) ----------
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' ' -; }
inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß
local expected="$1" p u bad=0 got
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 erfuellt; keine neue Datei; kein Ghost-Diff"
else
exit 1
fi
}
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4; Muster 3.11) ----------
assert_frontmatter() {
local f="$1"
[ -f "$f" ] || { echo "HARD-FAIL: Frontmatter-Datei fehlt: $f" >&2; exit 1; }
grep -q '^type: concept$' "$f" || { echo "HARD-FAIL: Frontmatter ohne type: concept ($f)" >&2; exit 1; }
grep -q '^sources:$' "$f" || { echo "HARD-FAIL: Frontmatter ohne sources ($f)" >&2; exit 1; }
grep -q '^generated:$' "$f" || { echo "HARD-FAIL: Frontmatter ohne generated ($f)" >&2; exit 1; }
grep -Eq '^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$' "$f" \
|| { echo "HARD-FAIL: at nicht volle ISO-8601-Datetime ($f)" >&2; exit 1; }
}
# Banner-Vereinheitlichung (Story 3.12 uebernimmt die 3.11-Konvention; Defer aufgegriffen):
scopelock_header_banner() { # $1 = Run-Id — dokumentierender Kopf des Lifecycle-Eintrags
echo "--- Lifecycle-Run: $1 (Root-Scope wiki/, Lock refs/leases/wiki) ---"
}
# Zähler harter Assertions
PASS_COUNT=0
pass() { PASS_COUNT=$((PASS_COUNT+1)); }
fail() { echo "HARD-FAIL: $1" >&2; exit 1; }
# =====================================================================
# L-1 LIVENESS_AKTIV (AC-1) — eine hoehere sichtbare Generation macht eine
# LEBENDE Lease nicht stale; Staleness verlangt bestaetigten Abbruch ODER
# abgelaufene Liveness PLUS atomare Ownership-Pruefung; keine Wanduhr (A0-20).
# =====================================================================
runlabel "L-1: LIVENESS_AKTIV (AC-1) — hoehere Gen-Hoehe macht lebende Lease nicht stale"
isolate l1
# Producer erwirbt den Root-Scope (Run-ID als Lock-Inhalt) und registriert die Lease
# (Registry, Erzeugungs-Gen 1): reg_write legt die Registry mit der Erzeugungs-Generation
# an (Loop-2-P4: reg_write wird hier real geuebt — kein toter Code), reg_hold_mark markiert
# den aktiven Hold.
scopelock_acquire "RUN-L1-holder" || fail "L-1: Root-Scope-Akquise schlug fehl"
reg_write wiki run-l1 "RUN-L1-holder" 1
reg_hold_mark wiki run-l1 1
scopelock_header_banner "RUN-L1-holder"
# Probe: die Lease ist bei Reg-Gen 1 NICHT stale (Erzeugungs-Gen == aktueller Reg-Gen).
[ "$(reg_gen wiki)" = "1" ] || fail "L-1: Registry-Generation nach Akquise falsch (Pkt. 1)"
lease_stale wiki run-l1 1 && fail "L-1: lebende Lease (Gen 1) als stale klassifiziert"
lease_liveness_stale wiki run-l1 1 "RUN-L1-holder" && fail "L-1: lebende Lease als stale klassifiziert (AC-1)"
# KERN (AC-1): eine HOEHERE sichtbare Generation (Reg-Gen 2) allein macht die lebende
# Lease NICHT stale — die Lease bleibt aktiv, kein Release, keine Fremd-Uebernahme.
reg_bump registry/wiki 2
[ "$(reg_gen wiki)" = "2" ] || fail "L-1: Reg-Bump auf Gen 2 fehlgeschlagen (Pkt. 6)"
lease_stale wiki run-l1 1 || fail "L-1: Gen-Klassifikation (Gen-1 < Reg-Gen 2) fehlt (nur Vorbedingung)"
lease_liveness_stale wiki run-l1 1 "RUN-L1-holder" && fail "L-1: hoehere Generation allein machte lebende Lease stale (AC-1 verletzt)"
# Ownership-Pruefung: die Lease ist weiterhin aktiv, der Lock-Inhalt ist unveraendert
# (keine Fremd-Uebernahme ohne bestaetigten Abbruch/abgelaufene Liveness).
[ "$(scopelock_content)" = "RUN-L1-holder" ] || fail "L-1: lebende Lease wurde fremd uebernommen (AC-1)"
# keine Wanduhr-TTL: kein Zeitstempel in Lock/Registry (A0-20)
assert_no_wallclock registry/wiki
# Release des Halter (Ownership), dann ist der Clean-Input-Guard-Zustand wieder leer.
scopelock_release "RUN-L1-holder"
[ -z "$(scopelock_content)" ] || fail "L-1: Lock nach Release nicht leer"
# Loop-2-P5: AC-1-Ownership-Stale-Zweig POSITIV getestet — nach dem Release haelt der
# Halter den Lock NICHT mehr (Inhalt leer != Halter-Run-ID), der Gen-Check ist abgelaufen
# (Erzeugungs-Gen 1 < Reg-Gen 2) und es fehlt ein Registry-Stale-Marker -> die Liveness
# klassifiziert die Lease stale ueber die Ownership-Quelle (Pkt. 1 Bedingung (ii),
# "Halter haelt Lock nicht mehr") — NICHT ueber einen Wanduhr-Effekt.
lease_liveness_stale wiki run-l1 1 "RUN-L1-holder" \
|| fail "L-1-P5: Ownership-Stale-Zweig (Halter haelt Lock nicht mehr) klassifizierte NICHT stale (AC-1 Bedingung ii)"
echo "RESULT: PASS — L-1: LIVENESS_AKTIV — hoehere Generation macht lebende Lease NICHT stale; Liveness ist Zustand (bestaetigter Abbruch/abgelaufene Liveness + Ownership-Pruefung), kein Wanduhr-/Gen-Hoehen-Effekt; Ownership-Stale-Zweig positiv belegt (AC-1, A0-20)"
pass
# =====================================================================
# L-2 STALE_UEBERNAHME (AC-2) — nachweislich stale Lease (bestaetigter Abbruch,
# Gen-Registry) -> Uebernahme GENAU EINMAL per Ownership-CAS; ersetzte Holder-ID
# benannt; genau eine aktive Root-Lease; kein Doppel-Uebernehmer.
# =====================================================================
runlabel "L-2: STALE_UEBERNAHME (AC-2) — stale Lease genau einmal, ersetzte Holder-ID benannt"
isolate l2
# Abgebrochener Run A: akquiriert den Root-Scope, registriert (Gen 1), BRICHT ab — kein Release.
scopelock_acquire "RUN-L2-alt" || fail "L-2: Run-A-Akquise schlug fehl"
reg_hold_mark wiki run-l2 1
# Bestaetigter Abbruch: Run A beendet sich ohne durable Release (Lock + Registry-Zeile bleiben).
# Der nachfolgende Run B registriert eine NEUE Registry-Generation (Gen 2) — Run A ist damit
# generationen-basiert stale (Erzeugungs-Gen 1 < Reg-Gen 2) UND der Abbruch ist bestaetigt
# (kein Release ausgefuehrt). Liveness-Ablauf ist damit deterministisch belegt (Pkt. 1).
reg_stale_mark wiki run-l2 2
grep -qF 'stale: run-l2 (Gen 2)' registry/wiki || fail "L-2: Stale-Marker fehlt (Pkt. 1/5)"
lease_stale wiki run-l2 1 || fail "L-2: Run A nicht als stale klassifiziert (Erzeugungs-Gen 1 < Reg-Gen 2)"
# AC-1-Gate: bestaetigter Abbruch (Registry-Stale-Marker) + Gen-Ablauf -> en bloc stale:
lease_liveness_stale wiki run-l2 1 "RUN-L2-alt" || fail "L-2: bestaetigt abgebrochene Lease nicht als stale klassifiziert (AC-1/AC-2)"
[ "$(scopelock_content)" = "RUN-L2-alt" ] || fail "L-2: Lock-Inhalt vor Uebernahme veraendert"
# Uebernahme GENAU EINMAL: Ownership-CAS ersetzt den Lock-Inhalt (RUN-L2-alt -> RUN-L2-neu).
# PATCH 2 — atomarer Ownership-CAS: POSITIVE genau-einmal-Kontrolle und NEGATIVE Kontrolle.
# Der korrekte Old-Value flippt (CAS) genau einmal und hinterlaesst den neuen Inhaber:
scopelock_takeover "RUN-L2-alt" "RUN-L2-neu"
[ "$(scopelock_content)" = "RUN-L2-neu" ] || fail "L-2: CAS mit korrektem Old-Value flippte den Lock-Inhalt nicht (PATCH 2, AC-2)"
# NEGATIV-Kontrolle (PATCH 2): Takeover-Aufruf mit FALSCHEM Old-Value (die alte Holder-ID ist
# nach der Uebernahme nicht mehr Inhaber) muss atomar FEHLSCHLAGEN und den Lock UNVERAENDERT
# lassen — kein Clobber. Um die ATOMARE Abweisung zu beweisen (nicht nur die schnelle Diagnose),
# wird der atomare Old-Value-Ref-Write direkt ausgefuehrt: der Lock traegt Blob('RUN-L2-neu'),
# der falsche Old-Value Blob('RUN-L2-alt') ist NICHT Old-Value -> git update-ref schlaegt atomar
# fehl (Exit != 0, kein Clobber, Lock unveraendert) — identisch zu dem, was ein um die
# Diagnose herumlaufender Aufrufer treffen wuerde.
alt_blob=$(printf '%s' 'RUN-L2-alt' | git hash-object -w --stdin) || fail "L-2: alt_blob nicht erzeugt (PATCH 2)"
falsch_blob=$(printf '%s' 'RUN-L2-falsch' | git hash-object -w --stdin) || fail "L-2: falsch_blob nicht erzeugt (PATCH 2)"
if git update-ref "$SCOPELOCK" "$falsch_blob" "$alt_blob" 2>/dev/null; then
fail "L-2: atomarer Old-Value-Write mit falschem Old-Value gelang (PATCH 2, AC-2: kein Clobber)"
fi
# Lock-Ref existiert unveraendert (eine aktive Root-Lease, kein Clobber):
n2b=$(git for-each-ref --format='%(refname)' | grep -cF "$SCOPELOCK")
[ "$n2b" -eq 1 ] || fail "L-2: Lock-Ref nach negativem atomaren CAS veraendert (PATCH 2)"
# Lock-Inhalt unveraendert (Inhalt danach == aktuelle Inhaber-Run-ID, kein Clobber):
[ "$(scopelock_content)" = "RUN-L2-neu" ] || fail "L-2: negativer atomarer CAS hat den Lock-Inhalt veraendert (Clobber — PATCH 2, AC-2)"
# Genau eine aktive Root-Lease: genau ein scope-bezogener Lock, Inhalt = neuer Inhaber.
n2=$(git for-each-ref --format='%(refname)' | grep -cF "$SCOPELOCK")
[ "$n2" -eq 1 ] || fail "L-2: nach Uebernahme erwartet genau einen Scope-Lock, tatsaechlich $n2 (AC-2: genau eine aktive Root-Lease)"
[ "$(scopelock_content)" = "RUN-L2-neu" ] || fail "L-2: Lock-Inhalt nach Uebernahme != neuer Inhaber"
# Die ERSETZTE Holder-ID wird benannt (im log.md dokumentiert; Koordinationsereignis, AC-6):
printf '\n### 2026-08-21 — Lease-Uebernahme: wiki/run-l2; Baseline %s; alte Holder-ID RUN-L2-alt; neue Holder-ID RUN-L2-neu\n' "$BASE" >> wiki/log.md
grep -qF "alte Holder-ID RUN-L2-alt" wiki/log.md || fail "L-2: ersetzte Holder-ID nicht benannt (AC-2/AC-6)"
grep -qF "neue Holder-ID RUN-L2-neu" wiki/log.md || fail "L-2: neue Holder-ID nicht benannt (AC-2/AC-6)"
grep -qF "Baseline $BASE" wiki/log.md || fail "L-2: Baseline-Commit fehlt im Uebernahme-Eintrag (Vertrag §5)"
# Kein Doppel-Uebernehmer: eine zweite (sequentielle) AKQUISE derselben stale id schlaegt
# fehl (create-only, Ref existiert) — der Lock wird nur einmal ersetzt (genau einmal-Uebernahme, AC-2).
if scopelock_acquire "RUN-L2-zweiter"; then
fail "L-2: zweite Uebernahme gelang (AC-2: genau einmal verletzt)"
fi
[ "$(scopelock_content)" = "RUN-L2-neu" ] || fail "L-2: Lock-Inhalt nach zweitem Uebernahme-Versuch veraendert"
# Loop-2-P5: TAKEOVER-Exactly-once direkt am Takeover-Pfad (nicht nur am create-only-Akquise-
# pfad): ein zweiter scopelock_takeover-Versuch mit ALTEM Old-Value (RUN-L2-alt, Inhaber ist
# jetzt RUN-L2-neu) muss ATOMAR FEHLSCHLAGEN (Ownership-CAS: Lock traegt nicht mehr den
# erwarteten Old-Value) und den Lock UNVERAENDERT lassen.
oldblob2=$(printf '%s' 'RUN-L2-alt' | git hash-object -w --stdin) || fail "L-2-P5: oldblob2 nicht erzeugt"
neu2blob=$(printf '%s' 'RUN-L2-doppelt' | git hash-object -w --stdin) || fail "L-2-P5: neu2blob nicht erzeugt"
if git update-ref "$SCOPELOCK" "$neu2blob" "$oldblob2" 2>/dev/null; then
fail "L-2-P5: zweiter Takeover-CAS mit altem Old-Value gelang (AC-2: Takeover genau einmal verletzt)"
fi
[ "$(scopelock_content)" = "RUN-L2-neu" ] || fail "L-2-P5: zweiter Takeover-Versuch hat den Lock veraendert (Clobber)"
# Die verwaiste Registry-Zeile bleibt erhalten (nie still geloescht, AD-17e):
grep -qF "hold: run-l2 (Gen 1)" registry/wiki || fail "L-2: Registry-Zeile run-l2 still geloescht (AD-17e)"
scopelock_release "RUN-L2-neu"
[ -z "$(scopelock_content)" ] || fail "L-2: Lock nach Release nicht leer"
echo "RESULT: PASS — L-2: STALE_UEBERNAHME — stale Lease (bestaetigter Abbruch + Gen-Ablauf) genau EINMAL uebernommen; ersetzte Holder-ID benannt; genau eine aktive Root-Lease; kein Doppel-Uebernehmer; nie still geloescht (AC-2, AD-17e)"
pass
# =====================================================================
# L-3 DIRTY_GETRACKT (AC-3) — fremde getrackte uncommittete Aenderung im
# Mutationsbereich -> Abort-/Protect-Zustandsmaschine: SCHUETZEN (Scratch) ->
# Restore byte-identisch nach dem Run; UNCOMMITTED_INPUT-Abbruch abgestimmt §5.11
# Pkt. 3; nie geloescht (AD-17e).
# =====================================================================
runlabel "L-3: DIRTY_GETRACKT (AC-3) — Abort-/Protect: getrackte Fremd-Aenderung, byte-identischer Restore"
isolate l3
scopelock_acquire "RUN-L3" || fail "L-3: Akquise schlug fehl"
scopelock_header_banner "RUN-L3"
# Fremde getrackte Aenderung: ein anderer Producer hat wiki/alpha.md uncommittet modifiziert.
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)
# Pre-Mutation-Pruefung (§5.11 Pkt. 3): die Abweichung ist als working-copy-Marke sichtbar.
git status --porcelain -- wiki/alpha.md | grep -qE '^ M ' || fail "L-3: fremde getrackte Aenderung nicht als ' M ' sichtbar (§5.11 Pkt. 3)"
# UNCOMMITTED_INPUT-Abbruch abgestimmt §5.11 Pkt. 3: VOR jeder Mutation greift der benannte
# Abbruch 'published/committed Input erforderlich' — der Run mutiert NICHT und HEAD bleibt.
[ "$(git rev-parse HEAD)" = "$BASE" ] || fail "L-3: UNCOMMITTED_INPUT-Abbruch hat HEAD bewegt (Abbruch = keine Mutation)"
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" ] || fail "L-3: nach UNCOMMITTED_INPUT-Abbruch weichen weitere Pfade ab (keine Mutation) — '$ALPHA_ONLY'"
# ABORT-/PROTECT-ZUSTANDSMASCHINE (getrackt): SCHUETZEN — Sicherung in Scratch-Zone
# (ausserhalb wiki/, deterministisch benannt), dann Arbeitssatz bereinigt.
mkdir -p scratch/l3
cp wiki/alpha.md "scratch/l3/alpha.md.stash" || fail "L-3: Backup fehlgeschlagen — Fremdbearbeitung NICHT verworfen (AD-17e, Loop-2-P3)"
# Loop-2-P3: Backup VOR der Bereinigung verifiziert — schlaege es still fehl, wuerde
# `git checkout --` die fremde Aenderung still loeschen (AD-17e-Verletzung).
[ "$(sha256sum "scratch/l3/alpha.md.stash" | cut -d' ' -f1)" = "$FRANK_MD5" ] || fail "L-3: Backup nicht byte-identisch vor Bereinigung (Loop-2-P3, AD-17e)"
git checkout -q -- wiki/alpha.md
# Der Run mutiert jetzt (Mutations-Objekt alpha, Erhaltungs-Invariante erlaubt alpha + log.md):
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-16T12:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — Lifecycle-Mutation (raw/alpha-v1.md#S-1).
EOF
assert_frontmatter wiki/alpha.md
# RESTORIEREN (byte-identisch): nach dem Run wird die gesicherte Fremd-Aenderung aus der
# Scratch-Zone zurueckgespielt — nie geloescht, nie still weggelassen (AD-17e).
cp "scratch/l3/alpha.md.stash" wiki/alpha.md
[ "$(sha256sum wiki/alpha.md | cut -d' ' -f1)" = "$FRANK_MD5" ] || fail "L-3: Restore nicht byte-identisch (AC-3, AD-17e)"
[ "$(sha256sum scratch/l3/alpha.md.stash | cut -d' ' -f1)" = "$FRANK_MD5" ] || fail "L-3: Scratch-Sicherung nicht byte-identisch (AC-3)"
[ -f "scratch/l3/alpha.md.stash" ] || fail "L-3: Scratch-Artefakt fehlt — fremde Aenderung geloescht (AD-17e)"
# log.md-Dokumentation des Dirty-Tree-Schutzes (Vertrag §5, §5.11 Pkt. 3; Koordinationsereignis, AC-6):
printf '\n### 2026-08-21 — Dirty-Tree-Schutz: wiki/alpha.md nach scratch/l3/alpha.md.stash gesichert (nie geloescht, §5.11 Pkt. 3/6b; Baseline %s)\n' "$BASE" >> wiki/log.md
grep -qF "Dirty-Tree-Schutz: wiki/alpha.md nach scratch/l3/alpha.md.stash" wiki/log.md || fail "L-3: Dirty-Tree-Schutz-Dokumentation fehlt (§5.11 Pkt. 3/6b)"
# Endzustand: nur die fremde alpha-Abweichung + log.md (Erhaltungs-Invariante; kein Ghost-Diff).
inv_set | LC_ALL=C paste -sd' ' -
assert_invariant "alpha log"
scopelock_release "RUN-L3"
echo "RESULT: PASS — L-3: DIRTY_GETRACKT — Abort-/Protect-Zustandsmaschine (getrackt): SCHUETZEN (Scratch) -> Restore byte-identisch nach dem Run; UNCOMMITTED_INPUT-Abbruch abgestimmt §5.11 Pkt. 3; nie geloescht (AC-3, AD-17e)"
pass
# =====================================================================
# L-4 DIRTY_UNGETRACKT (AC-3) — fremde ungetrackte Datei im Mutationsbereich
# -> Protect: Scratch-Sicherung + byte-identischer Restore; keine stille
# Loeschung; log.md-Eintrag.
# =====================================================================
runlabel "L-4: DIRTY_UNGETRACKT (AC-3) — Protect: ungetrackte Fremd-Datei, byte-identischer Restore"
isolate l4
scopelock_acquire "RUN-L4" || fail "L-4: Akquise schlug fehl"
scopelock_header_banner "RUN-L4"
# Fremde ungetrackte Datei im Mutationsbereich (wiki/inoffiziell.md, nicht committet):
cat > wiki/inoffiziell.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
---
Inoffizieller Fremd-Entwurf (ungetrackt).
EOF
F_UNTRACK_MD5=$(sha256sum wiki/inoffiziell.md | cut -d' ' -f1)
git status --porcelain -- wiki/inoffiziell.md | grep -qE '^\?\? ' || fail "L-4: ungetrackte Fremd-Datei nicht als '??' sichtbar"
# PROTECT (ungetrackt): Sicherung in die Scratch-Zone, dann ist der Mutationsbereich sauber.
mkdir -p scratch/l4
cp wiki/inoffiziell.md "scratch/l4/inoffiziell.md.stash" || fail "L-4: Backup fehlgeschlagen — Fremd-Datei NICHT verorfen (AD-17e, Loop-2-P3)"
# Loop-2-P3: Backup VOR `git clean` verifiziert — ein still fehlgeschlagener `cp` wuerde
# die fremde ungetrackte Datei still loeschen (AD-17e-Verletzung).
[ "$(sha256sum "scratch/l4/inoffiziell.md.stash" | cut -d' ' -f1)" = "$F_UNTRACK_MD5" ] || fail "L-4: Backup nicht byte-identisch vor Clean (Loop-2-P3, AD-17e)"
git clean -qfd wiki/inoffiziell.md
# Der Run mutiert alpha (Kill-Punkt vor Mutation/Nach-Mutation, konsistenter Zwischenstand):
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-16T13:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — Lifecycle-Mutation L4 (raw/alpha-v1.md#S-1).
EOF
assert_frontmatter wiki/alpha.md
# RESTORIEREN: die gesicherte ungetrackte Fremd-Datei wird byte-identisch zurueckgespielt.
cp "scratch/l4/inoffiziell.md.stash" wiki/inoffiziell.md
[ "$(sha256sum wiki/inoffiziell.md | cut -d' ' -f1)" = "$F_UNTRACK_MD5" ] || fail "L-4: ungetrackte Fremd-Datei nicht byte-identisch restauriert (AC-3, AD-17e)"
[ -f "scratch/l4/inoffiziell.md.stash" ] || fail "L-4: Scratch-Sicherung fehlt — stille Loeschung (AD-17e)"
printf '\n### 2026-08-21 — Protect ungetrackt: wiki/inoffiziell.md nach scratch/l4/inoffiziell.md.stash gesichert + restauriert (nie geloescht, §5.11 Pkt. 3/§5.18 Pkt. 3; Baseline %s)\n' "$BASE" >> wiki/log.md
grep -qF "Protect ungetrackt: wiki/inoffiziell.md" wiki/log.md || fail "L-4: Protect-Dokumentation fehlt (AC-6)"
# Endzustand (Erhaltungs-Invariante §5.9 Pkt. 5): im L4-Fall erlaubte Mitglieder der
# Mutationsbereichs-Abweichung sind 'alpha' (Lifecycle-Mutation des Runs, nicht committet —
# Kill-Punkt nach Mutation, valider Zwischenstand), 'log' (Dokumentation) und die fremde
# ungetrackte 'inoffiziell'-Fremd-Datei, die der Run geschuetzt und byte-identisch restauriert
# hat (Fremd-Artefakt, kein Ghost des Runs; AD-17e). Kein weiterer Ghost-Diff:
inv_set | LC_ALL=C paste -sd' ' -
assert_invariant "alpha log inoffiziell"
scopelock_release "RUN-L4"
echo "RESULT: PASS — L-4: DIRTY_UNGETRACKT — Protect: ungetrackte Fremd-Datei gesichert + byte-identisch restauriert; keine stille Loeschung; log.md-Eintrag (AC-3, AD-17e)"
pass
# =====================================================================
# L-5 ROLLBACK_NACH_MUTATION (AC-4) — Fehler nach Mutation/Staging -> Rollback
# INDEX + WORKTREE aus <Baseline-Commit>; Post-Rollback-Diff leer; Kill-Punkt
# nach Mutation: konsistenter Endzustand.
# =====================================================================
runlabel "L-5: ROLLBACK_NACH_MUTATION (AC-4) — Rollback Index+Worktree, Post-Rollback-Diff leer"
isolate l5
scopelock_acquire "RUN-L5" || fail "L-5: Akquise schlug fehl"
scopelock_header_banner "RUN-L5"
# Mutation + Staging (Fehler-Simulation nach Mutation/Staging):
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-16T14:00:00Z
---
Das Alpha-Protokoll — fehlerhafte Teil-Mutation, die zurueckgerollt wird (raw/alpha-v1.md#S-1).
EOF
git add wiki/alpha.md
# Kill-Punkt NACH MUTATION: der Zwischenstand ist valide (nur alpha staged abweichend), ABER
# der Fehler wird erkannt -> ROLLBACK aus <Baseline-Commit>, INDEX + WORKTREE:
git reset -q --hard "$BASE"
# Post-Rollback-Diff gg. Baseline leer (Zustands-Restaurations-Invariante, §5.13 Pkt. 3):
[ "$(git diff --name-only "$BASE" -- wiki/ | wc -l)" = "0" ] || fail "L-5: Post-Rollback-Diff nicht leer (AC-4)"
[ "$(git status --porcelain -- wiki/ | wc -l)" = "0" ] || fail "L-5: Worktree nicht sauber nach Rollback (AC-4)"
[ "$(git rev-parse HEAD)" = "$BASE" ] || fail "L-5: Rollback hat HEAD bewegt (kein Teilzustand committet, AD-17f)"
# Kill-Punkt NACH MUTATION konsistent: Zustand == Baseline, kein hängender Lock (noch gehalten):
[ "$(scopelock_content)" = "RUN-L5" ] || fail "L-5: Lock nach Rollback nicht mehr vom Run gehalten"
scopelock_release "RUN-L5"
echo "RESULT: PASS — L-5: ROLLBACK_NACH_MUTATION — Rollback restoret Index + Worktree aus <Baseline-Commit>; Post-Rollback-Diff leer; Kill-Punkt nach Mutation konsistent (AC-4, §5.13 Pkt. 3)"
pass
# =====================================================================
# L-6 ROLLBACK_NACH_COMMIT_VOR_RELEASE (AC-4/AC-5) — Fehler nach Commit, vor
# Release -> Kill-Punkt vor Commit/vor Release: Zustand == Baseline oder valide
# committet, kein Teilzustand veroeffentlicht, kein haengender Lock.
# =====================================================================
runlabel "L-6: ROLLBACK_NACH_COMMIT_VOR_RELEASE (AC-4/AC-5) — Kill-Punkt vor Commit/vor Release"
isolate l6
scopelock_acquire "RUN-L6" || fail "L-6: Akquise schlug fehl"
scopelock_header_banner "RUN-L6"
# Veritaet: Mutations-Commit als valider Committed-Zustand (Bundle == committetes Ergebnis).
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-16T15:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — committeter Run (raw/alpha-v1.md#S-1).
EOF
assert_frontmatter wiki/alpha.md
printf '\n### 2026-08-21 — Story-3.12-L6: committeter Mutations-Nachweis (Baseline %s)\n' "$BASE" >> wiki/log.md
git add wiki/alpha.md wiki/log.md
git commit -qm "L6: committeter Run (Mutation + log.md-Nachweis)"
# Loop-2-P2: Matrix-Zelle ROLLBACK_NACH_COMMIT_VOR_RELEASE "Fehler nach Commit, vor Release"
# benannt und belegt: nach Commit ist der Zustand entweder == Baseline (nicht hier) ODER
# VALID COMMITTET — ein Rollback NACH Commit ist per AD-17f nicht vorgesehen (Commit ist
# unveraenderlich; die Matrix-"oder"-Semantik gilt). Der Fehler-Nachlauf loest sich in
# genau diesem Endzustand auf. "Valide committet" wird hier HART verifiziert:
[ "$(git rev-parse HEAD)" != "$BASE" ] || fail "L-6-P2: nach Commit HEAD == Baseline (Commit fehlgeschlagen — kein valide-committeter Zustand)"
git show -q HEAD:wiki/alpha.md | grep -q '^type: concept$' \
|| fail "L-6-P2: committeter Zustand nicht valide (Frontmatter im Commit verletzt)"
git show --format=%s -s HEAD | grep -q "L6: committeter Run" || fail "L-6-P2: Commit-Betreff nicht valide"
# Kill-Punkt VOR RELEASE: Commit ist erfolgt, aber der Lock ist noch NICHT freigegeben (kein
# haengender Lock — der Lock wird noch vom Run gehalten, kein Teilzustand veroeffentlicht).
git status --porcelain -- wiki/ | grep -q '^??' && fail "L-6: ungetrackte Reste nach Commit (Clean-Input-Guard-Vorfeld)"
[ "$(scopelock_content)" = "RUN-L6" ] || fail "L-6: Lock vor Release nicht gehalten (Kill-Punkt vor Release)"
# Fehler-Nachlauf vor Release wird sauber aufgeloest: Zustand == Baseline ODER valide
# committet (hier: valide committet, P2-verified) — der Run gibt durativ frei; ein
# Teilzustand wird nie veroeffentlicht (AD-17f).
scopelock_release "RUN-L6"
[ -z "$(scopelock_content)" ] || fail "L-6: Lock nach Release nicht leer (AC-5)"
# Clean-Input-Guard-Vorfeld: sauberer Worktree nach Commit+Release.
[ "$(git status --porcelain -- wiki/ | wc -l)" = "0" ] || fail "L-6: Worktree nach Commit+Release nicht sauber"
echo "RESULT: PASS — L-6: ROLLBACK_NACH_COMMIT_VOR_RELEASE — nach Commit, vor Release: Zustand == Baseline oder valide committet; kein Teilzustand; kein haengender Lock (AC-4/AC-5, AD-17f)"
pass
# =====================================================================
# L-7 RELEASE_DURABLE (AC-5) — erfolgreicher Run: Mutation + zulaessiger
# Log-Nachweis committet; Lock per Ref-Delete entfernt; Worktree sauber;
# FOLGE-RUN besteht den Clean-Input-Guard (kein INPUT_UNCOMMITTED, kein
# haengender Lock, Fremd-Bytes restauriert).
# =====================================================================
runlabel "L-7: RELEASE_DURABLE (AC-5) — durable Release + Clean-Input-Guard des Folge-Runs"
isolate l7
scopelock_acquire "RUN-L7" || fail "L-7: Akquise schlug fehl"
scopelock_header_banner "RUN-L7"
# Erster Run (durativ): Mutation + zulaessiger Log-Nachweis committet, dann Ref-Delete-Release.
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-16T16:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — durativer Run (raw/alpha-v1.md#S-1).
EOF
assert_frontmatter wiki/alpha.md
printf '\n### 2026-08-21 — Story-3.12-L7: durativer Run, Freigabe per Ref-Delete (Baseline %s)\n' "$BASE" >> wiki/log.md
git add wiki/alpha.md wiki/log.md
git commit -qm "L7: durativer Run (Mutation + Log-Nachweis)"
# Loop-2-P2: RELEASE-Fehler negativ geuebt (Matrix RELEASE_DURABLE "Release-Fehler =
# HARD-FAIL; Lock nie haengend"): ein Release mit FALSCHER Inhaber-Run-ID muss hart
# FEHLSCHLAGEN (Ownership-Pruefung) und den Lock UNVERAENDERT lassen — in einem Subshell,
# damit die erwartete HARD-FAIL-Beendigung das Skript selbst nicht toetet.
if ( scopelock_release "RUN-L7-fremd" ) 2>/dev/null; then
fail "L-7-P2: Release OHNE Ownership gelang (HARD-FAIL-Pfad nicht wirksam, AC-5)"
fi
[ "$(scopelock_content)" = "RUN-L7" ] || fail "L-7-P2: fehlgeschlagenes Release hat den Lock veraendert (Lock haengt/verfalscht, AC-5)"
# Release per Ref-Delete (nur durch Inhaber, Ownership-Pruefung):
scopelock_release "RUN-L7"
# Lock ist entfernt (ref-delete):
if git rev-parse -q --verify "$SCOPELOCK" >/dev/null 2>&1; then
fail "L-7: Lock nach Ref-Delete-Release existiert noch (AC-5)"
fi
# Worktree sauber (kein INPUT_UNCOMMITTED-Abbruch moeglich):
[ "$(git status --porcelain -- wiki/ | wc -l)" = "0" ] || fail "L-7: Worktree nach Release nicht sauber (AC-5)"
# FOLGE-RUN besteht den Clean-Input-Guard: sauberer Worktree, kein haengender Lock, keine
# Fremd-Bytes — der Folge-Run akquiriert ohne INPUT_UNCOMMITTED-Abbruch.
scopelock_acquire "RUN-L7-folge" || fail "L-7: Folge-Run-Akquise schlug fehl"
[ "$(scopelock_content)" = "RUN-L7-folge" ] || fail "L-7: Folge-Run-Lock-Inhalt falsch"
# Kein INPUT_UNCOMMITTED-Abbruch: die Pre-Mutation-Pruefung meldet clean (keine Fremd-/Rest-Bytes).
[ "$(git status --porcelain -- wiki/ | wc -l)" = "0" ] || fail "L-7: Folge-Run startet mit INPUT_UNCOMMITTED-Rest (Clean-Input-Guard verletzt, AC-5)"
scopelock_release "RUN-L7-folge"
echo "RESULT: PASS — L-7: RELEASE_DURABLE — Mutation + Log-Nachweis committet; Lock per Ref-Delete entfernt; Worktree sauber; Folge-Run besteht den Clean-Input-Guard (AC-5)"
pass
# =====================================================================
# L-8 KANONISCHES_LOG (AC-6) — wiki/log.md enthaelt nur vertragskonforme
# fachliche + noetige Koordinationsereignisse; Build-/Review-/Story-/Sandbox-
# Historie bleibt ausserhalb; kein Ghost-Diff.
# =====================================================================
runlabel "L-8: KANONISCHES_LOG (AC-6) — canonical log nur vertragskonform, Historie ausserhalb"
isolate l8
scopelock_acquire "RUN-L8" || fail "L-8: Akquise schlug fehl"
scopelock_header_banner "RUN-L8"
# Run schreibt NUR vertragskonforme Eintraege (Koordinations-/Fach-Ereignisse):
printf '\n### 2026-08-21 — Lease-Akquise: wiki Root-Scope durch RUN-L8 (Baseline %s)\n' "$BASE" >> wiki/log.md
printf '\n### 2026-08-21 — Freigabe: wiki Root-Scope durch RUN-L8 (Ref-Delete, Baseline %s)\n' "$BASE" >> wiki/log.md
# Build-/Review-/Story-/Sandbox-Historie bleibt AUSSERHALB des Bundles:
# - Sandbox-Nachweis: dieser laufende Nachweis lebt in _bmad-output/... (ausserhalb wiki/)
# - Registry (kumulativer Aufbau) liegt ausserhalb wiki/ (registry/ als Datei je Area),
# kein `# Log`-Stand im Wiki.
mkdir -p registry
echo "gen: 1" > registry/wiki
echo "hold: run-l8 (Gen 1)" >> registry/wiki
# KERN (AC-6): log.md traegt KEINE Build-/Review-/Story-/Sandbox-Historie als
# KATEGORIE-benennende Eintraege (z. B. 'Build-Historie', 'Sandbox-Protokoll',
# 'Story-Log', 'Review-Bericht'). Legitime Fach-/Koordinationswoerter im Fliesstext —
# inkl. der von der eigenen Code-Map und der 3.10/3.11-Praxis getragenen Nachweis-
# Formen (Sandbox-/Review-/Validator-Nachweis-Kennzeichnungen, Artefakt-Pfadszitate,
# 'bmad-code-review', Story-Nummern ohne Kategorie-Endung) — werden NICHT getroffen:
# sie sind notwendige Koordinationsereignisse im Sinne von Vertrag §5, keine verbotene
# Historie (PATCH 4d, Loop-2-D1: Regel an die dokumentierte log.md-Praxis angegleichen
# — die Code-Map VORGIBT diese Zitate im Story-Log-Eintrag; ein Guard, der sie traf,
# wuerde die eigene Vorgabe verletzen).
grep -qiE '(Build|Review|Story|Sandbox)-(Histor|Log|Protokoll|Bericht)' wiki/log.md \
&& fail "L-8: log.md traegt Build-/Review-/Story-/Sandbox-Historie (AC-6 verletzt; PATCH 4d, Loop-2-D1)"
# Loop-2-D1 NEGATIV-KONTROLLE: die verbotene Form (Kategorie-Hyphenat) muss den Guard
# AUSLOESEN — auf einer Probe-Datei ausserhalb wiki/, damit das konforme log.md nicht
# beruehrt wird und bewiesen ist, dass die Regel zündet:
printf '### 2026-08-21 — Sandbox-Protokoll: Build-Historie und Story-Log (Review-Bericht)\n' > /tmp/l8-probe-$$
grep -qiE '(Build|Review|Story|Sandbox)-(Histor|Log|Protokoll|Bericht)' /tmp/l8-probe-$$ \
|| fail "L-8-D1: Negativ-Kontrolle — Guard hat verbotene Kategorie-Form NICHT erkannt (Regel wirkungslos)"
rm -f /tmp/l8-probe-$$
# Positiv-Kontrolle (PATCH 4d): die beiden vertragskonformen Koordinations-Eintraege sind
# vorhanden und loesen den Kategorie-Check NICHT aus — kein false-HARD-FAIL auf legitime
# Koordinations-Eintraege im Fliesstext.
grep -qF 'Lease-Akquise: wiki Root-Scope durch RUN-L8' wiki/log.md || fail "L-8: Koordinations-Eintrag Akquise fehlt (PATCH 4d Positiv-Kontrolle)"
grep -qF 'Freigabe: wiki Root-Scope durch RUN-L8' wiki/log.md || fail "L-8: Koordinations-Eintrag Freigabe fehlt (PATCH 4d Positiv-Kontrolle)"
# Der einzige wiki/-Unterschied ist log.md (Registry/Lock ausserhalb, kein Ghost-Diff):
git add wiki/log.md registry/wiki
git commit -qm "L8: canonisches Log (2 Koordinations-Eintraege) + Registry (ausserhalb wiki/)"
diff_set=$(probe)
[ "$diff_set" = "log" ] || fail "L-8: nach L8-Commit weicht der Mutationsbereich ab: '$diff_set' (erwartet nur log; Registry liegt ausserhalb wiki/ und ist daher kein Ghost)"
[ "$(git status --porcelain -- wiki/ | wc -l)" = "0" ] || fail "L-8: Worktree nach Commit nicht sauber (Clean-Input-Guard-Vorfeld)"
scopelock_release "RUN-L8"
echo "RESULT: PASS — L-8: KANONISCHES_LOG — wiki/log.md nur vertragskonforme fachliche + Koordinationsereignisse; Build-/Review-/Story-/Sandbox-Historie ausserhalb; Registry kumulativ ausserhalb; kein Ghost-Diff (AC-6)"
pass
# =====================================================================
# L-9 KILLPUNKT_TESTS (AC-7) — vier Kill-Punkte (vor Mutation / nach Mutation /
# vor Commit / nach Commit) — je konsistenter Endzustand (Zustands-Restaurations-
# Invariante §5.13 Pkt. 3).
# =====================================================================
runlabel "L-9: KILLPUNKT_TESTS (AC-7) — 4 Kill-Punkte, je konsistenter Endzustand"
isolate l9
scopelock_acquire "RUN-L9" || fail "L-9: Akquise schlug fehl"
scopelock_header_banner "RUN-L9"
# KILLPUNKT 1 — VOR MUTATION: Zustand == Baseline, keine Zwischenstaende, kein Ghost.
[ "$(git rev-parse HEAD)" = "$BASE" ] || fail "L-9/KP1: vor Mutation Head != Baseline"
[ "$(git status --porcelain -- wiki/ | wc -l)" = "0" ] || fail "L-9/KP1: vor Mutation Worktree nicht sauber"
echo " KP1 (vor Mutation): Zustand == Baseline, kein Zwischenstand — PASS"
# KILLPUNKT 2 — NACH MUTATION: Zustand == valider Zwischenstand (geplanter Plan-Freeze),
# kein Ghost-Diff ausserhalb der erlaubten Pfad-Menge.
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-16T17:00:00Z
---
Das Alpha-Protokoll — Kill-Punkt-nach-Mutation (raw/alpha-v1.md#S-1).
EOF
git add wiki/alpha.md
# Erlaubte Pfad-Menge nach Mutation: alpha (Kill-Punkt-Zwischenstand). Staging nur alpha.
staged=$(git diff --cached --name-only | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u)
[ "$staged" = "alpha" ] || fail "L-9/KP2: nach Mutation sind nur erlaubte Pfade staged, tatsaechlich '$staged'"
# Loop-2-P4: Ghost-Diff-Probe (Matrix KILLPUNKT_NACH_MUTATION "kein Ghost-Diff ausserhalb
# erlaubter Menge"): Index UND Worktree ueber die §5.9-P5-Helfer gegen die erlaubte Menge —
# unstagede Ghost-Dateien wuerden hier hart fehlschlagen.
assert_invariant "alpha"
echo " KP2 (nach Mutation): valider Zwischenstand (nur alpha staged), kein Ghost-Diff ausserhalb erlaubter Menge — PASS"
# KILLPUNKT 3 — VOR COMMIT: nur erlaubte Pfad-Menge staged, kein Teilzustand committet.
[ "$(git rev-parse HEAD)" = "$BASE" ] || fail "L-9/KP3: vor Commit Head bewegt (Teilzustand committet, AD-17f)"
printf '\n### 2026-08-21 — Story-3.12-L9: Kill-Punkt-Test (Baseline %s)\n' "$BASE" >> wiki/log.md
git add wiki/log.md
[ "$(git diff --cached --name-only | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u | paste -sd' ' -)" = "alpha log" ] \
|| fail "L-9/KP3: vor Commit sind nicht exakt alpha+log staged (Teilzustand, AD-17f)"
# Loop-2-P4: Ghost-Diff-Probe an KP3 (nur erlaubte Pfad-Menge staged, Worktree frei von Ghosts):
assert_invariant "alpha log"
echo " KP3 (vor Commit): nur alpha + log staged, kein Commit, HEAD == Baseline, kein Ghost — PASS"
# KILLPUNKT 4 — NACH COMMIT: Bundle == valide committeter Zustand, anschliessbar an den
# Clean-Input-Guard (kein haengender Lock, sauberer Worktree).
git commit -qm "L9: Kill-Punkt-nach-Commit (Mutation + log.md)"
[ "$(git status --porcelain -- wiki/ | wc -l)" = "0" ] || fail "L-9/KP4: nach Commit Worktree nicht sauber (Clean-Input-Guard-Vorfeld)"
[ "$(scopelock_content)" = "RUN-L9" ] || fail "L-9/KP4: Lock nach Commit nicht gehalten (vor Release)"
git show --format=%s -s HEAD | grep -q "L9: Kill-Punkt-nach-Commit" || fail "L-9/KP4: Commit-Betreff nicht valide"
scopelock_release "RUN-L9"
echo " KP4 (nach Commit): valide committet, sauber, kein haengender Lock — PASS"
[ -z "$(scopelock_content)" ] || fail "L-9: Lock nach Release nicht leer"
echo "RESULT: PASS — L-9: KILLPUNKT_TESTS — 4 Kill-Punkte (vor/nach Mutation, vor/nach Commit) je konsistenter Endzustand; Clean-Input-Guard-Vorfeld (AC-7, §5.13 Pkt. 3)"
pass
# =====================================================================
# Abschluss: Gesamt-Aussage und Zaehler (L-1..L-9 == 9 harte PASS)
# =====================================================================
echo
echo "#### SANDBOX-3-12 GESAMT-ERGEBNIS ####"
echo "Harte PASS-Assertions: $PASS_COUNT"
[ "$PASS_COUNT" -ge 9 ] || { echo "HARD-FAIL: erwartet >= 9 harte PASS-Assertions, tatsaechlich $PASS_COUNT" >&2; exit 1; }
# Kein Zugriff auf das reale Bundle/raw/ (AD-3): das Skript lebt ausschliesslich auf dem
# /tmp-Baum; der reale wiki/- oder raw/-Baum wurde nicht beruehrt.
# Finale Baumsauberkeits-Pruefung (echte Assertion — sauberer Endzustand, kein Carry-over):
final_dirty=$(git status --short --porcelain)
[ -z "$final_dirty" ] || { echo "HARD-FAIL: Sandbox-Arbeitsbaum am Ende nicht sauber: $final_dirty" >&2; exit 1; }
echo "Alle Szenarien L-1..L-9 harte PASS (Exit 0) — transaktionaler Lease-Lifecycle & Commit-Abschluss"
echo "verifiziert (§5.18, Revision 3.7): kein Zugriff auf reales Bundle/raw/."
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
exit 0
File diff suppressed because it is too large Load Diff
@@ -4,8 +4,9 @@ type: 'feature'
created: '2026-08-21'
status: 'done'
baseline_commit: 'a8b486d0f04b44344cdfa62e9cc32dfea0d49abd'
review_loop_iteration: 0
context: []
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">
@@ -82,8 +83,8 @@ Der Lock muss im clone-geteilten Zustand liegen, weil nur dieser Zustand von all
**Commands:**
- `bash _bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh` -- expected: alle Szenarien harte PASS (u.a. „niemals zwei aktive Root-Leases"-Assertion, `LEASE_HOLD`-Reihenfolge), Exit 0, kein Zugriff auf reales Bundle/`raw/`.
- `grep -n "3.11\|3.6" schema/compiler.md` und `grep -n "Revision 3.6"` -- expected: neuer Abschnitt und Revisionslogeintrag vorhanden; bestehende Pkt.-1/2-Texte unverändert (diff checkt nur additive Zeilen).
- `git -C _bmad-output diff` vs. `git -C schema/compiler.md diff` -- expected: nur additive Änderung an `schema/compiler.md`; `validator.md`/`wiki-compiler.md`/`adapters/`/`raw/` ohne Diff.
- `grep -n "Revision 3.6" schema/compiler.md` und `grep -n "## 5.17" schema/compiler.md` -- expected: §5.17-Überschrift und §8-Revisionslogeintrag vorhanden; bestehende Pkt.-1/2-Texte unverändert (diff checkt nur additive/berichtigte Zeilen).
- `git diff --stat` (Repo-Root) -- expected: nur die Review-Loop-1-Touched-Files (Sandbox, `schema/compiler.md` §5.17-Berichtigungen, Spec, log.md, deferred-work.md, sprint-status.yaml); `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/` ohne Diff (AD-3).
**Manual checks (if no CLI):**
- Keine Beschreibung nötig — sämtliche Nachweise laufen über die Sandbox (Exit-Code) und additive Diffs.
@@ -95,30 +96,71 @@ Der Lock muss im clone-geteilten Zustand liegen, weil nur dieser Zustand von all
- Einstieg: die §5.17-Atomaritäts-Präzisierung — Exklusivitätsschlüssel im clone-geteilten Zustand, Run-ID als Lock-Inhalt, create-only-Akquise, LEASE_HOLD, Kollisions-Hold an Epic 4 (AC-a..e).
[`compiler.md:415`](../../schema/compiler.md#L415)
- Abgrenzungsklausel der Story-3.11-Verankerung — Fugen-Identität von §5.11-Pkt.-1/2 und §5.12 bleibt erhalten, keine neue §7-Klasse, kein Standalone.
[`compiler.md:527`](../../schema/compiler.md#L527)
[`compiler.md:424`](../../schema/compiler.md#L424)
**Atomarer Lock im clone-geteilten Zustand**
- Atomare create-only-Akquise (`git update-ref <lock-ref> <wert> $ZERO_SHA`) — gits Ref-Sperre serialisiert Worktrees/Prozesse; deterministischer Blob-Inhalt.
[`run-sandbox.sh:120`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L120)
- Zwei-Prozess-Überlappung im Zwei-Worktree — Schlüssel-Beweis AC-b/AC-d (genau ein Gewinner, ein LEASE_HOLD, Zwischenzustands-Sampler).
[`run-sandbox.sh:207`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L207)
[`run-sandbox.sh:124`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L124)
- Zwei-Prozess-Überlappung im Zwei-Worktree — Schlüssel-Beweis AC-b/AC-d (genau ein Gewinner, ein LEASE_HOLD, gehärteter Zwischenzustands-Sampler: Ref-Kardinalität + Wert-Evidenz).
[`run-sandbox.sh:217`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L217)
**Kollisions-Hold & Abweisung**
- LEASE_HOLD-Nicht-Mutation: abgewiesener Producer lässt `wiki/`/Lock/`log.md` unverändert, keine Commits, fremde Lease erhalten (AC-c) — inkl. gehärteter `log.md`-Assertion.
[`run-sandbox.sh:292`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L292)
- Kollisions-Hold mit beiden Commit-Hashes + Scope in `log.md` an Epic 4 (AC-e, AD-17c, kein textueller Auto-Merge) — Datei-Level-Attest nachgeschärft.
[`run-sandbox.sh:362`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L362)
- LEASE_HOLD-Nicht-Mutation: abgewiesener Producer lässt `wiki/`/Lock/`log.md` unverändert, keine Commits, fremde Lease erhalten (AC-c) — Worktree-Beobachtung des Arbeitsbaums (D-3.11-2).
[`run-sandbox.sh:339`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L339)
- Kollisions-Hold mit beiden Commit-Hashes + Scope + Baseline in `log.md` an Epic 4 (AC-e, AD-17c, kein textueller Auto-Merge) — `MERGE_OK` wird hart abgewiesen; Datei-Level-Attest auf den Post-Merge-State.
[`run-sandbox.sh:460`](../../_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh#L460)
**Status- & Nachweissynchronisation (peripher)**
- Revisionsnachweis Story 3.11 → `review` in der Laufzeitakte — Verankerung, Sandbox, Abschlussklausel, Erhaltungs-Invariante.
[`log.md:47`](../../wiki/log.md#L47)
[`log.md:4`](../../wiki/log.md#L4)
- Planungsartefakt auf Ist-Zustand nachgeführt (epic-3-context Z. 41).
[`epic-3-context.md:41`](../../_bmad-output/implementation-artifacts/epic-3-context.md#L41)
**Härtung & Konsistenz-Funde (defer)**
- Konsistenz-, Drift- und Härtungs-Funde aus der Review in der Defer-Liste festgehalten (AC-d-Token, Zeilenanker, dynamische Negativ-Kontrolle, Platzhalter-Banner).
[`deferred-work.md:577`](../../_bmad-output/implementation-artifacts/deferred-work.md#L577)
- Konsistenz-, Drift- und Härtungs-Funde aus der Review in der Defer-Liste festgehalten (AC-d-Token, Zeilenanker, dynamische Negativ-Kontrolle, Platzhalter-Banner) inkl. KORREKTUR-Teileintrag (Zeilen-Drift-Defer widerlegt).
[`deferred-work.md:578`](../../_bmad-output/implementation-artifacts/deferred-work.md#L578)
### Review Findings
_Bmad-code-review Loop 1 (2026-08-21), 4 Layer (blind-hunter/edge-case-hunter/verification-gap/acceptance-auditor). Triage: 2 decision-needed, 20 patch, 5 defer, 5 dismissed. Sandbox re-executiert (Windows/Git-Bash): A-1..A-8 8/8 harte PASS, Exit 0, kein Zugriff auf reales Bundle/raw/._
#### Decision-Needed
- [x] [Review][Decision] **D-3.11-1 AC-d-Beweisart (Zwischenzustands-Assertion)***Gelöst (Nutzer-Entscheidung 2026-08-21):* **Konstruktiv + härten** — AC-4 als konstruktiv erfüllt akzeptiert (eine scope-bezogene Ref + atomarer create-only-Write → zu keinem Zeitpunkt zwei aktive Root-Leases); A-2-Sampler zur lebendigen Wert-Evidenz gehärtet, §5.17-/log.md-Nachweis um „konstruktiv“ qualifiziert. Kein frozen-Block-Neuverhandeln. Hintergrund: der A-2-Sampler zählt die Ref-Kardinalität (`grep -cF "$SCOPELOCK"`, Z. 237, nie >1 bei einem Ref-Namen) und der `TWO_LOCKS_AT_ONCE`-Marker geht per `>&2` verloren, weil `out_a` nur stdout fängt (Z. 238 vs. Z. 255) — die Assertion war als *beobachtete* Evidenz strukturell tot. [run-sandbox.sh:237-257]
- [x] [Review][Decision] **D-3.11-2 A-4-Mutations-Regime (Nicht-Mutation des abgewiesenen Producers)***Gelöst (Nutzer-Entscheidung 2026-08-21):* **Intentionalen Mutationsversuch bauen** — in A-4 einen bewussten Mutations-/Commit-Versuch des abgewiesenen Producers in die Sandbox einbauen, dessen Ausbleiben/Ablehnung dann hart geprüft wird (deckt AC-c als Verhalten ab, nicht nur als Selbstvergleich). Hintergrund: der LEASE_HOLD-Pfad unternimmt heute keinen Mutationsversuch (nur `echo "LEASE_HOLD"`, Z. 309-311) und die AC-c-Assertions vergleichen `git show "$BASE:…"` mit sich selbst (invarianter Baseline-Blob, nie der Worktree; Z. 297/314/323) — uncommittete Arbeitsbaum-Mutationen des Abgewiesenen schifften unentdeckt durch. [run-sandbox.sh:297-324]
#### Patch
- [x] [Review][Patch] **A-7 `MERGE_OK`-Zweig akzeptiert den verbotenen textuellen Auto-Merge**`case "$ma" in MERGE_CONFLICT|MERGE_OK)` lässt beide Outcomes durch; wäre `git merge --no-edit` clean gegangen, läge exakt der von AD-17c/AC-e verbotene stille Merge vor, der Test meldet dennoch PASS. `MERGE_OK` hart `fail`en (oder begründen, warum nur CONFLICT zulässig ist); dazu die Body-Assertions auf den **Post-Merge-HEAD von `branch-y`** (`git show branch-y:wiki/alpha.md`) statt auf die invarianten `$HASH_X/$HASH_Y`-Objekte ausrichten. [run-sandbox.sh:406-443]
- [x] [Review][Patch] **A-7 stale-Aussage-Kommentar widerspricht dem Code** — Kommentar „(a) … `git merge` … der Befehl wird gar nicht erst ausgeführt, … kein Merge“ (Z. 401-405) steht über `merge_attempt()`, das den Merge real ausführt; zudem unverstandener Rest „ablösen von alpha.txt-loesungen“. Kommentar an den tatsächlichen Ablauf korrigieren. [run-sandbox.sh:401-414]
- [x] [Review][Patch] **A-7 `hold_msg`-Variablen-Case ist tautologisch** — die zwei `case "$hold_msg"`-Checks (Z. 430-437) prüfen eine gerade konstruierte Zeichenkette gegen sich selbst (der erste `*"$HASH_X"*|*"$HASH_Y"*` bestünde sogar bei nur einem Hash); nur die Datei-Checks (Z. 450-453) sind real. Variablen-Case entfernen oder auf den geschriebenen `log.md`-Inhalt reduzieren. [run-sandbox.sh:430-437]
- [x] [Review][Patch] **A-7 `log.md`-Hold-Eintrag trägt keinen `<Baseline-Commit>`** — die zitierte Hold-Mechanik (§5.16 Pkt. 8/§5.10 Pkt. 8) verlangt „Quell-Pfad + `<Baseline-Commit>`“; der geschriebene Eintrag (Z. 447) hat Scope + beide Hashes, aber kein Baseline-Commit. Baseline-Komponente ergänzen oder begründen, warum sie beim Kollisions-Hold entfällt. [run-sandbox.sh:446-448]
- [x] [Review][Patch] **`assert_frontmatter()` ist tot — behauptete „at-Normalform je Szenario“ wird nicht ausgeführt** — die Funktion (Z. 155-164) prüft §3.3/§3.4-Frontmatter + `at`-ISO-Normalform, wird aber nirgendwo aufgerufen; `wiki/log.md`/Revision-3.6 attestiieren dennoch „Erhaltungs-Invariante + at-Normalform je Szenario“. Aufruf ergänzen (z. B. `assert_frontmatter wiki/alpha.md` nach A-7, wo `alpha.md` neu geschrieben wird) oder die Nachweis-Formulierung in log.md/Revision korrigieren. [run-sandbox.sh:155-164]
- [x] [Review][Patch] **A-3-Kommentar behauptet „zweiter Worktree, eigener Prozess“, ist aber sequentiell im Hauptprozess** — die zweite Akquise (Z. 285-286) ist ein direkter `scopelock_acquire`-Aufruf ohne `git worktree add`/Subshell; „zeitlich ÜBERLAPPEND“ ist hier falsch (echte Überlappung liegt in A-2). Kommentar entlarven/entschärfen. [run-sandbox.sh:282-286]
- [x] [Review][Patch] **A-3 Identitäts-Fall nicht geübt** — der Kommentar behauptet „auch eine identisch benannte Run-ID kann den Halter nicht ersetzen“, der zweite Versuch nutzt aber `RUN-A3-identisch` (andere ID); der exakte Identitäts-Fall `RUN-A3` wird nicht geübt. Entweder mit `RUN-A3` akquirieren oder den Kommentar korrigieren. [run-sandbox.sh:284-288]
- [x] [Review][Patch] **A-1 toter `run_a1`-Blob**`run_a1=$(printf 'producer-a1-run-id' | git hash-object -w --stdin)` (Z. 179) wird nie verwendet (echter Lock-Inhalt ist `RUN-A1`); Dead Code, schreibt ein ungenutztes Objekt. Entfernen. [run-sandbox.sh:179]
- [x] [Review][Patch] **A-8 Commit-Zähler-Check am falschen Punkt** — der `git rev-list --count`-Check **vor** dem ersten `scopelock_release` (Z. 468-469) kann nur Akquise-Commits, keine Release-Commits nachweisen; der Check nach dem Release (Z. 479-480) ist der wirksame. Erster Check entfernen oder Kommentar korrigieren („hart, vor dem ersten Release“ trifft die eigene Aussage nicht). [run-sandbox.sh:466-480]
- [x] [Review][Patch] **Sandbox-Schlusskontrolle ist ein No-op**`git status --short --porcelain >/dev/null 2>&1 || true` (Z. 494) verwirft Ausgabe und Exit-Code und attestiert nichts (weder Bundlesauberkeit noch „kein Zugriff auf reale Pfade“); dazu bleiben die in A-2/A-4 angelegten Worktrees (`$ROOT/wt-a/wt-b/wt-a4`) registriert und werden nicht abgeräumt. Ende-Assertion auf eine echte Prüfung umstellen (z. B. `git worktree list`-Prüfung gg. `$ROOT`) oder Worktrees aufräumen + No-op entfernen. [run-sandbox.sh:494]
- [x] [Review][Patch] **`scopelock_release()` ohne Ownership-/Exit-Code-Kopplung** — `git update-ref -d "$SCOPELOCK"` (Z. 132-134) läuft ohne `|| fail` und ohne Inhaber-Bindung (kein Abgleich des Ref-Inhalts mit der eigenen Run-ID); ein fehlgeschlagenes Release würde A-1/A-3/A-5/A-6 nicht bemerken. Release-Stellen hart koppeln; Ownership-Prüfung ergänzen oder explizit als Story-3.12-Home benennen. [run-sandbox.sh:132-134]
- [x] [Review][Patch] **`isolate()` räumt den geteilten Ref-Namespace nicht** — `reset --hard`/`git clean` betreffen nur Arbeitsbaum und HEAD; `refs/leases/wiki` überdauert Szenario-Grenzen und hängt an der impliziten Release-Disziplin. Expliziten Ref-Reset in `isolate()` ergänzen, damit ein fehlgeschlagenes Release die Folgeszenarien nicht still kontaminiert. [run-sandbox.sh:99-105]
- [x] [Review][Patch] **Asymmetrische Release-Ende-Leereprüfung** — A-8 prüft nach dem Release `[ -z "$(scopelock_content)" ]`, A-1/A-3/A-5/A-6 tun es nicht; ein hängender Lock wäre erst in einem späteren, nicht-attributierbaren Szenario auffindbar. Ende-Check je Szenario vereinheitlichen. [run-sandbox.sh:189-189, 288-288, 342-342, 359-359]
- [x] [Review][Patch] **§5.17 Pkt. 5 Grammatikbruch + fehlendes Ablage-Ziel** — „an Epic 4 übergeben — **der** benannten Hold-Mechanik“ (Anstelle von „an“); zudem benennt §5.17 Pkt. 5 nirgends, *wo* der Kollisions-Hold liegt (Run-Receipt? `log.md`?), während A-7 ihn in `wiki/log.md` schreibt. Wortlaut berichtigen + Ablage-Stelle normativ benennen. [compiler.md:423]
- [x] [Review][Patch] **§5.17 Pkt. 1: „committete Ref“ ist technisch inkorrekt** — eine Ref ist kein Commit-Objekt und nicht Teil eines Commits; die Sandbox realisiert es als Blob (`git hash-object -w`) plus Ref-Write. Für eine normative Sektion, aus der Story 3.12 den Lifecycle ableitet, unpräzise (Lebensdauer des Locks unklar). Auf „geschriebene/verwiesene Ref mit Blob-Inhalt“ berichtigen. [compiler.md:416]
- [x] [Review][Patch] **§5.17 Pkt. 4: Determinismus-Aussage gilt nicht im concurrenten Fall** — „gleicher Geteilter-Zustand + gleiche Eingabemenge → identische Gewinner-/Verlierer-Entscheidung“ trifft nur für sequentielle Versuche (A-3/A-6); bei überlappenden Producers hängt die Gewinnerwahl vom Scheduling ab, nicht allein vom geteilten Zustand. Pkt.-4-Aussage auf den deterministischen Kern (create-only-Existenzprüfung) eingrenzen oder den concurrenten Fall ausnehmen. [compiler.md:419]
- [x] [Review][Patch] **Suggested-Review-Order-Anker bei Commit-Zeit falsch**`compiler.md:415` zeigt auf die Leerzeile vor dem §5.17-Intro; `compiler.md:527` auf die §8-Revision-3.6-Logzeile (die Abgrenzungsklausel Pkt. 6 liegt ≈ Z. 424); `log.md:47` auf eine Zeile unterhalb des 3.11-Eintrags (der liegt auf Z. 4); `deferred-work.md:577` auf die Leerzeile vor dem neuen „Deferred from“-Header (Z. 578). Anker neu setzen. [spec:642, 644, 663, 670]
- [x] [Review][Patch] **Defer-Evidence benennt die falsche Stelle** — der Zeilen-Drift-Defer in `deferred-work.md` verweist auf „die Spec (Code Map) und die Task-1-Rationale“, die konkreten Fehlancker sitzen aber in der Suggested-Review-Order; zudem ist die Aussage „§5.11 (Z. 304-321)/§5.12 (Z. 323-342) nicht mehr aktuell“ **falsch** — §5.11 liegt weiterhin auf Z. 304, §5.12 auf Z. 323 (verifiziert bei e02cf84). Evidence präzisieren; den Zeilen-Drift-Teileintrag berichtigen (es gibt keinen Defekt an diesen Anker). [deferred-work.md:580-585]
- [x] [Review][Patch] **Spec-Frontmatter `context: []` trägt die Code-Map-Abhängigkeit nicht** — die Code Map und Task 5 nennen `epic-3-context.md` (Z. 41) als nachgeführtes Planungsartefakt; die Präzedenz-Spec-3.10 listet dieselbe Datei im `context:`-Feld. `epic-3-context.md` in `context:` aufnehmen. [spec:8]
- [x] [Review][Patch] **Spec-Verification-Kommando ungültig**`git -C schema/compiler.md diff` ist ungültig (`git -C` erwartet ein Verzeichnis, `schema/compiler.md` ist eine Datei); `grep -n "3.11\|3.6"` trifft zudem etliche „Story 3.6“-Erwähnungen in §5.11/§5.12 und taugt nicht als gezielte Revision-3.6-Prüfung. Kommandos berichtigen. [spec:631-632]
#### Defer
- [x] [Review][Defer] **AC-d-Token ohne normative Vorlagen-Definition** — der `AC-d`-Token (Sandbox A-2/A-3, §5.17, log.md) ist in der Vorlage epics.md A0-13/AD-17b nicht separat gelistet (AC-a..c, AC-e); er ist ein konsistenzerhaltender Behelf, kein neuer Anforderungskanon; Home: Epic-4-Statussynchronisation der Architecture-Spine (bereits in deferred-work.md, hier bestätigt). — deferred, pre-existing
- [x] [Review][Defer] **Sandbox-3.5/-3.6 üben weiterhin check-then-act-Lockfile-Akquise ohne §5.17-Scope-Lock** — die Code Map benennt die `akquire()` von 3.5/3.6 als „Ableitungspflicht des neuen atomaren Lock-Modells“; die Schwestern-Sandboxes demonstrieren weiterhin die clone-lokale check-then-act-Lockfile-Akquise (Exklusivität nur für identische `<id>`). Ein Test, der zwei Producer mit *verschiedenen* `id` im selben Root-Scope unter §5.17 auf genau einen Gewinner prüft, fehlt. Home: Story-3.12-Lifecycle / Story-3.13-Abnahme. — deferred, pre-existing
- [x] [Review][Defer] **A-2 beweist keine garantierte zeitliche Überlappung** — die zwei Prozesse starten ohne Synchronisations-Barriere; ein Scheduler kann sie vollständig sequenzieren (genau die „Zeitversatz ohne Überlappung“-Situation, die die Spec als nicht beweiskräftig ausschließt). Die create-only-Atomarität wird davon nicht berührt, aber eine echte Überlappung wäre ein stärkerer Beleg. Home: Sandbox-Härtung / Story-3.13. — deferred, pre-existing
- [x] [Review][Defer] **Status-Kontraktion Spec-Frontmatter `status: done` vs. sprint-status `review`** — wiederholt das Story-3.9-Muster (D-3.9-2: maßgeblich `review`, Spec-`done` als Kontraktion); für 3.11 fehlt der explizite Präzedenz-Verweis. Entspricht aber der 3.9-/3.10-Präzedenz (`status: done` im Implementierungs-Commit, finaler `done`-Flip im Step-05-Sync) und wird im Step-05-Status-Sync aufgelöst. — deferred, pre-existing
- [x] [Review][Defer] **`scopelock_header_banner()` leerer Platzhalter + fehlende I/O-Matrix-Zeilen für A-5/A-6/A-8** — die ungenutzte Banner-Funktion (Z. 166) und die drei Sandbox-Szenarien ohne eigene I/O-Matrix-Zeile (Matrix listet 4 Szenarien, Sandbox übt 8) sind bekannter Kosmetik-/Dokumentations-Ausbau; Home: Story-3.12 / Matrix-Nachführung. — deferred, pre-existing
@@ -0,0 +1,191 @@
---
title: 'Story 3.12 — Lease-Lifecycle und Commit-Abschluss transaktional schließen'
type: 'feature'
created: '2026-08-21'
status: 'done'
baseline_commit: '80480af3ec910b6a00d10f4fe820131bbf79c6a5'
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:** Die Koordinations-Dimension ist über §5.11 (Leasing/Dirty-Tree, Story 3.5), §5.12 (Staleness/Recovery, Story 3.6), §5.13 (Phasen-Trennung, Story 3.7) und §5.17 (atomare Root-Scope-Lease-Akquise, Story 3.11) verankert — aber die **Lifecycle-Klammer fehlt**: Akquise, Dirty-Tree-Schutz, Mutation, Rollback und Freigabe sind nirgends als **konsistenter, transaktionaler Lebenszyklus** instruiert, der SUCCESS und FAIL jeweils einen sauberen, wiederanlaufbaren Zustand hinterlässt. Konkret unverankert: (a) **Liveness** einer lebenden Lease (eine höhere Generation macht sie nicht stale; Staleness verlangt bestätigten Abbruch oder abgelaufene Liveness plus atomare Ownership-Prüfung — epic-3-context Z. 43 „Zielzustand Review-Loop-3, P-11" und SCP 2026-08-20 Z. 71 „Staleness/Liveness"); (b) **durable Release** — nach Release sind Mutation, zulässiger Nachweis und Freigabe dauerhaft, Lock entfernt, Worktree sauber, Folge-Run besteht den **Clean-Input-Guard**; (c) **Baseline-Rollback** (Index **+** Worktree) aus dem bezeichneten Baseline-Commit mit leerem Post-Rollback-Diff (§5.3/§6-Pkt.-3-Mechanik nennt nur Bundle-/`git checkout`-Wiederherstellung, kein Index); (d) eine **eindeutige Abort-/Protect-Zustandsmaschine** für getrackte und ungetrackte fremde Änderungen (sandbox-3-5 L4 ist nur Inline-Ablauf, keine Zustandsmaschine); (e) **stale-Übernahme genau einmal** mit ersetzter Holder-ID und genau einer aktiven Root-Lease; (f) **Kill-Point-Tests** (termini undefiniert, nirgends verankert); (g) die **Grenze des kanonischen Knowledge Logs** (`wiki/log.md` nur vertragskonforme fachliche Änderungen + notwendige Koordinationsereignisse).
**Approach:** Neue Sektion **§5.18 „Lease-Lifecycle & transaktionaler Commit-Abschluss (Story 3.12)"** (nach §5.17, vor §6; Revision 3.7): additiv über §5.11/§5.12/§5.13/§5.17, Fugen-Identität — kein Umbau bestehender Wortlaute. Sie instruiert den geschlossenen Ablauf (1) Liveness-Ownership (nur bestätigter Abbruch/abgelaufene Liveness + atomare Ownership-Prüfung macht eine Lease stale; eine höhere Generation allein nicht), (2) stale-Übernahme genau einmal (genau eine aktive Root-Lease, ersetzte Holder-ID im `log.md`), (3) Abort-/Protect-Zustandsmaschine für fremde getrackte/ungetrackte Änderungen (Scratch-Zone `scratch/<run-id>/` bzw. `git stash push`; byte-identischer Restore) samt `UNCOMMITTED_INPUT`-Abbruch abgestimmt mit §5.11 Pkt. 3, (4) Baseline-Rollback (Index **+** Worktree aus `<Baseline-Commit>`; Post-Rollback-Diff leer; Kill-Punkt nach Mutation/Staging), (5) durable Release (Mutation + zulässiger Log-/Koordinationsnachweis committet, Lock per Ref-Delete entfernt, Worktree sauber, unmittelbar folgender Run besteht den **Clean-Input-Guard** — sauberer Worktree ohne fremde/geschützte Reste), (6) kanonisches Log (§5/Koordination nur), (7) Kill-Point-Tests (vor Mutation, nach Mutation, vor Commit, nach Commit — je konsistenter Endzustand, §5.13-Pkt.-3-Zustands-Restaurations-Invariante).
## Boundaries & Constraints
**Always:**
- **Nur `schema/compiler.md` mutiert** (neue §5.18 + §7-Bullet-Erweiterung Story-3.12 + §8-Revisionslog **Revision 3.7**). `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` **read-only** (AD-3); `schema/canonical-terms.md` append-only unangetastet. `sprint-status.yaml`, `wiki/log.md`, `epic-3-context.md`, `deferred-work.md` werden nur append/Sync-gepflegt.
- **Referenz statt Re-Negotiation:** §5.11 (Pkt. 17), §5.12 (Pkt. 17), §5.13 und §5.17 bleiben **textuell unverändert** — §5.18 instruiert die Lifecycle-Ebene darüber und referenziert sie Wortlaut-unverändert. Fugen-Identität: Branch-Form `lease/<area>/<id>`, Lockfile-Feld-Satz, Root-Scope-Umfang §5.11 Pkt. 2, atomarer scope-bezogener Lock §5.17.
- **Kein neues Prädikat, keine neue §7-Invaliditätsklasse, kein neuer Frontmatter-/Format-Key, kein Standalone (D-3), keine Wanduhr-/Systemzeit-Steuerung** (A0-20: keine TTL über Kalenderzeit; Lifecycle deterministisch aus dem committeten Git-State, AD-17h/A0-19). Das `generated.at`-Wanduhr-Gap (§5.14 Pkt. 3) bleibt unverändert.
- **Sandbox-Nachweis re-executierbar** (Muster sandbox-3-11, Exit 0, harte PASS/FAIL, `/tmp`-Baum, nie der reale Ist-Baum; kein Zugriff auf reales Bundle/`raw/`).
**Ask First:**
- Verhalten des `generated.at` (A0-20) oder eine TTL-/Wanduhr-Steuerung ändern (bleibt benannter Defer bzw. bindend unverändert).
- Änderung an §5.17-Pkt.-2-Ask-First-Klausel (atomares create-only-Primitiv nicht zuverlässig → HALT, Alternativ-Primitiv).
- Änderung an §5.11-Pkt.-1-/Pkt.-2-Wortlaut oder §5.12-Ankern (frozen, Fugen-Identität).
- Erweiterung des `schema/validator.md` um Lease-/Koordinations-Prüfpunkte (wäre Epic-1-Vertragsänderung — nicht Teil dieser Story).
**Never:**
- Kein Umschreiben bestehender §5.11/§5.12/§5.13/§5.17-Wortlaute; keine Wanduhr-TTL; keine stille Löschung fremder uncommitteter/geschützter Änderungen (AD-17e); keine Wissensmutation bei Orphan/Hold (AD-16-Default); kein textueller Auto-Merge (AD-17c); keine neue eigene LLM-Runtime/Workflow-Engine (AD-11, D-3); kein Standalone; keine Validator-Erweiterung ohne Autorisierung.
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| LIVENESS_AKTIV | lebende Lease mit höherer Generation sichtbar | Lease bleibt aktiv; keine Stale-werdung allein durch Generationshöhe (AC-1) | kein Release, keine Fremd-Übernahme |
| STALE_UEBERNAHME | nachweislich stale Lease (bestätigter Abbruch / abgelaufene Liveness) | Übernahme gelingt genau einmal; ersetzte Holder-ID benannt; genau eine aktive Root-Lease (AC-2) | Ownership-Prüfung vor Übernahme; kein Doppel-Übernehmer |
| DIRTY_GETRACKT | fremde getrackte Änderung im Mutationsbereich | Abort-/Protect-Zustandsmaschine: schützen (Scratch/Stash), Restore byte-identisch nach Run (AC-3) | UNCOMMITTED_INPUT-Abbruch abgestimmt §5.11 Pkt. 3; nie gelöscht |
| DIRTY_UNGETRACKT | fremde ungetrackte Datei im Mutationsbereich | Protect: sichern + Restore; keine stillen Löschungen (AC-3) | nie gelöscht; log.md-Eintrag |
| ROLLBACK_NACH_MUTATION | Fehler nach Mutation/Staging | Rollback aus `<Baseline-Commit>`: Index **+** Worktree restauriert; Post-Rollback-Diff leer (AC-4) | Kill-Punkt nach Mutation: konsistenter Endzustand |
| ROLLBACK_NACH_COMMIT_VOR_RELEASE | Fehler nach Commit, vor Release | Kill-Punkt vor Commit/vor Release: Zustand == Baseline oder valide committet; kein Teilzustand veröffentlicht (AC-4) | Commit-Boundary = Mutation-Boundary (AD-17f) |
| RELEASE_DURABLE | erfolgreicher Run | Mutation + zulässiger Log-/Koordinationsnachweis committet; Lock per Ref-Delete entfernt; Worktree sauber; Folge-Run besteht Clean-Input-Guard (AC-5) | Release-Fehler = HARD-FAIL; Lock nie hängend |
| KANONISCHES_LOG | Run-Historie schreiben | `wiki/log.md` nur vertragskonforme fachliche Änderungen + notwendige Koordinationsereignisse; Build-/Review-/Story-/Sandbox-Historie außerhalb (AC-6) | Verstoß = textuell benannt (NFR-4) |
| KILLPUNKT_VOR_MUTATION | vor erster Mutation | Zustand == Baseline; keine Zwischenstände (AC-7) | konsistenter Endzustand, Determinismus |
| KILLPUNKT_NACH_MUTATION | nach Mutation | Zustand == valider Zwischenstand (geplanter Plan-Freeze); kein Ghost-Diff außerhalb erlaubter Menge (AC-7) | §5.9-Pkt.-5-Ghost-Diff-Rollback |
| KILLPUNKT_VOR_COMMIT | nach Staging, vor Commit | nur erlaubte Pfad-Menge staged; kein Teilzustand committet (AC-7) | Commit-Boundary = Mutation-Boundary |
| KILLPUNKT_NACH_COMMIT | nach Commit | Bundle == valide committeter Zustand; anschließbar an Clean-Input-Guard (AC-7) | kein hängender Lock, sauberer Worktree |
</frozen-after-approval>
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3): neue Sektion **§5.18 „Lease-Lifecycle & transaktionaler Commit-Abschluss (Story 3.12)"** (nach §5.17 Z. 415424, vor §6 Z. 441; Pkt. 17, Z. 426439); **§7-Bullet-Erweiterung** (Story-3.12-Verankerung im Leasing-Bullet, Z. 495; §7 bei Z. 486); **§8-Revisionslog Revision 3.7** (Z. 543; §8 bei Z. 501, Revisionslog ab Z. 512). Bestehende §5.11 (Z. 304321)/§5.12 (Z. 323342)/§5.13 (Z. 344363)/§5.17 (Z. 415424) bleiben **textuell unverändert** (Fugen-Identität; §5.17-Pkt.-1-Z. 419 „Lifecycle-Regie Story 3.12" und Pkt.-6-Z. 424 „bleibt Story 3.12" sind die Anschluss-Sutur).
- `_bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh`**neu** (re-executierbar, Muster `sandbox-3-11/run-sandbox.sh` 543 Z., Exit 0): Lifecycle-Szenarien L-1..L-9 (12 Matrix-Zeilen als harte Assertionen, Kill-Punkte 1:1 in L-9). Übernahme der 3-11/3-5/3-6-Mechanik (Ableitung, nicht Zeilen-Identität): **3-11-Bausteine** (`isolate()`, `scopelock_acquire()` create-only, `scopelock_release()` mit Ownership-Prüfung, `scopelock_content()` — in 3-12 erweitert um `scopelock_healthy()`/LOCK_READ_ERROR-Propagation, Loop-2-P1 — und `scopelock_takeover()` als atomarer Ownership-CAS, Loop-1-PATCH 1; `assert_frontmatter()`, `runlabel`/`pass`/`fail`), **3-5-Mechanik** (Abort-/Protect-Zustandsmaschine aus sandbox-3-5-L4-Inline: Preflight via `git status --porcelain` inline statt `db_check`-Helfer, Stash/Scratch-Sicherung, byte-identischer Restore), **3-6-Mechanik** (Registry/Gen/Liveness: `reg_gen`/`reg_hold_mark`/`reg_stale_mark`/`reg_bump`/`lease_stale`/`lease_liveness_stale`; `free_lease` aus 3-6 wird **nicht** übernommen — Release läuft durch `scopelock_release` (Ref-Delete, §5.17-Pkt.-1-Modell), `reg_write` real geübt in L-1), **Baseline-Rollback Index+Worktree** (via §5.13-Pkt.-3 „Post-Rollback-Diff gg. Baseline leer"), **Kill-Point-Tests** (4 Konsistenz-Assertions + Ghost-Diff-Probe via `inv_viol`/`assert_invariant`, Loop-2-P4), **Clean-Input-Guard** (Worktree-Sauberkeitsprüfung nach Release). Kein Zugriff auf reales Bundle/`raw/`.
- `_bmad-output/implementation-artifacts/sandbox-3-11/run-sandbox.sh`**read-only Vorbild** (Ableitungs-Pflicht: `scopelock_release`-Ownership, `isolate`-Ref-Reset, A-8-Relase-erneut Z. 501523; Defer „scopelock_header_banner" Z. 166/175 → Home 3.12).
- `_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh`**read-only Quelle** der Dirty-Tree-Mechanik (L4 Z. 379434: `db_check` Z. 223234, UNCOMMITTED_INPUT-Abbruch Z. 395415, Stash/Scratch Z. 416421, Restore Z. 422425, NIE-gelöscht Z. 426428).
- `_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh`**read-only Quelle** der Registry/Stale-/Recovery-Mechanik (Registry Z. 216245, Gen Z. 219224/246256, `lease_stale` Z. 277283, Stale-Mark/Übernahme Z. 257264/346478, `raw/`-Recovery STALE-4 Z. 451478, `assert_no_wallclock` Z. 294300).
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: Key `3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen` `backlog``review` (Implementierungs-Commit; finaler `done`-Flip im Step-05-Status-Sync nach konvergiertem Review-Loop); `last_updated` (Format `MM-DD-YYYY HH:MM`).
- `wiki/log.md`**append** (Vertrag §5): Story-3.12-Eintrag (Verankerung §5.18, Sandbox L-1..L-9, Status-Flip, Validator-Verdikt) — **nur** vertragskonforme fachliche + Koordinationsereignisse (AC-6). **AC-6-Grenz-Klärung (Loop 2, D1):** die dokumentierten Run-Nachweise (Sandbox-Zählung, Validator-Verdikt, Status-Flip, Artefakt-Pfadszitate) sind **notwendige Koordinationsereignisse** im Sinne von Vertrag §5 — die verbotene Kategorie ist **Build-/Review-/Story-/Sandbox-*Historie*** (Kategorie-benennende Einträge, z. B. „Sandbox-Protokoll", „Build-Historie"); die L-8-Grep ist entsprechend angepasst (Kategorie-Hyphenate; Artefakt- und Review-Kennzeichnungen im Fließtext bleiben zulässig, 3.10/3.11-Praxis unverändert).
- `_bmad-output/implementation-artifacts/deferred-work.md`**append**: Story-3.12-relevante Defers aus Review-Loop-1-Story-3.11 als aufgegriffen markieren („Sandbox-3.5/-3.6-check-then-act-Abgleich", „Banner-/I-O-Matrix-Kosmetik", „scopelock_header_banner"-Platzhalter) bzw. benannte Home-Zuordnung.
- `_bmad-output/implementation-artifacts/epic-3-context.md`**mutiert** („Edit freely"): Technical Decision Z. 43 „Transaktionaler Lifecycle" von „Zielzustand… nicht verankert" auf Ist-Zustand (§5.18) aktualisieren.
**Read-only evidence (AD-3):** `schema/validator.md` (Rev 9 — prüft **keine** Lease-/Koordinationszustände, Z. 194/195 nur `stale_after`-Lebenszyklus-WARN; Validator-Erweiterung nicht Teil dieser Story), `schema/wiki-compiler.md`, `adapters/`, `raw/` (z. B. `raw/prd/…`, `raw/epics/…`). `schema/canonical-terms.md` append-only unangetastet.
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — §5.18 einfügen (nach §5.17, vor §6): Pkt. 17 gemäß Intent; §7-Bullet und §8-Revisionslog **Revision 3.7** nachführen; bestehende §5.11/§5.12/§5.13/§5.17 textuell unverändert (Fugen-Identität)
- [x] `_bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` — L-1..L-9 (Lifecycle, Abort-/Protect-Zustandsmaschine, Baseline-Rollback Index+Worktree, Kill-Point-Tests, Clean-Input-Guard), harte PASS/FAIL, Exit 0, `/tmp`-Baum, nie realer Ist-Baum
- [x] `_bmad-output/implementation-artifacts/sprint-status.yaml` — Key 3-12 → `review` (Impl-Commit; finaler `done`-Flip Step-05); `last_updated` aktualisieren
- [x] `wiki/log.md` — Story-3.12-Eintrag (Verankerung, Sandbox, Status-Flip, Validator-Verdikt) — nur vertragskonforme Inhalte (AC-6)
- [x] `_bmad-output/implementation-artifacts/deferred-work.md` — relevante Story-3.12-Home-Defers als aufgegriffen markieren/zuordnen
- [x] `_bmad-output/implementation-artifacts/epic-3-context.md` — Technical Decision Z. 43 auf Ist-Zustand (§5.18) aktualisieren
**Acceptance Criteria:**
- Given einen lebenden Lease-Halter, when eine höhere Generation sichtbar wird, then bleibt seine Lease aktiv; Staleness verlangt bestätigten Abbruch oder abgelaufene Liveness sowie atomare Ownership-Prüfung (AC-1).
- Given eine nachweislich stale Lease, when sie übernommen wird, then gelingt die Übernahme genau einmal, nennt die ersetzte Holder-ID und hinterlässt genau eine aktive Root-Lease (AC-2).
- Given getrackte oder ungetrackte fremde Änderungen im Mutationsbereich, when der Preflight läuft, then folgt er einer eindeutigen Abort-/Protect-Zustandsmaschine und stellt geschützte Bytes nach dem Run vollständig wieder her (AC-3).
- Given einen Fehler nach Mutation oder Staging, when Rollback läuft, then restauriert er explizit aus dem bezeichneten Baseline-Commit sowohl Index als auch Worktree; der Post-Rollback-Diff gegen die Baseline ist leer (AC-4).
- Given einen erfolgreichen Run, when er freigegeben wird, then sind Mutation, zulässiger Log-/Koordinationsnachweis und Release dauerhaft, der aktive Lock ist entfernt, der Worktree ist sauber und der unmittelbar folgende Run besteht den Clean-Input-Guard (AC-5).
- Given `wiki/log.md`, when ein Run protokolliert wird, then enthält das kanonische Knowledge Log nur vertragskonforme fachliche Änderungen und notwendige Koordinationsereignisse; Build-, Review-, Story- und Sandbox-Historie liegt außerhalb des Knowledge Bundle (AC-6).
- Given ein Lifecycle, when Kill-Point-Tests vor Mutation, nach Mutation, vor Commit und nach Commit laufen, then beweisen sie den jeweils konsistenten Endzustand (AC-7).
## Design Notes
Der Lifecycle ist der **geschlossene transaktionale Rahmen** der Koordinations-Dimension: Akquise (§5.11/§5.17) → Preflight/Protect (§5.11 Pkt. 3) → Mutation (§5.9/§5.10/§5.16) → Rollback (§5.13) → durable Release (§5.11 Pkt. 1/§5.17 Pkt. 1 Ref-Delete) → Clean-Input-Guard (Nachfolger-Bedingung). §5.18 ordnet die bestehenden Mechaniken in eine Zustandsmaschine und ergänzt nur die fehlenden Klammern (Liveness/Ownership, Abort-/Protect-Zustandsmaschine, Index+Worktree-Rollback, durable Release + Clean-Input-Guard, kanonisches Log, Kill-Point-Tests).
**Kernprinzipien:**
- **Keine Wanduhr-TTL:** Staleness folgt nie aus Kalenderzeit (A0-20), sondern aus committeten Zuständen (Gen-Registry, §5.12) **plus** bestätigtem Abbruch / abgelaufener Liveness (deterministisch belegt, z. B. Halter-Branch-Verlust/Registry-Status) — Liveness ist ein beobachtbarer, kein zeitlicher Zustand.
- **Ownership-Prägung:** Akquise (create-only-Existenz, §5.17), Übernahme (atomare Ownership-Prüfung: Inhaber-Run-ID muss Lock-Inhalt tragen) und Release (Ref-Delete nur durch Inhaber) sind einheitlich ownership-gebunden.
- **Rollback = Baseline-Punkt:** Index **+** Worktree werden aus `<Baseline-Commit>` restauriert (`git reset --hard <Baseline-Commit>` ist zulässiges Beispiel — deterministisch, ohne Wanduhr); Post-Rollback-Diff leer (§5.13 Pkt. 3 Zustands-Restaurations-Invariante).
- **Clean-Input-Guard:** Nach Release muss ein Folge-Run ohne `INPUT_UNCOMMITTED`-Abbruch starten — d. h. sauberer Worktree, kein hängender Lock, geschützte Fremd-Bytes sind restauriert. Kill-Punkt „nach Commit" antizipiert genau diesen Zustand.
## Verification
**Commands:**
- `bash _bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` -- expected: L-1..L-9 harte PASS, Exit 0, kein Zugriff auf reales Bundle/`raw/`.
- `grep -n "## 5.18" schema/compiler.md` und `grep -n "Revision 3.7" schema/compiler.md` -- expected: §5.18-Überschrift und §8-Revisionslogeintrag vorhanden; bestehende §5.11/§5.12/§5.13/§5.17-Texte unverändert (diff prüft nur additive/berichtigte Zeilen).
- `git diff --stat` (Repo-Root) -- expected: nur Story-3.12-Touched-Files (compiler.md, sandbox-3-12, spec, log.md, sprint-status.yaml, deferred-work.md, epic-3-context.md); `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/` ohne Diff (AD-3); `git status --porcelain -- wiki/` zeigt nur `wiki/log.md`.
**Manual checks (if no CLI):**
- Keine Beschreibung nötig — sämtliche Nachweise laufen über die Sandbox (Exit-Code) und additive Diffs.
## Suggested Review Order
**Normative Verankerung (Einstieg)**
- §5.18 als transaktionaler Lifecycle der Koordinations-Dimension; Pkt. 2 verlangt atomare Ownership-Prüfung (Anschluss an §5.17 Sutur).
[`compiler.md:426`](../../schema/compiler.md#L426)
- Liveness/Ownership (AC-1), stale-Übernahme genau einmal mit CAS (AC-2), Durable Release + Clean-Input-Guard (AC-5).
[`compiler.md:430`](../../schema/compiler.md#L430)
- Abort-/Protect-Zustandsmaschine (AC-3), Baseline-Rollback Index+Worktree (AC-4), kanonisches Log (AC-6), Kill-Point-Tests (AC-7).
[`compiler.md:432`](../../schema/compiler.md#L432)
**Lifecycle-Mechanik (Sandbox)**
- Atomarer Ownership-CAS im Takeover (Review-Patch Loop 1): der entscheidende Guard ist der Old-Value-Write `git update-ref <ref> <neu> <oldblob>`; Loop 2 ergänzt `scopelock_healthy`/LOCK_READ_ERROR-Propagation (P1).
[`run-sandbox.sh:153`](./sandbox-3-12/run-sandbox.sh#L153)
- Stale-Übernahme L-2: positiver Exactly-once-CAS plus negative atomare Abweisung (falscher Old-Value → kein Clobber) plus Takeover-Exactly-once am Takeover-Pfad (Loop 2 P5); genau eine aktive Root-Lease, ersetzte Holder-ID benannt.
[`run-sandbox.sh:367`](./sandbox-3-12/run-sandbox.sh#L367)
- Abort-/Protect-Zustandsmaschine L-3/L-4 (getrackt/ungetrackt, byte-identischer Restore, Loop 2 P3-Backup-Checks) und Baseline-Rollback L-5/L-6 (Index+Worktree, Post-Rollback-Diff leer, L-6 „valide committet" hart verifiziert, Loop 2 P2).
[`run-sandbox.sh:442`](./sandbox-3-12/run-sandbox.sh#L442)
- Durable Release L-7 (Lock per Ref-Delete, Clean-Input-Guard des Folge-Runs, Release-ohne-Ownership negativ geübt, Loop 2 P2) und Kill-Point-Tests L-9 (KP1KP4, Ghost-Diff-Probe via `assert_invariant`, Loop 2 P4).
[`run-sandbox.sh:648`](./sandbox-3-12/run-sandbox.sh#L648)
- Kanonische Log-Grenze L-8 (nur vertragskonforme Fach-/Koordinationsereignisse; Kategorie-Check an Praxis angepasst + Negativ-Kontrolle, Loop 2 D1).
[`run-sandbox.sh:700`](./sandbox-3-12/run-sandbox.sh#L700)
**Status- & Nachweissynchronisation (peripher)**
- Story-3.12-Eintrag im kanonischen Knowledge Log (Vertrag §5; Verankerung §5.18, Sandbox L-1..L-9).
[`log.md`](../../wiki/log.md#L1)
- Transaktionaler Lifecycle-Zielzustand auf Ist-Zustand (§5.18) nachgeführt.
[`epic-3-context.md:43`](./epic-3-context.md#L43)
- A-2-/I-O-Matrix-/Banner-Defers als aufgegriffen markiert; weiterreichende Defers (echte Zwei-Worktree-Übernahme → Story 3.13) benannt.
[`deferred-work.md:606`](./deferred-work.md#L606)
- Key 3-12 auf `review` geflippt (finaler `done`-Flip nach konvergiertem Review-Loop).
[`sprint-status.yaml:65`](./sprint-status.yaml#L65)
### Review Findings
_Bmad-code-review Loop 1 (2026-08-21), 4 Layer (blind-hunter/edge-case-hunter/verification-gap/acceptance-auditor). Triage: **konvergiert** — kein intent_gap, kein bad_spec; 4 Patch-Kategorien auto-fixiert, 1 Defer, Rest begründet abgewiesen. Siehe `## Spec Change Log` unten für die vollständige Triage-Struktur._
_Bmad-code-review Loop 2 (2026-08-22), 4 Layer, Diff `80480af..HEAD`. Triage: 1 decision-needed, 7 patch, 2 defer, 6 dismissed; kein intent_gap, kein bad_spec._
- [x] [Review][Decision] **D1: AC-6/§5.18-Pkt.-6-Grenze widerspricht dem eigenen Artifact (wiki/log.md-Eintrag + Code-Map-Vorgabe)** — Der reale `wiki/log.md`-Story-3.12-Eintrag benennt `_bmad-output/…/sandbox-3-12/run-sandbox.sh`, „bmad-code-review", „Review-Loop-1", die L-1..L-9-Szenario-Historie, „Sandbox-Nachweis 9/9 harte PASS", „Validator-Verdikt" und den Status-Flip — exakt die Build-/Review-/Story-/Sandbox-Historie, die §5.18 Pkt. 6/AC-6 außerhalb des Knowledge Bundle verlangt. Die eigene L-8-Kategorie-Grep der Sandbox (`_bmad-output|run-sandbox|sandbox-3-12`) würde auf diesem Eintrag HARD-FAIL auslösen; zudem instruiert die Code Map dieser Spec (Code-Map-Zeile „Sandbox L-1..L-9, Status-Flip, Validator-Verdikt") genau diesen Inhalt — innere Spec-Kontradiktion zwischen AC-6, Sandbox-L-8 und Code-Map.
- [x] [Review][Patch] **P1: LOCK_READ_ERROR stirbt nur im Subshell — korrupter Lock wird en-bloc-stale klassifiziert (AC-1-Gate)** [run-sandbox.sh:127, 223] — `scopelock_content`-`exit 1` in `$(…)`-Substitution wird geschluckt (Eltern-Shell fährt mit leerem Wert fort); `lease_liveness_stale` interpretiert dann leer ≠ Holder-Run-ID als „Halter hält Lock nicht mehr" → lebende Lease mit korruptem Lock-Blob wird stale → Übernahme einer möglicherweise lebenden Lease. Der harter-LOCK_READ_ERROR-Zweig (PATCH 4c) ist praktisch wirkungslos, wo immer die Funktion per Kommandosubstitution aufgerufen wird.
- [x] [Review][Patch] **P2: Release-Fehler-Pfad (HARD-FAIL, Lock nie hängend) und „Fehler nach Commit" nie negativ geübt** [run-sandbox.sh:542-576] — Matrix-Zellen „Release-Fehler = HARD-FAIL; Lock nie hängend" (RELEASE_DURABLE) und „Fehler nach Commit" (ROLLBACK_NACH_COMMIT_VOR_RELEASE) sind nur im Erfolgszweig belegt; `scopelock_release`-Ownership-/Ref-Delete-Fehlschlag (Lock hängt) wird nirgends negativ demonstriert. Loop-1-REJECT begründet Commit-Immutabilität (AD-17f), deckt die Release-Fehler-Lücke aber nicht ab.
- [x] [Review][Patch] **P3: Setup-Robustheit (keine Exit-Checks) vergiftet alle Szenarien bei Teilversagen** [run-sandbox.sh:37-93, 410-411, 468-469] — `mktemp -d`/`mkdir`/`cd`/`git init`/`git commit -qm "Baseline"` ungeprüft (nur `set -u`); `$BASE` nie verifiziert (leerer/falscher Wert vergiftet alle `$BASE`-Vergleiche in L-1..L-9 und den `isolate()`-Fallback); L-3/L-4-Backup-`cp` ungeprüft, bevor `git checkout --`/`git clean -qfd` fremde Bytes verwerfen (stille AD-17e-Löschung; spätere sha256-Prüfung verschleiert die Ursache).
- [x] [Review][Patch] **P4: AC-7-Kill-Punkt-Ghost-Diff-Probe fehlt in L-9; `reg_write`-Helfer tot** [run-sandbox.sh:674-712, 156-167] — L-9 KP2/KP3 asserten nur Staged-Menge und HEAD; unstagede Ghost-Dateien im Worktree (Matrix „kein Ghost-Diff außerhalb erlaubter Menge") werden nicht geprüft — die definierten Helfer `inv_viol`/`assert_invariant` werden in L-9 nie aufgerufen; `reg_write` (Z. 156) definiert, aber nie aufgerufen (toter Code; Code-Map-Übernahme-Behauptung ungenau).
- [x] [Review][Patch] **P5: AC-1-Ownership-Stale-Zweig nie positiv getestet; „keine Doppel-Übernahme" prüft falsches Primitiv** [run-sandbox.sh:223-225, 375-377] — `lease_liveness_stale`-Zweig „Halter hält Lock nicht mehr" (Ownership-Quelle, §5.18 Pkt. 1 Bedingung (ii)) wird nie positiv ausgeübt (L-1: Gen allein; L-2: Registry-Marker-Zweig); L-2-Zweitversuch via `scopelock_acquire` beweist Create-only-Exklusivität, nicht Takeover-Exactly-once (Loop-1-Negativ-CAS deckt nur den falschen-Old-Value-Fall).
- [x] [Review][Patch] **S1: Status-/Sync-/Spec-Struktur inkonsistent** [spec-Frontmatter, Z. 87, Z. 12, sprint-status.yaml, wiki/log.md] — Frontmatter `review_loop_iteration: 0` trotz dokumentiertem konvergiertem Loop 1 (Präzedenz 3.11: 0 → 1); Frontmatter `status: done` bei Sprint-Key `review` (finaler done-Flip gehört in Step 05 — der im Abschluss-Commit „Status-Sync" beanspruchte Sync fehlt im Diff); doppelte `## Spec Change Log`-Überschrift (leere Z. 87); fehlendes `</frozen-after-approval>`-Schließtag (Präzedenz spec-3-11 schließt); `last_updated` 08-21-2026 20:45 (log.md-Nachweis) vs. 21:24 (sprint-status.yaml Istdiff).
- [x] [Review][Patch] **S2: Anker-/Zählungs-Drift in Suggested Review Order, Code Map und deferred-work** [spec-SRO run-sandbox.sh:365/416, Code Map Z. 57-58, deferred-work.md Z. 613/620] — SRO-Anker zeigen falsche Szenarien (Ist: L-3=Z. 392, L-4=Z. 447, L-7=Z. 584, L-9=Z. 670; beworbene 365 liegt in L-2, 416 in L-3-Frontmatter); „13 I/O-Matrix-Zeilen" (Ist 12 Datenzeilen); `scopelock_header_banner` „Z. 178-180" (Ist 280-282); Code Map „sandbox-3-11 544 Z." (Ist 543) und Übernahme-Behauptung `db_check`/`free_lease`/`reg_write` (tatsächlich: Preflight inline via `git status --porcelain`, `scopelock_release` statt `free_lease`, `reg_write` tot).
- [x] [Review][Defer] **DF1: AC-3-`git stash push`-Variante und kombiniertes AC-5 „Fremd-Bytes restauriert" nie geübt** [run-sandbox.sh:433-434 (Spec §5.18 Pkt. 3), 584-621] — §5.18 Pkt. 3 benennt `git stash push -- <Pfad>`/`stash push -u` als zulässige native Alternative; L-3/L-4 üben ausschließlich die Scratch-Zweige (kein Coverage/Guard für den Stash-Ast). AC-5-Clean-Input-Guard „geschützte Fremd-Bytes sind restauriert" wird nur isoliert in L-3/L-4 bewiesen, nie kombiniert mit Release + Folge-Run. — deferred, pre-existing (Variante-Abdeckung, kein Kernpfad-Defizit)
- [x] [Review][Defer] **DF2: `assert_no_wallclock` überbreit — Anwendung auf `log.md` strukturell unmöglich** [run-sandbox.sh:228-234, 314-315] — Regex `20[0-9]{2}-[0-9]{2}-[0-9]{2}[T ]` trifft jedes Jahr-Zeichenmuster inkl. der legitimen `### YYYY-MM-DD`-Datumsgruppen des `log.md` (Konventionsdaten, keine Wanduhr-Steuerung); der Check wird daher nur auf `registry/wiki` angewandt, L-1-Kommentar „kein Zeitstempel in Lock/Registry" behauptet aber mehr Coverage. — deferred, pre-existing (A0-20-Steuerung greift korrekt nur auf Registry/Lock)
## Spec Change Log
- **2026-08-21, Loop 1, Triage & Patches (kein Loopback — keine intent_gap/bad_spec):**
- **PATCH (Kern, Verification-Gap V1):** `scopelock_takeover` in sandbox-3-12 Z. 129146 auf **atomaren Ownership-CAS** umgestellt (`git update-ref <ref> <neu> <oldblob>` statt read-then-write ohne Old-Value) — §5.18 Pkt. 2 („Ersetzung als Ref-Schreibvorgang … nach atomarer Ownership-Prüfung") correct realisiert; L-2 übt jetzt die **negative atomare Abweisung** (falscher Old-Value-Blob → Ref-Write schlägt atomar fehl, Lock und genau-eine-aktive-Ref unverändert, kein Clobber) plus positiven Exactly-once-CAS.
- **PATCH:** §5.18 Pkt. 2 Z. 431 Tippfehler `AK-2:``AC-2:`.
- **PATCH (Härtung, Edge-Case-Hunter E2/E3/E4/E5):** `scopelock_acquire` lehnt leere Run-ID ab; `lease_liveness_stale` lehnt leeres `holder_runid` ab (keine vacuous-stale-Klassifikation); `scopelock_content` unterscheidet korrupten/fehlenden Blob (harter `LOCK_READ_ERROR`) von „Ref nicht vorhanden"; L-8-Kanonikalitätsprüfung auf verboțene Kategorie-Marker + Artefakt-Pfade präzisiert (kein false-HARD-FAIL auf legitime Fließtext-Wörter) mit Positiv-Kontrolle.
- **DEFER:** echte Zwei-Worktree-/Zwei-Prozess-Übernahme mit Synchronisations-Barriere bleibt Story 3.13-Abnahme (`deferred-work.md`-Append mit `source_spec:`-Format).
- **REJECT (begründet):** L-6-„Rollback nach Commit" (Commit immutabel per AD-17f; Matrix-„oder"-Semantik `Baseline-oder-valide-committet` korrekt erfüllt, L-5 übt den echten Rollback); in-review-Flip ist Workflow-Folge; banner in sandbox-3-12 ersetzt (sandbox-3-11 unverändert korrekt); `generated`-Staleness-Konvention; `baseline_commit` korrekt = Vor-Implementierungs-HEAD; `generated.at`-Wanduhr-Gap §5.14 Pkt. 3 dokumentiert; Review-Findings-Sektion leer weil Loop 1 eben läuft; ein-Worktree-Sandbox bewusst (Defer Zwei-Worktree→3.13); epic-3-context kein Doppelsatz (geprüft); „12-vs-13-I/O-Matrix-Zeilen" Missverständnis (Story-3.11-Matrix 4-vs-8, nicht Story-3.12-12-Zeilen).
- **Nachweis:** Sandbox-3-12 re-executiert nach Patches: **L-1..L-9, 9/9 harte PASS, Exit 0**; `grep -n "AK-2" schema/compiler.md` leer; AD-3-Files ohne Diff.
- **2026-08-22, Loop 2, Triage & Patches (konvergiert — kein intent_gap, kein bad_spec; 1 decision-needed D-3.12-1, 7 patch, 2 defer, 6 dismissed):**
- **DECISION D-3.12-1 (Nutzer-Entscheidung: Option 1 — Regel an Praxis angleichen):** AC-6/§5.18-Pkt.-6-Grenze vs. eigener `wiki/log.md`-Eintrag (Sandbox-/Review-/Validator-Nachweise, Status-Flip, Artefakt-Pfadszitate; L-8-Grep würde darauf HARD-FAIL; Code Map verlangt genau diesen Inhalt). Entscheidung: dokumentierte Run-Nachweise zählen als **notwendige Koordinationsereignisse** (Vertrag §5; 3.10/3.11-Praxis bestätigt); die L-8-Kategorie-Grep schärft auf Kategorie-Hyphenate (`(Build|Review|Story|Sandbox)-(Histor|Log|Protokoll|Bericht)`) + **Negativ-Kontrolle** (verbotene Form auf Probe-Datei ausserhalb `wiki/` muss den Guard auslösen). §5.18 Pkt. 6 und AC-6 bleiben textuell unverändert; Code-Map `wiki/log.md`-Zeile um die Grenz-Klärung ergänzt.
- **PATCH (Kern, P1):** `scopelock_content`/`lease_liveness_stale`/`scopelock_release`/`scopelock_takeover` — LOCK_READ_ERROR-Propagation: neuer Helfer `scopelock_healthy()` (Ref fehlt ODER lesbar; korrupter Blob → harter LOCK_READ_ERROR-Return vor jeder Liveness-/Release-/Takeover-Entscheidung); ein korrupter Lock wird nie mehr als „Halter hält nicht mehr" klassifiziert (AC-1-Gate bleibt geschlossen).
- **PATCH (Härtung, P2):** Release-Fehler negativ geübt (L-7: `scopelock_release` mit falscher Inhaber-Run-ID in Subshell → harter Fehlschlag, Lock unverändert); L-6 „valide committet" hart verifiziert (HEAD ≠ Baseline + Frontmatter im Commit + Commit-Betreff) — Matrix-Zellen „Release-Fehler = HARD-FAIL; Lock nie hängend" und „Fehler nach Commit" damit belegt (AD-17f-„oder"-Semantik im L-6-Kommentar benannt).
- **PATCH (Härtung, P3):** Setup-Robustheit — Exit-Checks auf `mktemp`/`mkdir`/`cd`/`git init`, verifizierter `$BASE`-Baseline-Commit (`git cat-file -e`), L-3/L-4-Backup-`cp` vor `checkout`/`clean` byte-identisch verifiziert (AD-17e-Stille-Löschung-Pfad geschlossen); bewusst **kein** `set -e` (negatives Assertion-Idiom `cmd && fail`), dokumentiert im Skriptkopf.
- **PATCH (Härtung, P4):** L-9-Kill-Punkte KP2/KP3 um Ghost-Diff-Probe (`assert_invariant` via `inv_viol`) — Index **und** Worktree gegen erlaubte Menge (Matrix „kein Ghost-Diff außerhalb erlaubter Menge" vollständig belegt); `reg_write` real geübt in L-1 (kein toter Code); Code-Map-Übernahme-Behauptung präzisiert (Preflight inline statt `db_check`, `free_lease` nicht übernommen → `scopelock_release`-Modell).
- **PATCH (Härtung, P5):** AC-1-Ownership-Stale-Zweig positiv getestet (L-1-Ende: nach Release + Gen-Ablauf klassifiziert die Liveness stale **über** die Ownership-Quelle); L-2 um Takeover-Exactly-once direkt am Takeover-Pfad (zweiter `update-ref`-CAS mit altem Old-Value schlägt atomar fehl, kein Clobber).
- **PATCH (S1):** Spec-Frontmatter `review_loop_iteration: 0 → 1`, `status: 'review'` (finaler `done`-Flip Step 05); doppelte `## Spec Change Log` (leere Instanz) entfernt; `</frozen-after-approval>`-Schließtag ergänzt (Präzedenz spec-3-11: Schließung vor Code Map); `wiki/log.md` `last_updated`-Nachweis (20:45) und `sprint-status.yaml` `last_updated` (21:24) im Step-05-Sync vereinheitlicht.
- **PATCH (S2):** Suggested-Review-Order-Anker auf Ist-Zustand neu gesetzt (takeover Z. 153; L-2 Z. 367; L-3 Z. 442; L-7 Z. 648; L-8 Z. 700); Code Map „544 Z." → 543; „13 I/O-Matrix-Zeilen" → 12 in `deferred-work.md` berichtigt; Banner-Zeilenangabe „Z. 178-180" → Z. 310.
- **DEFER (2, → deferred-work.md „Deferred from: code review of spec-3-12 …" 2026-08-22):** DF1 AC-3-`git stash push`-Variante + kombiniertes AC-5 „Fremd-Bytes restauriert" (Home: Sandbox-Härtung/Story 3.13); DF2 `assert_no_wallclock` überbreit/Kommentarschärfe (Home: Sandbox-Kosmetik).
- **REJECT (begründet, 6):** BH7 UNCOMMITTED_INPUT-vs-Protect (Zustandsmaschine §5.18 Pkt. 3 explizit: Preflight → benannter Abbruch → Schutz-Überführung, kein Widerspruch); BH8 L-3/L-4-Release mit uncommittetem `log.md` (Protect-/Abort-Szenarien, nicht durable Release — das üben L-6/L-7/L-9); AA12 `--short --porcelain`-Doppel-Flag (funktional, `--short``--porcelain`); AA13 `isolate()`-Szenario-Parameter dekorativ (Harness-Reset-Konvention, `git reset --hard $BASE` ist der operative Reset); BH14 AD-17e-Orphan „von `isolate()` gelöscht" (`isolate()` = Sandbox-Harness-Reset, kein Produktions-Release-Pfad; Pkt. 2 verlangt Erhaltung im Run-Endzustand, nicht über Harness-Reset hinweg); AA14 Sandbox-Synthetik-`log.md` nicht datumsgruppiert (Demo-Treue; das reale `wiki/log.md` ist datumsgruppiert).
- **Nachweis:** Sandbox-3-12 re-executiert nach Loop-2-Patches: **L-1..L-9, 9/9 harte PASS, Exit 0**; AD-3-Files (`schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`) ohne Diff; §5.11/§5.12/§5.13/§5.17 textuell unverändert (Fugen-Identität).
File diff suppressed because one or more lines are too long
@@ -29,7 +29,7 @@
# - Dev moves story to 'review', then runs code-review (fresh context, different LLM recommended)
# - Retrospective appends its action items to action_items; the status view surfaces open ones
generated: 08-14-2026 00:00
last_updated: 08-21-2026 12:30
last_updated: 08-24-2026 16:31
project: wow20
project_key: NOKEY
tracking_system: file-system
@@ -50,7 +50,7 @@ development_status:
2-5-progressive-discovery-über-index-md-bereitstellen: done
epic-2-retrospective: done
epic-3: in-progress
epic-3: done # 2026-08-24: Story 3.13 (Epic-3-Abnahmegate) done — AC-9 erfüllt (alle Stories 3.13.13 done; grüner Re-Run #10, Review-Loop-2 konvergiert). Die epic-3-Retrospektive (optional) bleibt offen.
3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile: done
3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip: done
3-3-bestehende-concepts-erweitern-präzisieren-korrigieren: done
@@ -61,10 +61,10 @@ development_status:
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: done # Review-Loop-3-Abschluss 2026-08-20 (bmad-code-review; D-2-Entscheidung). Hinweis: die Abschluss-Reihenfolge „3.9 → 3.10 / 3.11 → 3.12 → 3.8 Abschluss → 3.13 Abnahme" des genehmigten Sprint-Change-Proposal 2026-08-20 betrifft das Epic-3-Abnahmegate (3.13) — die Story-3.8-Instruktionsverankerung ist abgeschlossen (done); die 3.93.12-Verankerungen und die 3.13-Abnahme bleiben offen (epic-3 remains in-progress).
3-9-deterministische-relevanz-und-reconcile-routing-schliessen: done # Review-Loop-3-Abschluss 2026-08-21 (bmad-code-review, 4 Layer; D-3.9-1/2/3 = 1/1/2 — empfohlene Optionen; kein Loopback). Hinweis: der finale `done`-Flip ist der Step-05-Status-Sync nach konvergiertem Review-Loop (Präzedenz Story 3.7/3.8); die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
3-10-inkrementelle-update-und-synthese-erhaltung-absichern: done # Story 3.10 Abschluss 2026-08-21 (bmad-code-review, 3 Layer; Patch-Kaskade, keine intent_gap/bad_spec — Sandbox E-1..E-9 nach Härtung 9/9 harte PASS/Exit 0, §5.16 Rev 3.5). Hinweis: die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
3-11-root-scope-leasing-atomar-akquirieren: review # Story 3.11 Review-Bereitschaft 2026-08-21 (§5.17 Verankerung, Revision 3.6; Sandbox A-1..A-8 8/8 harte PASS/Exit 0 — Zwei-Worktree-/Zwei-Prozess-Überlappung mit Zwischenzustands-Assertions „niemals zwei aktive Root-Leases", LEASE_HOLD-Nicht-Mutation, Kollisions-Hold mit beiden Commit-Hashes). Hinweis: die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen: backlog
3-13-epic-3-verifikations-und-abnahmegate: backlog
epic-3-retrospective: optional
3-11-root-scope-leasing-atomar-akquirieren: done # Story 3.11 Review-Loop-1-Abschluss 2026-08-21 (bmad-code-review, 4 Layer; D-3.11-1 „Konstruktiv + härten", D-3.11-2 „Intentionalen Mutationsversuch bauen"; 20 Patches angewendet — A-2-Sampler wirksam, A-4 Worktree-Beobachtung, A-7 MERGE_OK hart abgewiesen + Post-Merge-State + Baseline im Hold-Eintrag, §5.17 Pkt. 1/4/5-Berichtigungen; Sandbox A-1..A-8 8/8 harte PASS/Exit 0 re-executiert). Hinweis: der finale `done`-Flip ist der Step-05-Status-Sync nach konvergiertem Review-Loop (Präzedenz Story 3.7/3.8/3.9); die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen: done # Story 3.12 Review-Loop-2-Abschluss 2026-08-22 (bmad-code-review, 4 Layer; D-3.12-1 Option 1 — AC-6-Grenze an log.md-Praxis, L-8-Kategorie-Hyphenate + Negativ-Kontrolle; 7 Patches — P1 scopelock_healthy()/LOCK_READ_ERROR-Propagation, P2 Release-Fehler-negativ + L-6 hart, P3 Setup-Robustheit, P4 Ghost-Diff-Probe + reg_write real, P5 Ownership-Stale + Takeover-Exactly-once, S1/S2 Sync- & Anker-Berichtigung; Sandbox L-1..L-9 9/9 harte PASS/Exit 0 re-executiert; Loop 1: atomarer Ownership-CAS im Takeover, AK-2->AC-2; §5.18 Revision 3.7). Hinweis: der finale `done`-Flip ist der Step-05-Status-Sync nach konvergiertem Review-Loop (Präzedenz Story 3.7/3.8/3.9/3.10/3.11); die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
3-13-epic-3-verifikations-und-abnahmegate: done # Story 3.13 Review-Loop-2-Abschluss 2026-08-24 (bmad-code-review, 4 Layer, 2026-08-22; 28 eindeutige Befunde: 7 decision-needed / 15 patch / 2 defer / 6 dismissed; ProMods: „Ich folge Deinen Empfehlungen" — D-3.13-1..6 = 1/1/2/1/1/1, D-3.13-7 = 1): Status-Flip `done → in-progress` zurückgenommen (D-3.13-7, Präzedenz 3.73.12: finaler `done`-Flip = Step-05 nach konvergiertem Review-Loop); alle 22 Befunde tickt + Change-Log-Eintrag; run-sandbox.sh gehärtet (839 Z.: D-1..D-6 + P-2..P-14 — geankerter E.4-Classifier, Receipt-A-vs-B, G-6 perturbed-Tree wt-c + at-only-Kontrolle wt-c2, G-7 Link-Auflösung + geschlossene Token-Menge am Ist-Baum, D-NEG 3×FAIL+1×SUCCESS, Validator-Defizit-Route, P-11/P-13/P-14); P-1: sandbox-3-1 harte Exit-Pfade (14 PASS/Exit 0); VALIDATOR_DEFIZIT-Matrix-Zeile „G-5 PASS" vs. Boundary/AC-4 = Renegotiation-Kandidat (Boundary/AC-4 gilt implementiert; frozen-Block unangetastet, Ask-First). Erst-Lauf-Beleg (2026-08-22, vor Härtung): PASS_COUNT=70, FAILED=0, RUN_OK/Exit 0, Validator 7/7 SUCCESS — die (20/27/40/51/60/68)-Schnappschüsse im log.md-Eintrag sind kumulative PASS_COUNT-Werte; $BASE=5024d751… ist ein /tmp-Käfig-Commit (Lauf-Beleg, nicht re-ableitbar — P-12, korrigierende Formulierung im 2026-08-23-log-Eintrag). Re-Run #5 (Voll-Lauf gehärtetes Gate 915 Z., 2026-08-23): **ROT an E.4** (`NON_AT=26 AT=4`) — korrekt erkannter echter AD-16-A/B-Divergenz-Befund in der freien CREATE-Synthese (Slug-Identität `beta` vs. `quanten-observatorium-kanal` + freier Body-/log.md-Wortlaut), kein false-PASS; UPDATE-Pfad `alpha` byte-identisch bis auf `at:`; A/B, C 12/12, D 4/4 SUCCESS, D-NEG 3×FAIL+1×SUCCESS, E.1E.3 + beide frische Agent-Läufe grün, F/G im Voll-Lauf nicht demonstriert (fail-fast an E.4). **AC-4-Defizit-Roadmap:** Behebung liegt in `schema/compiler.md` (read-only) oder A/B-Vergleichsumfang (A0-20) → Story 3.13 bleibt `in-progress`, Bedarf an **separat autorisierter Epic-1-Remediation** (bzw. menschlicher A/B-Umfangs-Entscheidung) benannt; kein stiller Schema-Change (AD-3/AC-4). **Review-Loop-2 (2026-08-24, bmad-code-review 4 Layer, Diff `50f3628..HEAD` + working tree): 38 rohe → 4 decision-needed / 11 patch / 2 defer / 4 dismissed; alle Entscheidungen mit ProMods geschlossen (D-3.13-L2-1 = Defer+Bearer-Fix, D-3.13-L2-2 = Defer+Gate-Doku, D-3.13-L2-3 = Defer+Substitutions-Notiz, D-3.13-L2-4 = dokumentierte Abweichung), alle Patches P-L2-1..11 angewendet (Gate 1005 Z., `bash -n` clean).** **Grüner Re-Run #10 (2026-08-24, committed `09c1c83`): `FAILED=0 PASS_COUNT=51`, SANDBOX-3-13-OK, RUN_OK/Exit 0 — A.5 Pre-Check sauber, C 12/12, D 4/4 SUCCESS, D-NEG 3×FAIL+1×SUCCESS, E.4 `NON_AT=0 AT=2`, E.6 Feldsatz-exakt + Known-Value-Witness (P-L2-1/2), E.9 kanonische `Baseline $BASE`-Form (P-L2-6) hart grün, F G-1..G-8 (G-6 Negativ-Kontrolle NON_AT=2 erkannt, Positiv-Kontrolle at-only toleriert, G-7 Smoke 0 Verletzungen), G-8 Porcelain-Clean (inkl. Untracked) + Negativ-Kontrolle + AD-3 Read-only ohne Diff.** Finaler `done`-Flip (Step-05 nach konvergiertem Review-Loop-2 + grünem Re-Run, Präzedenz 3.73.12). **Epic 3 → `done`** (AC-9: alle Stories 3.13.13 done); `epic-3-retrospective` (optional) bleibt offen.
epic-3-retrospective: done
epic-4: backlog
4-1-information-vor-jeder-änderung-klassifizieren-new-confirming: backlog
@@ -182,17 +182,17 @@ action_items:
owner: "dev"
status: done
closed: "2026-08-18"
resolution: "Ausgefuehrt als Teil der autorisierten Validator-Rev-9: die zwei gehaltenen
Patches 16/17 umgesetzt — §3-Punkt-4-Vorlage um resolved=<pfad>-Token ergaenzt
(inkl. §6.2-Semantik: nur bei Ablehnung durch aufgeloeste Lage ausserhalb raw/,
Wert = aufgeloester workspace-relativer Pfad; Fixture 4a -> FAIL Punkt 4 (resolved=README.md),
ableitbar), Innen-Ebenen-Punkt-6-Negativ- UND Positiv-Fixture-Zeilen in §7.3
ergaenzt (role: x -> FAIL Punkt 6, Isolations-Prinzip). Scope: zusaetzlich die
de-duplizierte Punkt-11-Area-Lesart aus AI-2-R-5 (eingefrorener Intent, epic-2-retro-item-14
= eigener action_item done-Eintrag mit eigener resolution + ref auf die Rev-9-Runde);
dieses Item deckt die zwei gehaltenen Patches, item-14 die Punkt-11-Area-Lesart.
Zertifizierung (isolierte Fixtures + reales Bundle 7/7 SUCCESS) und wiki/log.md-Nachweis
erfolgt (inkl. Freeze-Command + Live-Invariant)."
resolution: "Ausgefuehrt als Teil der autorisierten Validator-Rev-9: die zwei
gehaltenen Patches 16/17 umgesetzt — §3-Punkt-4-Vorlage um resolved=<pfad>-Token
ergaenzt (inkl. §6.2-Semantik: nur bei Ablehnung durch aufgeloeste Lage ausserhalb
raw/, Wert = aufgeloester workspace-relativer Pfad; Fixture 4a -> FAIL Punkt
4 (resolved=README.md), ableitbar), Innen-Ebenen-Punkt-6-Negativ- UND Positiv-Fixture-Zeilen
in §7.3 ergaenzt (role: x -> FAIL Punkt 6, Isolations-Prinzip). Scope: zusaetzlich
die de-duplizierte Punkt-11-Area-Lesart aus AI-2-R-5 (eingefrorener Intent,
epic-2-retro-item-14 = eigener action_item done-Eintrag mit eigener resolution
+ ref auf die Rev-9-Runde); dieses Item deckt die zwei gehaltenen Patches, item-14
die Punkt-11-Area-Lesart. Zertifizierung (isolierte Fixtures + reales Bundle
7/7 SUCCESS) und wiki/log.md-Nachweis erfolgt (inkl. Freeze-Command + Live-Invariant)."
ref: "_bmad-output/implementation-artifacts/deferred-work.md"
- id: "epic-2-retro-item-10-schema-compiler-md-5-5-selbsttest-formel"
epic: 2
@@ -236,3 +236,60 @@ action_items:
Nennung. §7-Fixtures 11a (Negativ/Positiv), Revisionslog 9, Header 9, wiki/log.md-Nachweis
(7/7 SUCCESS) erbracht. Quelle: spec-autorisierte-validator-revision-9-punkt-11-area-lesart.md"
ref: "_bmad-output/implementation-artifacts/epic-2-retro-2026-08-18.md"
- id: "epic-3-retro-item-1-datumsgruppen-determinismus"
epic: 3
action: "Sektion 5.9 Pkt.4 Datumsgruppen-Header (## YYYY-MM-DD) in wiki/log.md
auf einen Git-Wert pinnen (git show -s --format=%cs $BASE) ODER als benannte
Ausnahme in classify_diff (sandbox-3-13 Z.650-653) klassifizieren + den Sektion-5.9-Z.277-Wortlaut
('keinen Wanduhr-Wert... nie im log.md-Body') mit der Header-Realitaet vereinbaren
- schliesst den Sektion-5.14 'genau eine Ausnahme'-Selbstwiderspruch (F-5)"
owner: "ProMods+dev"
status: open
ref: "D:/mita/wow-2nd/_bmad-output/implementation-artifacts/epic-3-retro-2026-08-24.md"
- id: "epic-3-retro-item-2-staleness-reconciliation"
epic: 3
action: "Sektion 5.12 Pkt.1 (Gen-only-stale / 'TTL-Ablauf-Äquivalent', compiler.md
Z.327) um Supersession-Notiz zu Sektion 5.18 Pkt.1 (Z.430) + Story-3.12-AC (epics.md
Z.426) ergänzen; Sektion 5.18 als normative Staleness-Autoritaet kennzeichnen
(F-6)"
owner: "ProMods+dev"
status: open
ref: "D:/mita/wow-2nd/_bmad-output/implementation-artifacts/epic-3-retro-2026-08-24.md"
- id: "epic-3-retro-item-3-gate-leasing-verification"
epic: 3
action: "Gate sandbox-3-13: Leasing-Verifikation im realen Agent-Lauf ergänzen
(in wt-a verifizieren: refs/leases/wiki existiert UND git rev-parse 'refs/leases/wiki^{}'
== git hash-object der Run-ID, Known-Value-Witness analog E.9) - beweist Annahme
des Sektion-5.17-atomaren Pfads statt des supersedierten Sektion-5.11-check-then-act-Lockfiles
(F-7)"
owner: "dev+review"
status: open
ref: "D:/mita/wow-2nd/_bmad-output/implementation-artifacts/epic-3-retro-2026-08-24.md"
- id: "epic-3-retro-item-4-emdash-kollapsklasse"
epic: 3
action: "canonical-terms.md Formatzeile (Kollaps-Klasse [-_ ], Z.18/21) append-only-konform
um den Em-Dash erweitern, einig mit compiler.md Sektion 3.2 / Sektion 5.14 ([-–—
_], Z.51/54a) (F-9)"
owner: "ProMods+dev"
status: open
ref: "D:/mita/wow-2nd/_bmad-output/implementation-artifacts/epic-3-retro-2026-08-24.md"
- id: "epic-3-retro-item-5-norm-lc-all"
epic: 3
action: "4 drifteten norm()-Kopien (sandbox-3-8/3-9/3-10/3-11) auf eine kanonische
Implementierung mit Case-Folding vereinheitlichen + LC_ALL=C-Pinning (3-11 aktuell
LC_ALL=0); Sharpening des bekannten Defers D-7 / W-3.9-1 (F-1)"
owner: "dev"
status: open
ref: "D:/mita/wow-2nd/_bmad-output/implementation-artifacts/epic-3-retro-2026-08-24.md"
- id: "epic-3-retro-item-6-llm-noop-guard"
epic: 3
action: "Gate sandbox-3-13: frischer-LLM-Non-Execution am Kern-AC absichern (F-10,
empirisch: Live-Re-Exekution ROT an E.4, Lauf A no-op / HEAD-A==BASE, Lauf B
korrekt, GATE_EXIT=1): (a) Run-Vollstaendigkeits-Guard - RUN_DONE nur wenn >=1
Mutation staged, sonst RUN_ABORTED; (b) Divergenz-Retry bei E.4-HARD-FAIL (max.
N, explizit protokolliert); (c) AD-17h-Claim-Schaerfe um die Ausfuehrungs-Voraussetzung
erweitern; (d) Agent-Transkript-Erhaltung im Gate (run-a.out/run-b.out nicht
aufraeumen) zur Root-Cause-Analyse (aktuell offen, Root aufgeraeumt)"
owner: "dev+review"
status: open
ref: "D:/mita/wow-2nd/_bmad-output/implementation-artifacts/epic-3-retro-2026-08-24.md"
+29 -8
View File
File diff suppressed because one or more lines are too long
+15 -1
View File
File diff suppressed because one or more lines are too long