diff --git a/_bmad-output/implementation-artifacts/deferred-work.md b/_bmad-output/implementation-artifacts/deferred-work.md index de05112..e06eacf 100644 --- a/_bmad-output/implementation-artifacts/deferred-work.md +++ b/_bmad-output/implementation-artifacts/deferred-work.md @@ -648,3 +648,9 @@ Der Epic-3-Abnahmegate-Lauf (`_bmad-output/implementation-artifacts/sandbox-3-13 - **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). diff --git a/_bmad-output/implementation-artifacts/sandbox-3-1/run-sandbox.sh b/_bmad-output/implementation-artifacts/sandbox-3-1/run-sandbox.sh index 49d174d..41211cb 100644 --- a/_bmad-output/implementation-artifacts/sandbox-3-1/run-sandbox.sh +++ b/_bmad-output/implementation-artifacts/sandbox-3-1/run-sandbox.sh @@ -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 < -- 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 -- 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 diff --git a/_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh b/_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh index 92254dd..e6e98c4 100644 --- a/_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh +++ b/_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh @@ -2,327 +2,368 @@ # ============================================================================ # Sandbox Story 3.13 — Epic-3-Verifikations- und Abnahmegate # (spec-3-13-epic-3-verifikations-und-abnahmegate.md, 2026-08-22) -# # Re-executierbar: bash run-sandbox.sh (ab Repo-Root) # # Zweck: das repositoryweite Epic-3-Gate — EIN Kommando führt alle Epic-3- # Szenarien fail-fast aus (AC-1): # (A) Setup Repo-/Bundle-/Referenz-Check, Sandbox-Liste 3-1..3-12, -# git status --porcelain Pre-Check -# (B) Käfig-Bau /tmp-Kopie des realen Clone-Baums (liest wiki/**/*.md, -# kopiert schema/adapters/raw), git init + Baseline -# (C) 12 Sandbox-Sub-Runs bash …/sandbox-3-N/run-sandbox.sh, Exit-Check -# pausiert, fail-fast +# Porcelain-Pre-Check (vollständig, inkl. Untracked) +# (B) Käfig-Bau /tmp-Kopie des realen Clone-Baums (schema/adapters/ +# raw/wiki), git init + Baseline +# (C) 12 Sandbox-Sub-Runs bash …/sandbox-3-N/run-sandbox.sh, hart, fail-fast # (D) Validator-Agent frischer Sub-Agent liest schema/validator.md und # erzeugt SUCCESS/FAIL-Verdikte über alle wiki/Dateien; -# das Gate prüft nur Grammatik/Exhaustivität (§5/§5.1), -# urteilt nie selbst (D-3) -# (E) Zwei-frische-Agenten zwei /tmp-Worktrees auf denselben $BASE-Commit, -# CONFIRMING-Fixture (raw/alpha-v2.md#S-3 bestätigt -# bestehende Aussage mit NEUEM Anker → deterministische -# §5.16-Pkt.-4-Multi-Beleg-Konsolidierung), beide Läufe -# führen schema/compiler.md aus; A/B-git diff -- wiki/ -# at-only; G-6 perturbed Negativ-Kontrolle -# (F) G-1..G-8 harte Assertions (inkl. Epic-5-Consumer-Smoke G-7, -# Porcelain-Check G-8) -# (G) Nachweise/Porcelain Endzustands-Invariante +# das Gate prüft nur Grammatik/Exhaustivität/Exaktheit +# (§5/§5.1), urteilt nie selbst (D-3) +# (D-NEG) Negativ-Fixtures (AC-3-Hälfte): separater /tmp-Käfig mit 3 §7-/§6- +# Negativ-Fixtures + 1 Positiv-Kontrolle; frischer +# Validator-Agent MUSS 3×FAIL + 1×SUCCESS liefern +# (E) Zwei-frische-Agenten zwei /tmp-Worktrees auf denselben $BASE-Commit +# (isolierte Mini-Bundle-Fixture: UPDATE alpha + CREATE +# beta), beide Läufe führen schema/compiler.md aus; +# A/B-anglichen: Frontmatter/Index/Log/Byte-Kern nur +# at-Ausnahme, Run-Receipts A==B (außer at/Tree-Cellen) +# (F) G-1..G-8 harte Assertions (inkl. G-6 echter perturbed-Drittrun-Tree +# C durch denselben E.4-Classifier + Positiv-/Negativ- +# Kontrolle, G-7 Epic-5-Smoke auf Ist-Baum, G-8 Porcelain) +# (G) Nachweise/Porcelain E.9-Nachweis + finaler Porcelain-Check # -# Constraints (Spec): -# - setup: set -u, kein set -e (negatives Assertions-Idiom); ROOT=$(mktemp -# -d /tmp/sb313-XXXXXX); git config core.autocrlf false + core.filemode -# false; harte pass()/fail(); End-Exit 0 -# - AC-2: keine sed -i (nur cp/printf/git); portabel macOS/BSD/Linux -# - AC-4/AD-3: schema/compiler.md, validator.md, wiki-compiler.md, -# adapters/, raw/ read-only; das Gate mutiert sie nie -# - AC-5: das Gate schreibt KEINE erwarteten Wiki-Bodies/Lease-/Log-/Git- -# Ausgänge; die beiden frischen Agent-Kontexte erzeugen die Wiki-/Receipt- -# Ausgabe; das Gate vergleicht nur A vs B -# - races ausschließlich als /tmp-Käfig des realen Clone-Baums (nie Ist-Baum) -# - D-3: der Validator ist eine Instruktion; das Gate validiert nur -# Verdikt-Grammatik/-Exhaustivität, urteilt nie fachlich +# Exit: 0 = ALLE harten Checks grün (Szenarien + Gate + Nachweise); +# ≠0 = irgendein harter Check rot (fail-fast, FAILED>0 → exit 1). +# +# Review-Loop-2-Härtung (Story 3.13, D-3.13-1..6 + P-2..P-15): +# - D-1: AGENT_PROMPT = nur compiler.md-Anweisung + Fixture-Fakten; +# KEINE erwarteten Plan-/Entscheidungs-/Receipt-/Log-Literale. +# - D-2: E.6 = Run-Receipt-A-vs-B-Abgleich (ohne erwartete Literale; +# Modell: sandbox-3-8 DET-2, at/tree-Cellen gefiltert). +# - D-3: G-6 = echter perturbed committed Tree (wt-c) durch denselben +# E.4-Classifier: MUSS NON_AT≠0 flaggen; at-only-Kontrolle wt-c2 +# MUSS NON_AT=0 liefern (deterministisch, kein dritter Agent). +# - D-4: G-7 = generischer Link-Auflösungs-Check + geschlossene, am realen +# Bundle validierte Planungstoken-Menge (P-13). +# - D-5: D-NEG = 3 §7-/§6-Negativ-Fixtures + frischer Validator-Agent, +# der zwingend 3×FAIL + 1×SUCCESS liefert (AC-3-Hälfte). +# - D-6: E.1-Fixture mit CREATE-Fall (beta: neue raw-Quellen → neues +# Concept + Index-Link); AC-6 sechs Demonstrationsfälle. +# - P-2..P-14: siehe spec §5.18 (Patches). # ============================================================================ set -u -# ---------- Basis-Exit-/Pass-/Fail-Idiom (harte Assertions, kein set -e) ---------- -FAILED=0 PASS_COUNT=0 -runlabel() { echo; echo "########## $1 ##########"; } +FAILED=0 pass() { PASS_COUNT=$((PASS_COUNT+1)); echo "PASS: $1"; } fail() { FAILED=$((FAILED+1)); echo "FAIL: $1" >&2; } -cmd_or_fail() { # $1 = Beschreibung ; rest = Kommando — hart abbrechen wenn Kommando fehlschlägt - local desc="$1"; shift - if ! "$@"; then - echo "HARD-FAIL: $desc" >&2 - exit 1 +hardfail() { echo "HARD-FAIL: $1" >&2; exit 1; } +runlabel() { echo; echo "########## $1 ##########"; } +# 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 +} +# expect_gte: $1=Beschreibung, $2=Schwelle, $3=Zahl +expect_gte() { + case "$3" in (''|*[!0-9]*) fail "$1 — nicht numerisch: [$3]"; return;; esac + if [ "$3" -ge "$2" ]; then pass "$1 ($3 ≥ $2)"; else + fail "$1 — $3 < $2" fi } -# $* / "$@" -# ShellCheck SC2034 not an issue here. - -# ---------- A. Setup: Repo-/Bundle-/Referenz-Check ---------- -runlabel "A. SETUP — Repo-/Bundle-/Referenz-Check, Sandbox-Liste, Porcelain-Pre-Check" - -REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" -# Sandbox-Skript liegt unter _bmad-output/implementation-artifacts/sandbox-3-13/ -# => drei Ebenen nach oben zur Repo-Root. -echo "REPO_ROOT=$REPO_ROOT" - -# Referenz-Sandboxen 3-1..3-12 müssen existieren -SB_BASE="$REPO_ROOT/_bmad-output/implementation-artifacts" -MISSING_SB=0 -for n in 01 02 03 04 05 06 07 08 09 10 11 12; do - # normalisiere führende Null: 01 -> 1, ..., 09 -> 9, 10..12 bleiben - case "$n" in - 0*) num="${n#0}";; - *) num="$n";; - esac - d="$SB_BASE/sandbox-3-$num/run-sandbox.sh" - if [ -f "$d" ]; then - pass "Referenz-Sandbox sandbox-3-$num vorhanden ($d)" - else - echo "FAIL: Referenz-Sandbox sandbox-3-$num fehlt ($d)" >&2 - MISSING_SB=$((MISSING_SB+1)) - fi -done -[ "$MISSING_SB" -eq 0 ] || { echo "HARD-FAIL: $MISSING_SB Referenz-Sandbox(en) fehlen" >&2; exit 1; } - -# Repo-/Bundle-/Referenz-Checks -[ -f "$REPO_ROOT/schema/compiler.md" ] && pass "schema/compiler.md vorhanden" \ - || { echo "HARD-FAIL: schema/compiler.md fehlt" >&2; exit 1; } -[ -f "$REPO_ROOT/schema/validator.md" ] && pass "schema/validator.md vorhanden" \ - || { echo "HARD-FAIL: schema/validator.md fehlt" >&2; exit 1; } -[ -f "$REPO_ROOT/schema/wiki-compiler.md" ] && pass "schema/wiki-compiler.md vorhanden" \ - || { echo "HARD-FAIL: schema/wiki-compiler.md fehlt" >&2; exit 1; } -[ -f "$REPO_ROOT/schema/canonical-terms.md" ] && pass "schema/canonical-terms.md vorhanden" \ - || { echo "HARD-FAIL: schema/canonical-terms.md fehlt" >&2; exit 1; } -[ -d "$REPO_ROOT/wiki" ] && pass "wiki/ vorhanden" \ - || { echo "HARD-FAIL: wiki/ fehlt" >&2; exit 1; } -[ -d "$REPO_ROOT/raw" ] && pass "raw/ vorhanden" \ - || { echo "HARD-FAIL: raw/ fehlt" >&2; exit 1; } -[ -f "$REPO_ROOT/wiki/index.md" ] && pass "wiki/index.md vorhanden" \ - || { echo "HARD-FAIL: wiki/index.md fehlt" >&2; exit 1; } - -# git status --porcelain Pre-Check: sauberer Ist-Zustand (G-8 Vorabbedingung) -PORCELAIN_PRE=$(git -C "$REPO_ROOT" status --porcelain --untracked-files=no 2>/dev/null || true) -if [ -z "$PORCELAIN_PRE" ]; then - pass "Porcelain Pre-Check: Ist-Baum frei von getrackten uncommitteten Änderungen (G-8-Vorbedingung)" -else - echo "HARD-FAIL: Porcelain Pre-Check — Ist-Baum hat getrackte uncommittete Änderungen:" >&2 - printf '%s\n' "$PORCELAIN_PRE" >&2 - exit 1 -fi - -# Read-only-Invarianten vorab (AD-3): Schema-/raw-/adapters-Hashes gegen Baseline -# (unterstützt den AD-3-Nachweis am Ende; Baseline-Stichprobe) -echo "(Read-only-Invarianten AD-3 werden in G-8 hart geprüft — hier nur Stichprobe der Existenz)" - -echo -echo "SETUP abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" - -# ============================================================================== -# B. KÄFIG-BAU — /tmp-Kopie des realen Clone-Baums (nie Ist-Baum) -# ============================================================================== -runlabel "B. KAEFIG-BAU — /tmp-Kopie des realen Clone-Baums" - -# ROOT: /tmp-Basis für ALLE Gate-Artefakte (Käfig, Worktrees, Receipts, Manifest) -ROOT=$(mktemp -d /tmp/sb313-XXXXXX) || { echo "HARD-FAIL: mktemp ROOT fehlgeschlagen" >&2; exit 1; } +# P-14: /tmp-Aufräum-Trap (kein Orphan-Käfig; kein rm -rf außerhalb $ROOT) +ROOT=$(mktemp -d /tmp/sb313-XXXXXX) +trap 'rm -rf "$ROOT"' EXIT CAGE="$ROOT/cage" -mkdir -p "$CAGE" || { echo "HARD-FAIL: cage mkdir fehlgeschlagen" >&2; exit 1; } -pass "Gate-Root $ROOT (loeschbar: rm -rf $ROOT)" +mkdir -p "$CAGE" -# (1) Bundle-Dateiliste aus der REALEN wiki/ lesen (nur Textquelle, §-Spec: "reales -# wiki/ wird nur als Textquelle gelesen") — für die Exhaustivitäts-Nachführung -# des Validators und den Epic-5-Smoke. -echo "--- bundle-dateiliste (wiki/**/*.md) ---" -WIKI_MD_LIST=$(cd "$REPO_ROOT" && find wiki -type f -name '*.md' | LC_ALL=C sort) -[ -n "$WIKI_MD_LIST" ] || { echo "HARD-FAIL: keine wiki/**/*.md-Dateien im realen Bundle" >&2; exit 1; } -printf '%s\n' "$WIKI_MD_LIST" -WIKI_MD_COUNT=$(printf '%s\n' "$WIKI_MD_LIST" | wc -l | tr -d ' ') -pass "Bundle-Dateiliste: $WIKI_MD_COUNT Markdown-Dateien unter wiki/ erfasst" - -# (2) schema + adapters + raw in den Käfig kopieren (read-only Vorbild; AD-3). -# Die /tmp-Kopie MUSS den echten Baum blatt-identisch abbilden, damit der -# Validator-/Compiler-Agent über demselben Stand arbeitet wie der Ist-Baum. -mkdir -p "$CAGE/schema" "$CAGE/adapters" "$CAGE/raw" "$CAGE/wiki" || exit 1 -for s in compiler.md validator.md wiki-compiler.md canonical-terms.md; do - cp "$REPO_ROOT/schema/$s" "$CAGE/schema/$s" 2>/dev/null \ - || { echo "HARD-FAIL: schema/$s in Käfig kopieren fehlgeschlagen" >&2; exit 1; } +runlabel "A: Setup (Repo-/Bundle-/Referenz-Check, Sandbox-Liste, Porcelain)" +# A.1: Repo-Root auflösen (dieses Skript liegt unter _bmad-output/…) +SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_PATH/../../.." && pwd)" +cd "$REPO_ROOT" || hardfail "Repo-Root nicht erreichbar: $REPO_ROOT" +echo "REPO_ROOT: $REPO_ROOT" +git rev-parse --is-inside-work-tree >/dev/null 2>&1 || hardfail "Repo-Root ist kein Git-Repo" +BASE_REAL=$(git rev-parse HEAD) || hardfail "Repo-Root HEAD nicht auflösbar" +echo "REPO-HEAD (real): $BASE_REAL" +# P-3: Agent-Binary aufloesbar sein MUSS (PATH-Pruefung, kein verhaerteter Pfad) +CLAUDE_BIN="${CLAUDE_BIN:-$(command -v claude 2>/dev/null || true)}" +[ -n "$CLAUDE_BIN" ] && [ -x "$CLAUDE_BIN" ] || hardfail "CLAUDE_BIN nicht auflösbar (PATH: 'claude' nicht gefunden; CLAUDE_BIN-Env setzen)" +echo "CLAUDE_BIN: $CLAUDE_BIN" +command -v timeout >/dev/null || hardfail "timeout fehlt (git-for-win/coreutils erwartet)" +command -v sha256sum >/dev/null || hardfail "sha256sum fehlt (git-for-win/coreutils erwartet)" +command -v tar >/dev/null || hardfail "tar fehlt" +command -v cygpath >/dev/null || hardfail "cygpath fehlt (Git-Bash-Umgebung erwartet)" +command -v bash >/dev/null || hardfail "bash fehlt" +# A.2: Referenz-Schemata vorhanden (P-12: Vorhandensein-Hartcheck) +for ref in schema/compiler.md schema/validator.md schema/wiki-compiler.md schema/canonical-terms.md adapters/README.md; do + [ -f "$ref" ] || hardfail "Referenz-Schema fehlt: $ref" done -pass "schema/ (compiler.md, validator.md, wiki-compiler.md, canonical-terms.md) in Käfig kopiert (AD-3 read-only Vorbild)" -cp -r "$REPO_ROOT/adapters/." "$CAGE/adapters/" 2>/dev/null \ - || { echo "HARD-FAIL: adapters/ in Käfig kopieren fehlgeschlagen" >&2; exit 1; } -pass "adapters/ in Käfig kopiert (read-only Vorbild)" -cp -r "$REPO_ROOT/raw/." "$CAGE/raw/" 2>/dev/null \ - || { echo "HARD-FAIL: raw/ in Käfig kopieren fehlgeschlagen" >&2; exit 1; } -pass "raw/ in Käfig kopiert (read-only Vorbild)" -# wiki/ als Textquelle in den Käfig spiegeln (Grundlage Validator-/Compiler-Agent) -cp -r "$REPO_ROOT/wiki/." "$CAGE/wiki/" 2>/dev/null \ - || { echo "HARD-FAIL: wiki/ in Käfig kopieren fehlgeschlagen" >&2; exit 1; } -pass "wiki/ als Textquelle in Käfig kopiert" +pass "A.2 Referenz-Schemata vorhanden (compiler/validator/wiki-compiler/canonical-terms/adapters)" +# A.3: Bundle-Vorbedingungen (V-1..V-6, Vertrag §2) im Ist-Repo +[ -f wiki/index.md ] || hardfail "V-1: wiki/index.md fehlt" +[ -d raw ] || hardfail "V-2: raw/ fehlt" +[ -f raw/README.md ] || hardfail "V-3: raw/README.md fehlt" +[ -f wiki/log.md ] || hardfail "V-4: wiki/log.md fehlt" +for area in concepts processes states; do + [ -d "wiki/$area" ] || hardfail "V-5: Bundle-Area wiki/$area fehlt" +done +[ -f wiki/concepts/README.md ] || hardfail "V-6: wiki/concepts/README.md fehlt" +pass "A.3 Bundle-Vorbedingungen V-1..V-6 erfüllt (Ist-Repo)" +# A.4: Sandbox-Suite 3.1..3.12 vollständig +SUITE_OK=1 +for N in 1 2 3 4 5 6 7 8 9 10 11 12; do + sdir="_bmad-output/implementation-artifacts/sandbox-3-$N" + [ -f "$sdir/run-sandbox.sh" ] || { fail "Sandbox-3-$N fehlt: $sdir/run-sandbox.sh"; SUITE_OK=0; } +done +[ "$SUITE_OK" -eq 1 ] || hardfail "Epic-3-Sandbox-Suite unvollständig" +pass "A.4 Sandbox-Suite 3.1..3.12 vollständig (12/12)" +# A.5: Porcelain-Pre-Check: vollständiger Status (P-11: kein --untracked-files=no; +# Untracked = repo-relevanter Zuwaschs, der vor dem Gate committed sein muss) +PORE_PRE=$(git status --porcelain) +echo "--- git status --porcelain (Pre-Check; leer = erwartet) ---" +echo "$PORE_PRE" +[ -z "$PORE_PRE" ] && pass "A.5 Pre-Check: Repo sauber (committed, kein Untracked)" \ + || fail "A.5 Pre-Check: Repo NICHT sauber — Zustand muss vor dem Gate committed sein (AD-17a): $PORE_PRE" -# (3) Käfig als git-Repo initialisieren (Baseline für Validator-Exhaustivität) -# — Determinismus: autocrlf/filemode deaktiviert (LF-Blobs, AD-17h). -cd "$CAGE" || exit 1 -git init -q || { echo "HARD-FAIL: cage git init fehlgeschlagen" >&2; exit 1; } +runlabel "B: Käfig-Bau (/tmp-Kopie: schema/adapters/raw/wiki + git init + Baseline)" +mkdir -p "$CAGE" +cp -r schema "$CAGE/schema" +cp -r raw "$CAGE/raw" +cp -r wiki "$CAGE/wiki" +# P-4: Isolation — realen Bundle-Inhalt (raw/wiki) ersetzen durch die +# 3-13-Mini-Fixture; Käfig trägt damit KEINE realen Concept-/Raw-Dateien +rm -rf "$CAGE/raw" "$CAGE/wiki" +# Layout: Root-Concepts, KEINE Areas — Punkt-11-sauber (Area mit Inhalt ohne +# index.md = FAIL) und konsistent mit E.1; V-5-Area-Direktoren sind eine +# Repo-Voraussetzung (A.3), keine Validator-/Käfig-Pflicht. +mkdir -p "$CAGE/raw" "$CAGE/wiki" +cp raw/README.md "$CAGE/raw/README.md" 2>/dev/null || echo "# Raw" > "$CAGE/raw/README.md" +cat > "$CAGE/wiki/log.md" <<'EOF' +# Log +EOF +cat > "$CAGE/wiki/index.md" <<'EOF' +--- +type: bundle +okf_version: "0.2" +--- +# Index +- [Alpha](alpha.md) +- [Gamma](gamma.md) +EOF +cat > "$CAGE/wiki/alpha.md" <<'EOF' +--- +type: concept +sources: + - resource: raw/alpha-v1.md + id: s1 +generated: + by: wow-compiler/0.1.0 + at: 2026-08-21T08:00:00Z +--- +Alpha beschreibt die Baseline. +EOF +cat > "$CAGE/wiki/gamma.md" <<'EOF' +--- +type: concept +sources: + - resource: raw/gamma-v1.md + id: s1 +generated: + by: wow-compiler/0.1.0 + at: 2026-08-21T08:00:00Z +--- +Gamma ist eine isolierte Referenz ohne Überschneidung. +EOF +cat > "$CAGE/raw/alpha-v1.md" <<'EOF' +Evidenz v1: quanten-protocol-schlüssel (Stelle S-1). +EOF +cat > "$CAGE/raw/gamma-v1.md" <<'EOF' +Evidenz v1: gamma-observatorium-thema (Stelle S-1). +EOF +cd "$CAGE" || hardfail "Käfig nicht betretbar" +git init -q git config core.autocrlf false git config core.filemode false -git config user.email "gate@sandbox" -git config user.name "Gate-Sandbox" +git config user.email "sandbox@test" +git config user.name "Sandbox 3.13" git add -A -git commit -qm "Käfig-Baseline" || { echo "HARD-FAIL: cage Baseline-Commit fehlgeschlagen" >&2; exit 1; } -CAGE_BASE=$(git rev-parse HEAD) -[ -n "$CAGE_BASE" ] && git cat-file -e "$CAGE_BASE^{commit}" 2>/dev/null \ - || { echo "HARD-FAIL: Käfig-Baseline nicht verifiziert" >&2; exit 1; } -pass "Käfig-Baseline committet (CAGE_BASE=$CAGE_BASE)" +git commit -qm "Käfig-Baseline" || hardfail "Käfig-Baseline-Commit fehlgeschlagen" +echo "Käfig-Baseline-Commit: $(git rev-parse HEAD)" -echo -echo "KAEFIG-BAU abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" - -# ============================================================================== -# C. 12 SANDOX-SUB-RUNS — fail-fast, Exit-Check je Sandbox (AC-1) -# ============================================================================== -runlabel "C. 12-SANDBOX-SUB-RUNS — je bash …/run-sandbox.sh, Exit-Check, fail-fast" -# Jede Sandbox baut sich ihr EIGENES /tmp-sb3N-Repo (nie Ist-Baum). Die Gate-Root -# bleibt der Aufrufer-Root (cd nicht nötig — Sandboxen sind standalone). -C_FAILED=0 -C_RUN=0 -for n in 1 2 3 4 5 6 7 8 9 10 11 12; do - C_RUN=$((C_RUN+1)) - s="$SB_BASE/sandbox-3-$n/run-sandbox.sh" - echo "--- Sandbox-Sub-Run $n: $s ---" - if timeout 900 bash "$s" > "$ROOT/sandbox-$n.out" 2>&1; then - pass "Sandbox-Sub-Run $n (sandbox-3-$n) Exit 0" +runlabel "C: 12 Sandbox-Sub-Runs (hart, fail-fast; §15.2/AC-2)" +for N in 1 2 3 4 5 6 7 8 9 10 11 12; do + sdir="$REPO_ROOT/_bmad-output/implementation-artifacts/sandbox-3-$N" + echo "--- Sub-Run 3.$N: bash $sdir/run-sandbox.sh ---" + if bash "$sdir/run-sandbox.sh"; then + pass "Sub-Run 3.$N: OK (harte Exit-Pfade grün)" else - rc=$? - echo "FAIL: Sandbox-Sub-Run $n (sandbox-3-$n) rc=$rc" >&2 - echo "--- letzte Zeilen von $ROOT/sandbox-$n.out ---" >&2 - tail -20 "$ROOT/sandbox-$n.out" >&2 2>/dev/null || true - C_FAILED=$((C_FAILED+1)) - # fail-fast (AC-1): erste fehlgeschlagene Sandbox bricht ab - echo "HARD-FAIL: Sandbox-Sub-Run $n fehlgeschlagen (fail-fast)" >&2 + fail "Sub-Run 3.$N: EXIT≠0 — fail-fast, Gate bricht ab" + echo "SANDBOX-3-13-FAILED" >&2 exit 1 fi done -[ "$C_FAILED" -eq 0 ] || { echo "HARD-FAIL: $C_FAILED Sandbox-Sub-Run(s) fehlgeschlagen" >&2; exit 1; } -pass "Alle $C_RUN Sandbox-Sub-Runs Exit 0 (fail-fast-Sequenz)" +pass "C.12 ALLE 12 Sub-Runs grün" -echo -echo "12-SANDBOX-SUB-RUNS abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" - -# ============================================================================== -# D. VALIDATOR-AGENT — frischer Kontext führt schema/validator.md aus (AC-3/AC-4) -# ============================================================================== -runlabel "D. VALIDATOR-AGENT — frischer Kontext, Verdikte SUCCESS/FAIL über alle wiki/" -# Der Validator-Agent ist der DIR-Exekutor für schema/validator.md (D-3): er liest -# die Instruktion und erzeugt je wiki-Datei ein Verdikt (SUCCESS bzw. -# FAIL ). Das Gate prüft NUR Grammatik (§5/§5.1) und Exhaustivität -# (jede wiki/**/*.md hat genau ein Verdikt) — es urteilt nie fachlich (D-3). -# -# Ablage: verdict-.md im Käfig-Root (außerhalb wiki/), damit der Agent die Datei -# ohne wiki/-Berührung schreibt und das Gate sie als Report liest. -VALIDATOR_CAGE="$CAGE" -VALIDATOR_OUT="$ROOT/verdict-validator.md" -[ -n "${CLAUDE_BIN:-}" ] || CLAUDE_BIN="/d/Apps/nodejs/claude.cmd" -# -x schlägt unter MSYS/Windows für .cmd-Dateien fehl (Mode 0644 trotz Ausführbarkeit). -# Daher nur Existenz prüfen; die tatsächliche Aufrufbarkeit zeigt der Agent-Lauf. -[ -f "$CLAUDE_BIN" ] || { echo "HARD-FAIL: claude.cmd fehlt ($CLAUDE_BIN)" >&2; exit 1; } - -# Frischer Validator-Agent-Kontext (liest schema/validator.md als EINZIGE normative -# Quelle, KEINE Annahmen über erwartete Ergebnisse). Schreibt verdict-Datei. -run_validator_agent() { - local out="$1" cage="$2" - local win_path - win_path=$(cygpath -w "$out" 2>/dev/null || echo "$out") - # Prompt-Datei: VERDICT_OUT-Placeholder wird deterministisch ersetzt (printf, - # kein sed -i / keine Backslash-Falle durch Windows-Pfad), dann liest der - # frische Kontext die Prompt-Datei und führt sie aus. Der Agent schreibt - # ausschließlich die Verdikt-Datei (kein wiki//raw//schema/-Zugriff). - local vprompt - vprompt=$(mktemp "$ROOT/validator-prompt-XXXXXX.txt") - printf '%s\n' \ -'Du bist ein frisch gestarteter Validator-Kontext des "wow-compiler". Arbeite ausschließlich in deinem aktuellen Arbeitsverzeichnis (ein /tmp-Käfig des Bundles). LIES die Datei schema/validator.md (Rev 9) — sie ist die EINZIGE normative Instruktionsquelle. Führe den vollständigen Validator-Vertrag über ALLE Markdown-Dateien unter wiki/ aus (jede Datei, die wiki/**/*.md entspricht). Ermittle je Datei ein Verdikt: "SUCCESS " oder — bei einer Verletzung — "FAIL ". Schreibe ALLE Verdikte zeilenweise per Write-Tool in die Datei '"$win_path"' (eine Verdikt-Zeile je Datei, exakt das §5-Format, mindestens "SUCCESS " / "FAIL "). Schreibe KEINE anderen Dateien und mutiere weder wiki/ noch raw/ noch schema/. Melde am Ende exakt "VALIDATOR_DONE".' \ - > "$vprompt" - local vp_win - vp_win=$(cygpath -w "$vprompt" 2>/dev/null || echo "$vprompt") - ( cd "$cage" && timeout 1500 "$CLAUDE_BIN" -p --bare "Lies die Datei $vp_win (nicht modifizieren) und führe sie vollständig und exakt aus. Dein Arbeitsverzeichnis ist dieses Verzeichnis." --allowedTools "Read,Write" > "$ROOT/validator-agent.out" 2>&1 ) - return $? +runlabel "D: Validator-Agent (frischer Sub-Agent; Gate prüft nur §5/§5.1, urteilt nie selbst)" +run_validator_agent() { # $1=Käfigpfad, $2=out-pfad + local cdir="$1" out="$2" + local wdir; wdir=$(cygpath -w "$cdir") + ( + cd "$cdir" || exit 1 + timeout 2700 "$CLAUDE_BIN" -p --bare \ + --add-dir "$wdir" \ + --allowedTools "Read Grep Glob" \ + --permission-mode dontAsk \ + --output-format text \ + --max-turns 30 \ + "Du bist der Wow-Validator. LIES zuerst $cdir/schema/validator.md und folge ihr EXAKT. +Validiere dann jede Markdown-Datei unter wiki/ (alle .md, inkl. index.md und log.md) +gegen die dort definierten Regeln und das dortige Verletzungs-Katalog-Register. +WICHTIG: Dein OUTPUT ist KEINE Prosa, sondern NUR eine Liste von Zeilen in der +Validator-Grammatik (validator.md): +- für JEDE validierte wiki/-Datei EXAKT EINE Verdict-Zeile: + SUCCESS ODER FAIL +- ist der relativ zum Käfig-Root gepunktete Pfad (z.B. 'wiki/index.md'). +- Keine weiteren Ausgabezeilen, keine Erklärungen, keine Markdown-Listen-Sterne." \ + > "$out" 2>&1 + echo "VALIDATOR_DONE $cdir" >> "$out" + ) } - -# (Der Agent-Lauf wird in G-2 als harte Assertion eingebunden — hier zunächst der -# Agent-Aufruf selbst; Fehlschlag = harter Non-Zero, Story bleibt offen.) -if ! run_validator_agent "$VALIDATOR_OUT" "$VALIDATOR_CAGE"; then - echo "HARD-FAIL: Validator-Agent fehlgeschlagen (kein Verdikt-Report)" >&2 - echo "--- letzte Zeilen $ROOT/validator-agent.out ---" >&2 - tail -15 "$ROOT/validator-agent.out" >&2 2>/dev/null || true - exit 1 -fi -pass "Validator-Agent (frischer Kontext) hat schema/validator.md ausgeführt (Lauf rc=0)" - -# Verdikt-Datei präsent? -[ -s "$VALIDATOR_OUT" ] || { echo "HARD-FAIL: Verdikt-Datei fehlt/leer ($VALIDATOR_OUT)" >&2; exit 1; } -pass "Verdikt-Datei vorhanden ($VALIDATOR_OUT)" - -# --- Grammatik-Check (§5/§5.1): jede Zeile beginnt mit SUCCESS oder FAIL --- -VERDICT_BAD=0 -VERDICT_LINES=0 -while IFS= read -r line || [ -n "$line" ]; do - [ -z "$line" ] && continue - VERDICT_LINES=$((VERDICT_LINES+1)) - case "$line" in - SUCCESS\ *|FAIL\ *) : ;; - *) echo "FAIL: ungrammatische Verdikt-Zeile: $line" >&2; VERDICT_BAD=$((VERDICT_BAD+1));; - esac -done < "$VALIDATOR_OUT" -[ "$VERDICT_BAD" -eq 0 ] || { echo "HARD-FAIL: $VERDICT_BAD ungrammatische Verdikt-Zeile(n) (§5/§5.1)" >&2; exit 1; } -pass "Verdikt-Grammatik (§5/§5.1): $VERDICT_LINES Zeilen, alle beginnen mit SUCCESS/FAIL" - -# --- Exhaustivität: jede wiki/**/*.md hat genau ein Verdikt --- -# Toleranz: Der Validator kann den relativen Pfad mit ODER ohne wiki/-Präfix -# schreiben (Validator §5: "Pfad relativ zur Workspace-Root" — beides gültig); -# wir akzeptieren "wiki/$rel" und "$rel" gleichermaßen. -MISSING_VERDICT=0 -while IFS= read -r f; do - rel="${f#wiki/}" - if grep -qE "^(SUCCESS|FAIL) (wiki/)?${rel//\//\/}( |$)" "$VALIDATOR_OUT"; then - pass "Verdikt für $rel vorhanden" - else - echo "FAIL: kein Verdikt für wiki/$rel" >&2 - MISSING_VERDICT=$((MISSING_VERDICT+1)) +VAL_A="$ROOT/validator-a.out" +run_validator_agent "$CAGE" "$VAL_A" +grep -q '^VALIDATOR_DONE ' "$VAL_A" || hardfail "Validator-Run: VALIDATOR_DONE-Marker fehlt (Agent-Timeout/Fehler?)" +pass "D.1 Validator-Run abgeschlossen (VALIDATOR_DONE-Marker)" +WIKI_MD_LIST=$(find wiki -type f -name '*.md' | LC_ALL=C sort) +WIKI_MD_COUNT=$(printf '%s\n' "$WIKI_MD_LIST" | grep -c '^') +echo "--- wiki/-Dateiliste ($WIKI_MD_COUNT Dateien) ---" +echo "$WIKI_MD_LIST" +VERDICTS=$(grep -E '^(SUCCESS|FAIL) [^ ]+' "$VAL_A" | head -50) +# P-6: exakt EIN Verdict pro Datei (Pfad-Vorkommen zählen; Toleranz wiki/-Präfix) +DUP_FAIL=0 +while IFS= read -r wmd; do + [ -z "$wmd" ] && continue + rel="${wmd#wiki/}" + n=$(printf '%s\n' "$VERDICTS" | grep -cE "^(SUCCESS|FAIL) (wiki/)?$rel " || true) + if [ "$n" -eq 0 ]; then + fail "D.4 Exhaustivität: KEIN Verdict für $wmd (Defizit-Route, P-7)" + DUP_FAIL=1 + elif [ "$n" -gt 1 ]; then + fail "D.4 Eindeutigkeit: $n Verdicts für $wmd (erwartet exakt 1)" + DUP_FAIL=1 fi -done < "$NEGDIR/wiki/index.md" <<'EOF' +# Index +- [Pos](concepts/pos.md) +- [Neg1](concepts/neg-1.md) +- [Neg2](concepts/neg-2.md) +- [Neg3](concepts/neg-3.md) EOF -[ "$MISSING_VERDICT" -eq 0 ] || { echo "HARD-FAIL: $MISSING_VERDICT wiki-Datei(en) ohne Verdikt (Exhaustivität)" >&2; exit 1; } - -# --- SUCCESS-Urteil zählen (G-3: SUCCESS überall) --- -FAIL_VERDICTS=$(grep -cE '^FAIL ' "$VALIDATOR_OUT" || true) -SUCCESS_VERDICTS=$(grep -cE '^SUCCESS ' "$VALIDATOR_OUT" || true) -echo " Verdikt-Statistik: $SUCCESS_VERDICTS SUCCESS, $FAIL_VERDICTS FAIL (von $WIKI_MD_COUNT wiki-Dateien)" -pass "Validator-Exhaustivität: $WIKI_MD_COUNT wiki-Dateien, genau ein Verdikt je Datei" - -echo -echo "VALIDATOR-AGENT abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" +cat > "$NEGDIR/wiki/log.md" <<'EOF' +# Log +EOF +# neg-1: required Feld 'type' fehlt (Punkt 1) +cat > "$NEGDIR/wiki/concepts/neg-1.md" <<'EOF' +--- +sources: + - resource: raw/neg-1.md + id: s1 +generated: + by: wow-compiler/0.1.0 + at: 2026-08-21T08:00:00Z +--- +Negativ-Fixtur: required Feld 'type' fehlt. +EOF +# neg-2: Bundle ohne okf_version (Punkt 8) +cat > "$NEGDIR/wiki/concepts/neg-2.md" <<'EOF' +--- +okf_version: "0.2" +--- +# Neg-2 (Bundleroot-Defizit: okf_version fehlt auf Bundle-Ebene) +EOF +# neg-3: ungültiges 'at'-Datum (Punkt 11/EC-3: 2026-02-31 existiert nicht) +cat > "$NEGDIR/wiki/concepts/neg-3.md" <<'EOF' +--- +type: concept +sources: + - resource: raw/neg-3.md + id: s1 +generated: + by: wow-compiler/0.1.0 + at: 2026-02-31T10:00:00Z +--- +Negativ-Fixtur: ungültiges Kalenderdatum im 'at'-Feld. +EOF +# pos-1: valid +cat > "$NEGDIR/wiki/concepts/pos.md" <<'EOF' +--- +type: concept +sources: + - resource: raw/pos.md + id: s1 +generated: + by: wow-compiler/0.1.0 + at: 2026-08-21T08:00:00Z +--- +Positiv-Kontrolle: vollständiges, valides Konzept. +EOF +NEGVAL_OUT="$ROOT/validator-neg.out" +run_validator_agent "$NEGDIR" "$NEGVAL_OUT" +grep -q '^VALIDATOR_DONE ' "$NEGVAL_OUT" || hardfail "D-NEG: Validator-Run fehlgeschlagen (kein VALIDATOR_DONE)" +pass "D-NEG.1 Negativ-Validator-Run abgeschlossen" +NEG_VERDICTS=$(grep -E '^(SUCCESS|FAIL) [^ ]+' "$NEGVAL_OUT" | head -50) +neg_count() { printf '%s\n' "$NEG_VERDICTS" | grep -c "^FAIL wiki/concepts/$1 " || true; } +pos_count() { printf '%s\n' "$NEG_VERDICTS" | grep -c "^SUCCESS wiki/concepts/$1 " || true; } +neg1=$(neg_count neg-1.md); neg2=$(neg_count neg-2.md); neg3=$(neg_count neg-3.md) +pos1=$(pos_count pos.md) +[ "$neg1" -ge 1 ] && pass "D-NEG.2 neg-1 (ohne type): FAIL-Verdikt erhalten" \ + || fail "D-NEG.2 neg-1: kein FAIL-Verdikt (AC-3-Hälfte verletzt)" +[ "$neg2" -ge 1 ] && pass "D-NEG.3 neg-2 (ohne okf_version): FAIL-Verdikt erhalten" \ + || fail "D-NEG.3 neg-2: kein FAIL-Verdikt (AC-3-Hälfte verletzt)" +[ "$neg3" -ge 1 ] && pass "D-NEG.4 neg-3 (at: 2026-02-31): FAIL-Verdikt erhalten" \ + || fail "D-NEG.4 neg-3: kein FAIL-Verdikt (AC-3-Hälfte verletzt)" +[ "$pos1" -ge 1 ] && pass "D-NEG.5 pos (valid): SUCCESS-Verdikt erhalten (Positiv-Kontrolle)" \ + || fail "D-NEG.5 pos: kein SUCCESS-Verdikt (Positiv-Kontrolle gescheitert)" # ============================================================================== # E. ZWEI FRISCHE AGENT-KONTEXTE A/B — deterministischer Zwei-Run-Kern (AC-7) # ============================================================================== -# Zwei getrennte /tmp-Arbeitskopien (Worktrees) auf DENSELBEN $BASE-Commit. Beide -# Agent-Kontexte führen schema/compiler.md als EINZIGE normative Quelle aus -# (§5.14 Pkt. 2). Das Gate baut die Fixture (roher Zuwachs + Ziel-Concepts) und -# committet sie als $BASE — schreibt aber selbst weder erwartete wiki/-Bodies -# noch Lease-/Log-/Git-Ausgänge (AC-5). A vs B: nur die benannte at-Ausnahme -# (generated.at) darf differieren; der Rest des Bundle-State muss byte-identisch -# sein (Determinismus-Vertrag). Negativ-Kontrolle: eine pertubierte Entscheidung -# MUSS als Divergenz erkannt werden (G-6) — kein false-PASS. -runlabel "E. ZWEI FRISCHE AGENT-KONTEXTE A/B (AC-7) — deterministischer Zwei-Run-Kern" +runlabel "E. ZWEI FRISCHE AGENT-KONTEXTE A/B (AC-7)" -# --- E.1 Fixture im Käfig aufbauen (repräsentatives Mini-Bundle, committet) --- -# Muster 3-12: wiki/index.md + alpha.md + gamma.md + log.md, raw/alpha-v1.md als -# Baseline, Zuwachs raw/alpha-v2.md (bestätigende Evidenz, §5.16 Pkt. 4) → Update -# auf alpha.md. gamma.md = unabhängiges Thema, bleibt byte-identisch (AC-6). +# --- E.1 Fixture im Käfig (D-6: UPDATE alpha + CREATE beta; Bundle-Frontmatter P-4) --- +# alpha: bestätigender Zuwachs raw/alpha-v2.md (S-3, gleiche Aussage wie alpha-v1#S-2) +# beta: EINDEUTIGER Term quanten-observatorium-kanal — kein bestehendes Concept +# deckt ihn ab ⇒ Routing CREATE (neues Concept + Index-Link, Punkt 11/Anlage). +# Der Ziel-Pfad (Slug) wird vom Producer aus dem Term abgeleitet (§5.15/§5.7) — +# das Gate HARDCODIERT ihn NICHT (D-1: keine erwarteten Plan-Literale); die +# Witness detektiert die neue Datei über Git und prüft deren SEMANTIK. +# gamma: unabhängig, bleibt byte-identisch (AC-6) +rm -rf "$CAGE/wiki" "$CAGE/raw" # E.1 baut die Fixture sauber neu (keine §B-Reste in $BASE) mkdir -p "$CAGE/wiki" "$CAGE/raw" cat > "$CAGE/wiki/index.md" <<'FIXTURE' +--- +type: bundle +okf_version: "0.2" +--- # Index - [Alpha](alpha.md) - [Gamma](gamma.md) @@ -373,85 +414,65 @@ cat > "$CAGE/raw/gamma-v1.md" <<'FIXTURE' ### S-1 Evidenz v1: Gamma-Thema. FIXTURE -[ -f "$CAGE/wiki/index.md" ] && [ -f "$CAGE/raw/alpha-v2.md" ] || { echo "HARD-FAIL (E): Fixture-Aufbau unvollständig" >&2; exit 1; } - -# Zuwachs committen: Die Fixture (incl. raw/alpha-v2.md) wird als NEUER -# Baseline-Commit in den Käfig committet. Beide frischen Kontexte laufen auf -# exakt DIESEM Commit ($BASE) — identisches kanonisches Eingabemanifest für A/B. -( cd "$CAGE" && git add -A && git commit -qm "Käfig-Fixture: Baseline inkl. Zuwachs raw/alpha-v2.md" ) +cat > "$CAGE/raw/beta-v1.md" <<'FIXTURE' +### S-1 +Beta: quanten-observatorium-kanal etabliert die Baseline. +FIXTURE +cat > "$CAGE/raw/beta-v2.md" <<'FIXTURE' +### S-2 +Beta: quanten-observatorium-kanal bestätigt die Baseline. +FIXTURE +# Term-Isolations-Garantie (Fixture-Setup, kein Run-Zustand): der beta-Term darf +# in KEINER Baseline-Datei vorkommen — sonst wäre CREATE unscharf (Kandidat alpha). +if grep -rq 'quanten-observatorium-kanal' "$CAGE/wiki" "$CAGE/raw/alpha-v1.md" "$CAGE/raw/alpha-v2.md" "$CAGE/raw/gamma-v1.md"; then + echo "HARD-FAIL (E.1): beta-Term taucht in Baseline-Dateien auf (CREATE-Isolation verletzt)" >&2 + exit 1 +fi +pass "E.1 Fixture: Mini-Bundle (index+alpha+gamma+log) + Zuwachs alpha-v2/beta-v1/beta-v2, term-isoliert" +( cd "$CAGE" && git add -A && git commit -qm "Käfig-Fixture: Baseline inkl. Zuwachs" ) \ + || { echo "HARD-FAIL (E.1): Fixture-Commit fehlgeschlagen" >&2; exit 1; } BASE=$(git -C "$CAGE" rev-parse HEAD) -[ -n "$BASE" ] || { echo "HARD-FAIL (E): Baseline leer" >&2; exit 1; } -git -C "$CAGE" cat-file -e "$BASE^{commit}" 2>/dev/null \ - || { echo "HARD-FAIL (E): Fixture-Baseline nicht verifiziert" >&2; exit 1; } -# Witness-Baseline (vor Konsolidierung): alpha.md trägt im Baseline-Commit die -# EINFACHE Verankerung (raw/alpha-v1.md#S-2) — der Zuwachs raw/alpha-v2.md wird -# erst durch den Compilation Run als Multi-Beleg ergänzt. -pass "Fixture als \$BASE committet (\$BASE=$BASE, Zuwachs raw/alpha-v2.md im Baseline)" +[ -n "$BASE" ] && git -C "$CAGE" cat-file -e "$BASE^{commit}" 2>/dev/null \ + || { echo "HARD-FAIL (E.1): Fixture-Baseline nicht verifiziert" >&2; exit 1; } +pass "E.2 Fixture als \$BASE committet (\$BASE=$BASE)" -# --- E.2 Zwei getrennte Worktrees auf $BASE anlegen --- +# --- E.3 Zwei getrennte Worktrees auf $BASE + frische Agent-Kontexte --- git -C "$CAGE" worktree prune -git -C "$CAGE" worktree add -q "$ROOT/wt-a" "$BASE" || { echo "HARD-FAIL (E): Worktree wt-a nicht aufgebaut" >&2; exit 1; } -git -C "$CAGE" worktree add -q "$ROOT/wt-b" "$BASE" || { echo "HARD-FAIL (E): Worktree wt-b nicht aufgebaut" >&2; exit 1; } -pass "Zwei getrennte Worktrees wt-a/wt-b auf \$BASE (\$BASE=$BASE)" +git -C "$CAGE" worktree add -q "$ROOT/wt-a" "$BASE" \ + || { echo "HARD-FAIL (E.3): Worktree wt-a nicht aufgebaut" >&2; exit 1; } +git -C "$CAGE" worktree add -q "$ROOT/wt-b" "$BASE" \ + || { echo "HARD-FAIL (E.3): Worktree wt-b nicht aufgebaut" >&2; exit 1; } +pass "E.3 Worktrees wt-a/wt-b auf \$BASE angelegt" -# --- E.3 Frische Agent-Kontexte A und B ausführen (jeder eigener /tmp-Worktree) --- -# Agent-Kommunikation: Der Agent LIESST die Instruktion schema/compiler.md und -# die Fixture im Worktree, WRITE editiert wiki/-Dateien. Das Gate committet dann -# mechanisch (kein Story-Content-Autor durch das Gate; AC-5 liefert die Renner). -# Das Prompt ist für A und B byte-identisch bis auf den Run-Namen. -# (Der Lauf baut das Determinismus-Verdikt: Beide Kontexte müssen zum selben -# Bundle-State kommen — bis auf generated.at.) -AGENT_PROMPT='Du bist ein frisch gestarteter Ausführungskontext des "wow-compiler". Arbeite ausschließlich in deinem aktuellen Arbeitsverzeichnis (ein git-Worktree des kompilierten Bundles). Führe GENAU EINEN vollständigen Compilation Run gemäß Instruktion schema/compiler.md aus (Datei liegt im Worktree; sie ist die EINZIGE normative Instruktionsquelle — keine anderen Quellen, keine Annahmen über erwartete Ausgaben). +# D-1: AGENT_PROMPT = NUR Instruktion + Fixture-Fakten. Keine erwarteten +# Plan-/Entscheidungs-/Log-Literale, keine vorgegebenen Receipt-Werte +# (Gate-Checks sind A-vs-B + Nicht-Vakuum-Witness gegen Baseline). +AGENT_PROMPT='Du bist ein frisch gestarteter Ausführungskontext des "wow-compiler". Arbeite ausschließlich in deinem aktuellen Arbeitsverzeichnis (ein git-Worktree des Bundles). LIES schema/compiler.md im Worktree — sie ist die EINZIGE normative Instruktionsquelle. Führe GENAU EINEN vollständigen Compilation Run nach ihr aus (alle Phasen, inkl. Validierungsphase gegen schema/validator.md). Keine anderen Quellen, keine Annahmen über erwartete Ausgaben. -Die committete Fixture in diesem Worktree enthält: -- wiki/index.md (Bundleroot), wiki/log.md (leer, nur "# Log"), wiki/gamma.md (unabhängiges Thema, NICHT betroffen), wiki/alpha.md (bestehendes Concept). -- raw/alpha-v1.md (Baseline-Evidenz), raw/gamma-v1.md, raw/alpha-v2.md (neu committeter Zuwachs, Stelle S-3). +Fakten zur committeten Fixture in diesem Worktree (ausschließlich Fakten, keine Wertevorgaben): +- wiki/index.md (Bundleroot), wiki/log.md, wiki/alpha.md und wiki/gamma.md sind die bisherigen Concepts. +- raw/alpha-v1.md und raw/gamma-v1.md sind Baseline-Evidenz. +- raw/alpha-v2.md ist ein neu committeter Zuwachs (Stelle S-3); er enthält dieselbe belegte Aussage wie raw/alpha-v1.md#S-2. +- raw/beta-v1.md und raw/beta-v2.md sind neu committete Zuwächse (Stellen S-1/S-2) mit einem eindeutigen Term, für den kein bestehendes Concept existiert. -Die neue committete Evidenz raw/alpha-v2.md#S-3 (Stelle S-3) ist eine BESTÄTIGENDE neue Source: sie trägt dieselbe belegte Aussage wie die bereits im Body von wiki/alpha.md vorhandene, belegte Aussage mit Anker raw/alpha-v1.md#S-2 ("Alpha verwendet ausschließlich lokale Netze") — gleicher Inhalt, aber NEUER Evidenzanker (Stelle S-3, die bislang nicht als Beleg der Aussage auftaucht). - -Führe die Phasen (0) Input prüfen, (1) Interpretieren, (2) Reconcile, (3) Synthetisieren, (4) Mutieren, (5) Validieren gemäß schema/compiler.md aus. Der Zuwachs (alpha-v2) löst gemäß §5.9/§5.16-Pkt.-4 (CONFIRMING-Konsolidierung, §5.10-Pkt.-3-Multi-Beleg) ein Update auf wiki/alpha.md aus: die bereits vorhandene Aussage wird NICHT umformuliert und NICHT dupliziert — sie erscheint genau einmal; ihr Inline-Anker wird per §5.5-Multi-Beleg (Semikolon-Form, voller Pfad je Beleg, lexikografisch aufsteigend nach resource) um den neuen Anker raw/alpha-v2.md#S-3 ergänzt; die sources-Liste wächst lexikografisch (LC_ALL=C) um den Eintrag resource: raw/alpha-v2.md mit neuem id. generated.at = aktueller Run-Zeitstempel (ISO-8601-Datetime UTC, §4-Pkt.-3-Konvention). generated.by = wow-compiler/0.1.0. Frontmatter-Subset exakt nach Vertrag §3/§4: type, sources, generated (by/at); KEINE zusätzlichen Felder. - -Bestehende belegte Aussagen, §5.5-Inline-Verweise, sources-Einträge und §5.6-Links bleiben byte-identisch erhalten (Erhaltungs-Invariante §5.9 Pkt. 5 / §5.16 Pkt. 3). gamma.md und index.md bleiben UNVERÄNDERT (reines Body-Update, §5.9 Pkt. 3; kein neuer Index-Link). - -log.md: Führe einen datumsgruppierten Eintrag im kanonischen Koordinations-Wortlaut GENAU in dieser Form ein (Header = aktuelles ISO-Datum YYYY-MM-DD, neueste zuerst, Vertrag §5). Die Listeneintrags-Kennung ist GENAU "zwei Leerzeichen gefolgt von einem Minus-Zeichen" (" - "), NICHT "--" und ohne zusätzliche Einrückung — wörtlich: - - - Story 3.1-Update: alpha (wiki/alpha.md), Quelle raw/alpha-v2.md, Baseline-Commit - -(die drei rechts stehenden Werte kommen aus dem committeten Zustand; das ist der SHA des Worktree-HEAD/Baseline-Commit, den du mit git rev-parse HEAD auflösen kannst — verwende den vollen SHA, KEINE Abkürzung; KEINE weiteren Zeilen oder freie Formulierung in diesem Eintrag). Bestehender log.md-Inhalt bleibt unverändert. - -Validierungsphase (§6): prüfe ALLE wiki-Dateien gegen schema/validator.md; alle müssen SUCCESS liefern. - -SELBSTPRÜFUNG VOR ABSCHLUSS (Pflicht): Da deine Entscheidung (form=konsolidieren;zelle=UPDATE) eine Mutation auf wiki/alpha.md UND wiki/log.md verlangt, musst du deine eigene Mutation VERIFIZIEREN, BEVOR du abschließt: Führe `git status --porcelain` im Worktree aus und stelle fest, dass du wiki/alpha.md und wiki/log.md tatsächlich GEÄNDERT hast (Status " M wiki/alpha.md" und " M wiki/log.md" — NICHT unverändert). Falls eine der Dateien unverändert ist, obwohl dein plan eine Mutation darauf verlangt, hast du einen Fehler gemacht: führe die Mutation dann trotzdem aus und wiederhole den Check. Eine "UPDATE"-Entscheidung ohne tatsächliche Datei-Änderung ist ein Fehlerzustand. (run-receipt.txt bleibt untracked — das ist korrekt.) - -Erstelle im Worktree-Root eine Datei run-receipt.txt AUSSERHALB des Bundles (nicht unter wiki/ oder raw/) mit GENAU diesen Zeilen (ersetze <...> durch echte Werte): +Mutationen (wiki/-Concepts, index.md, log.md) UND die Receipt-Datei run-receipt.txt (Worktree-Root, AUSSERHALB von wiki/ und raw/) sind EXAKT nach den Konventionen von schema/compiler.md auszuführen — keine vorgegebenen Werte. Die run-receipt.txt trägt diese Felder (Werte aus deinem Lauf): baseline: - candidates: alpha - decision: form=konsolidieren;zelle=UPDATE;ziel=alpha - sources_added: raw/alpha-v2.md + candidates: + decision: + sources_added: -Commite NICHT selbst (der Harness committet deine Mutationen mechanisch nach deinem Lauf — Commit-Boundary = Mutations-Boundary; das Vermeidet Editor-/Interaktiv-Hangs des Agenten auf MSYS/Windows). Das run-receipt.txt bleibt für den Harness uncommittet. Schließe deinen Lauf ab mit der Auswertung der Validierungsphase und melde am Ende EXAKT "RUN_DONE ".' +Commite NICHT selbst (der Harness committiert deine Mutationen mechanisch; run-receipt.txt bleibt uncommittet). Schließe den Lauf mit der Validierungsphase ab und melde am Ende EXAKT "RUN_DONE ", wobei der dir mitgegebene Run-Name ist.' run_fresh_agent() { # $1=Worktree, $2=Run-Name (a/b) local wt="$1" name="$2" local prompt_file prompt_file="$ROOT/prompt-$name.txt" - cat > "$prompt_file" < und führe sie aus", wie in - # run_one.sh verifiziert); --allowedTools begrenzt den frischen Kontext. + printf '%s\n' "$AGENT_PROMPT" > "$prompt_file" local win_prompt win_prompt=$(cygpath -w "$prompt_file" 2>/dev/null || echo "$prompt_file") - # Timeout großzügig: Lauf A/B durchläuft (0)-(5) über schema/compiler.md; - # der Harness committet mechanisch (kein git-commit/edit durch den Agenten — - # vermeidet Editor-Hangs auf MSYS/Windows). Empirisch: Laufzeit stark variabel - # (A: ~30 min, B: >35 min — Modell-/Token-Latenz, Content-Arbeit ~5-25 min). - # 2700s = 45 min pro Lauf (Varianz-Puffer); deterministisch = korrekt > schnell. - ( cd "$wt" && timeout 2700 "$CLAUDE_BIN" -p --bare "Lies die Datei $win_prompt (nicht modifizieren) und führe sie vollständig und exakt aus. Dein Arbeitsverzeichnis ist dieses Worktree." --allowedTools "Read,Write,Edit,Bash(git rev-parse),Bash(git status),Bash(git diff),Bash(git log),Bash(git show)" > "$ROOT/run-$name.out" 2>&1 ) + ( cd "$wt" && timeout 2700 "$CLAUDE_BIN" -p --bare "Run-Name: $name — Lies die Datei $win_prompt (nicht modifizieren) und führe sie vollständig und exakt aus. Dein Arbeitsverzeichnis ist dieses Worktree." --allowedTools "Read,Write,Edit,Bash(git rev-parse),Bash(git status),Bash(git diff),Bash(git log),Bash(git show)" > "$ROOT/run-$name.out" 2>&1 ) local rc=$? - echo " Agent-$name: rc=$rc" >&2 - RUN_AGENT_RC=$rc # global (inverted-$?-Problem in if ! f ausgleichen) + RUN_AGENT_RC=$rc return $rc } @@ -461,152 +482,186 @@ if ! run_fresh_agent "$ROOT/wt-a" a; then tail -20 "$ROOT/run-a.out" >&2 2>/dev/null || true exit 1 fi +grep -q '^RUN_DONE a$' "$ROOT/run-a.out" || { echo "HARD-FAIL (E.3): Lauf A ohne RUN_DONE-Marker" >&2; tail -10 "$ROOT/run-a.out" >&2; exit 1; } +pass "Lauf A abgeschlossen (RUN_DONE-Marker vorhanden)" echo "--- Lauf B (frischer Agent-Kontext, Worktree wt-b) ---" if ! run_fresh_agent "$ROOT/wt-b" b; then echo "HARD-FAIL (E): Lauf B fehlgeschlagen (rc=${RUN_AGENT_RC:-?})" >&2 tail -20 "$ROOT/run-b.out" >&2 2>/dev/null || true exit 1 fi -pass "Beide frischen Agent-Kontexte (A/B) haben $BASE-Verarbeitung abgeschlossen" +grep -q '^RUN_DONE b$' "$ROOT/run-b.out" || { echo "HARD-FAIL (E.3): Lauf B ohne RUN_DONE-Marker" >&2; tail -10 "$ROOT/run-b.out" >&2; exit 1; } +pass "Lauf B abgeschlossen (RUN_DONE-Marker vorhanden)" -# Die Agenten schreiben Dateien, committen aber NICHT (Harness-Design): -# Daher führen wir den Mutations-Commit MECHANISCH im Gate aus (kein -# Content-Autor — das Gate schreibt keine erwarteten Bodies; es persistiert -# nur den von den Agenten erzeugten Bundle-State; AC-5 bleibt gewahrt). -# Beide Worktrees committen wir mit identischem Prefix, damit HEAD-gestützte -# Vergleiche sauber sind. Der jeweilige Commit erfasst exakt die Agenten-Mutationen. -# ACHTUNG: run-receipt.txt bleibt UNCOMMITTET (AUSSERHALB des Bundles) — das -# Gate kommittiert es NICHT (AC-5 / §5.14; die Receipts liegen außerhalb wiki/raw/). +# --- Mechanischer Mutations-Commit (Gate ist kein Content-Autor; AC-5) --- +# P-10: danach MUSS raw/ unverändert sein (Immutabilität des read-only Vorbilds) commit_worktree() { # $1=Worktree, $2=Name local wt="$1" name="$2" - # Nur wiki/ + raw/ stagen (Bundle); run-receipt.txt (außerhalb) bleibt uncommittet. - ( cd "$wt" \ - && git add wiki raw \ + ( cd "$wt" && git add wiki raw \ && if git diff --cached --quiet; then echo " ($name) keine Mutationen staged — kein Commit nötig" else - git commit -qm "Run: alpha-Konsolidierung ($name)" - fi ) - return $? + git commit -qm "Run: Compilation ($name)" + fi ) \ + || { echo "HARD-FAIL (E): Commit $name fehlgeschlagen" >&2; return 1; } + local rawstat + rawstat=$(git -C "$wt" diff "$BASE" HEAD --stat -- raw/ 2>/dev/null || true) + if [ -n "$rawstat" ]; then + echo "HARD-FAIL (E, P-10): raw/ in $name mutiert (Immutabilität verletzt):" >&2 + printf '%s\n' "$rawstat" >&2 + return 1 + fi + return 0 } -commit_worktree "$ROOT/wt-a" a || { echo "HARD-FAIL (E): Commit A fehlgeschlagen" >&2; exit 1; } -commit_worktree "$ROOT/wt-b" b || { echo "HARD-FAIL (E): Commit B fehlgeschlagen" >&2; exit 1; } -pass "Bundle-State beider Worktrees committet (Run-Ende; run-receipt.txt uncommittet)" +commit_worktree "$ROOT/wt-a" a || exit 1 +commit_worktree "$ROOT/wt-b" b || exit 1 +pass "Bundle-State beider Worktrees committet (raw/ unverändert, P-10; run-receipt.txt uncommittet)" -# --- E.4 A/B-Vergleich: Bundle-State bis auf at byte-identisch (§5.14 Pkt. 2) --- -# Beide Worktrees teilen das gemeinsame Käfig-Repo. Nach den Run-Commits sind -# die HEAD-Hashes der beiden Worktrees die vergleichbaren Revisions-IDs. -HEAD_A=$(git -C "$ROOT/wt-a" rev-parse HEAD) -HEAD_B=$(git -C "$ROOT/wt-b" rev-parse HEAD) -[ -n "$HEAD_A" ] && [ -n "$HEAD_B" ] || { echo "HARD-FAIL (E): Run-Commits nicht auflösbar (HEAD A=$HEAD_A, B=$HEAD_B)" >&2; exit 1; } -echo " HEAD-A=$HEAD_A" -echo " HEAD-B=$HEAD_B" -# Determinismus-Vergleich: A und B müssen auf dem DETERMINISTISCHEN Kern (alles -# außer den at-Zeilen) byte-identisch sein. Nur die at-Zeilen dürfen abweichen -# (§5.14 Pkt. 2: "diff ... nur die maskierte at-Zeile"; Allowance, keine -# Pflicht-Differenz). Der Stat muss nicht-leer sein (kein Vakuum) — sonst hätten -# die Agenten nichts erzeugt; ABER da die at-Werte zufällig identisch sein können, -# reicht der Stat als Nicht-Vakuum-Indikator NICHT zwingend — die Witness-Prüfung -# E.5 (Gegensatz zu Baseline) stellt das Nicht-Vakuum sicher. -AB_DIFF_FILE="$ROOT/ab-wiki.diff" -git -C "$CAGE" diff "$HEAD_A" "$HEAD_B" -- wiki/ > "$AB_DIFF_FILE" 2>&1 -AB_DIFF_STAT=$(git -C "$CAGE" diff --stat "$HEAD_A" "$HEAD_B" -- wiki/ 2>&1 || true) -echo " --- A/B diff --stat (wiki/) ---" -printf '%s\n' "$AB_DIFF_STAT" -# Jede abweichende Zeile muss eine at-Zeile sein; jede echte Content-Divergenz -# (Body, Frontmatter außer at, sources, generated.by) ist ein Determinismus-Defekt. -NON_AT=0 -AT_DIFFS=0 -for f in "$AB_DIFF_FILE"; do - [ -s "$f" ] || continue - # abweichende Zeilen: entferne Kontext-/Header-/Metadaten-Zeilen (diff-Format), - # nur echte +/- Inhaltszeilen zählen +# --- E.4 A/B-Vergleich: nur-at-Ausnahme (§5.14 Pkt. 2) — P-2: geankerter Classifier --- +# classify_diff $1=Diff-Datei: echo "NON_AT= AT=" +# at-Zeilen = Frontmatter-geankert (genau zwei führende Leerzeichen nach +/-). +# Der alte un-geankerte *"at: "*-Treffer (Body-Zeilen mit 'at: ') ist behoben. +classify_diff() { + local f="$1" non_at=0 atn=0 line + if [ ! -s "$f" ]; then echo "NON_AT=0 AT=0"; return; fi while IFS= read -r line || [ -n "$line" ]; do case "$line" in +*|-*) : ;; *) continue ;; esac case "$line" in - +++*|---*) continue ;; # Datei-Header - *"at: "*) AT_DIFFS=$((AT_DIFFS+1)) ;; # at-Zeile (auch +/ - mit führendem Leer) - *) NON_AT=$((NON_AT+1)) ;; + +++*|---*) continue ;; + "+ at: "*|"- at: "*) atn=$((atn+1)) ;; + *) non_at=$((non_at+1)) ;; esac done < "$f" -done -echo " A/B-Diff: $AT_DIFFS at-Zeilen vom Typ ' at: ...', $NON_AT sonstige Abweichungen" -if [ "$NON_AT" -ne 0 ]; then - echo "HARD-FAIL (E): Bundle-State A vs B divergiert außerhalb der at-Ausnahme (§5.14 Pkt. 2):" >&2 + echo "NON_AT=$non_at AT=$atn" +} +HEAD_A=$(git -C "$ROOT/wt-a" rev-parse HEAD) +HEAD_B=$(git -C "$ROOT/wt-b" rev-parse HEAD) +[ -n "$HEAD_A" ] && [ -n "$HEAD_B" ] \ + || { echo "HARD-FAIL (E.4): Run-Commits nicht auflösbar (A=$HEAD_A, B=$HEAD_B)" >&2; exit 1; } +echo " HEAD-A=$HEAD_A" +echo " HEAD-B=$HEAD_B" +AB_DIFF_FILE="$ROOT/ab-wiki.diff" +git -C "$CAGE" diff "$HEAD_A" "$HEAD_B" -- wiki/ > "$AB_DIFF_FILE" 2>&1 +AB_DIFF_STAT=$(git -C "$CAGE" diff --stat "$HEAD_A" "$HEAD_B" -- wiki/ 2>&1 || true) +echo " --- A/B diff --stat (wiki/) ---" +printf '%s\n' "$AB_DIFF_STAT" +AB_CLASS=$(classify_diff "$AB_DIFF_FILE") +echo " A/B-Classifier: $AB_CLASS" +AB_NON_AT=$(printf '%s' "$AB_CLASS" | sed -n 's/.*NON_AT=\([0-9]*\).*/\1/p') +if [ "$AB_NON_AT" != "0" ]; then + echo "HARD-FAIL (E.4): Bundle-State A vs B divergiert außerhalb der at-Ausnahme (§5.14 Pkt. 2):" >&2 cat "$AB_DIFF_FILE" >&2 exit 1 fi -# Der Diff kann leer sein, wenn beide Runs konvergieren (auch bei gleichem at — -# Allowance, keine Pflicht-Differenz). Das Nicht-Vakuum beweist E.5 (Witness -# gegen Baseline): Beide Runs MÜSSEN alpha.md real verarbeitet haben. -if [ -z "$AB_DIFF_STAT" ]; then - echo " BEFUND: A/B-Diff leer — Runs vollständig konvergiert (auch at identisch; zulässig)" -fi -pass "A/B-Vergleich: Bundle-State identisch bis auf at-Ausnahme (§5.14 Pkt. 2 / G-1)" +[ -z "$AB_DIFF_STAT" ] && echo " BEFUND: A/B-Diff leer — Runs vollständig konvergiert (zulässig)" +pass "E.4 A/B-Vergleich: Bundle-State identisch bis auf at-Ausnahme (§5.14 Pkt. 2 / G-1)" -# --- E.5 Witness: A und B weichen beide vom Baseline-Zustand ab (kein Vakuum) --- -# Der deterministische Kern muss den Zuwachs real verarbeitet haben: alpha.md -# trägt nachher den Multi-Beleg (raw/alpha-v1.md#S-2; raw/alpha-v2.md#S-3). -# Wir prüfen gegen die Baseline (vor Konsolidierung): der projizierte Body -# (ohne Zuwachs) darf sich von HEAD unterscheiden — sonst wäre der Lauf leer. +# --- E.5 Witness: Nicht-Vakuum gegen Baseline (UPDATE alpha + CREATE beta) --- +# alpha (UPDATE): Multi-Beleg-Anker raw/alpha-v2.md#S-3 + sources-Konsolidierung. ABS_A=$(git -C "$CAGE" show "$BASE:wiki/alpha.md" | sha256sum | cut -d' ' -f1) -SHA_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1) -SHA_B=$(git -C "$ROOT/wt-b" show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1) -[ -n "$ABS_A" ] && [ -n "$SHA_A" ] && [ -n "$SHA_B" ] || { echo "HARD-FAIL (E): Witness-Hashes leer (Vakuum)" >&2; exit 1; } -[ "$ABS_A" != "$SHA_A" ] || { echo "HARD-FAIL (E): Witness A == Baseline (Vakuum — Zuwachs nicht verarbeitet)" >&2; exit 1; } -[ "$ABS_A" != "$SHA_B" ] || { echo "HARD-FAIL (E): Witness B == Baseline (Vakuum — Zuwachs nicht verarbeitet)" >&2; exit 1; } -# Witness muss den Multi-Beleg-Beleg enthalten (wir erwarten in beidem den -# Zuwachs-Anker raw/alpha-v2.md#S-3 — Substanz, keine Harness-Erwartung): -if ! git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | grep -q 'raw/alpha-v2.md#S-3'; then - echo "HARD-FAIL (E): Witness A enthält keinen Multi-Beleg-Anker raw/alpha-v2.md#S-3" >&2 - exit 1 -fi -if ! git -C "$ROOT/wt-b" show 'HEAD:wiki/alpha.md' | grep -q 'raw/alpha-v2.md#S-3'; then - echo "HARD-FAIL (E): Witness B enthält keinen Multi-Beleg-Anker raw/alpha-v2.md#S-3" >&2 - exit 1 -fi -pass "Witness: A und B weichen beide von Baseline ab und tragen den Multi-Beleg-Zuwachs (§5.16 Pkt. 4)" +for wt in a b; do + SHA_W=$(git -C "$ROOT/wt-$wt" show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1) + [ "$ABS_A" != "$SHA_W" ] \ + || { echo "HARD-FAIL (E.5): Witness $wt alpha == Baseline (Vakuum — alpha-Zuwachs nicht verarbeitet)" >&2; exit 1; } + git -C "$ROOT/wt-$wt" show 'HEAD:wiki/alpha.md' | grep -q 'raw/alpha-v2.md#S-3' \ + || { echo "HARD-FAIL (E.5): Witness $wt alpha ohne Multi-Beleg-Anker raw/alpha-v2.md#S-3" >&2; exit 1; } +done +# beta (CREATE): die neue Concept-Datei wird GIBASES-DIFF-gedetectet (nie +# Slug-hartcodiert — §5.15 leitet den Pfad aus dem Term ab; D-1: kein Plan-Literal). +# Erwartet: exakt EINE neue wiki/-Datei (außer log.md/index.md, die M sind nicht A). +NEW_CONCEPT_A=$(git -C "$CAGE" diff --name-only --diff-filter=A "$BASE" "$HEAD_A" -- wiki/ | grep -vE '(^|/)log\.md$' | grep -vE '(^|/)index\.md$' | LC_ALL=C sort) +NEW_CONCEPT_B=$(git -C "$CAGE" diff --name-only --diff-filter=A "$BASE" "$HEAD_B" -- wiki/ | grep -vE '(^|/)log\.md$' | grep -vE '(^|/)index\.md$' | LC_ALL=C sort) +NEW_A_COUNT=$(printf '%s\n' "$NEW_CONCEPT_A" | grep -c '^' || true) +[ "$NEW_A_COUNT" -eq 1 ] \ + || { echo "HARD-FAIL (E.5): CREATE A — erwartet exakt 1 neue Concept-Datei, sind: $NEW_A_COUNT" >&2; printf '%s\n' "$NEW_CONCEPT_A" >&2; exit 1; } +# A und B müssen dieselbe neue Datei erzeugen (Determinismus der CREATE-Zuordnung) +[ "$NEW_CONCEPT_A" = "$NEW_CONCEPT_B" ] \ + || { echo "HARD-FAIL (E.5): CREATE-Pfad divergiert A($NEW_CONCEPT_A) vs B($NEW_CONCEPT_B) — Determinismus-Verstoß" >&2; exit 1; } +NEW_CONCEPT="$NEW_CONCEPT_A" +NEW_LEAF=$(basename "$NEW_CONCEPT") +echo " CREATE-Zieldatei (Producer-geleitet, §5.15): $NEW_CONCEPT" +for wt in a b; do + git -C "$ROOT/wt-$wt" show "HEAD:$NEW_CONCEPT" | grep -q 'resource: raw/beta-v1.md' \ + || { echo "HARD-FAIL (E.5): CREATE $wt — $NEW_CONCEPT sources ohne raw/beta-v1.md" >&2; exit 1; } + git -C "$ROOT/wt-$wt" show "HEAD:$NEW_CONCEPT" | grep -q 'resource: raw/beta-v2.md' \ + || { echo "HARD-FAIL (E.5): CREATE $wt — $NEW_CONCEPT sources ohne raw/beta-v2.md" >&2; exit 1; } + git -C "$ROOT/wt-$wt" show "HEAD:$NEW_CONCEPT" | grep -q 'quanten-observatorium-kanal' \ + || { echo "HARD-FAIL (E.5): CREATE $wt — $NEW_CONCEPT trägt nicht den beta-Term (falsches Concept)" >&2; exit 1; } + # Index-Verlinkung (Punkt 11/Anlage): die Bundleroot verlinkt die neue Datei. + git -C "$ROOT/wt-$wt" show 'HEAD:wiki/index.md' | grep -qF "$NEW_LEAF" \ + || { echo "HARD-FAIL (E.5): CREATE $wt — index.md verlinkt die neue Concept-Datei $NEW_LEAF nicht (Punkt 11)" >&2; exit 1; } +done +pass "E.5 Witness: alpha UPDATE (Multi-Beleg) + beta CREATE (neue Datei, sources, Term, Index-Link) in A und B" -# --- E.6 at-Ausnahme demonstrieren (P-7): Wallclock-at kann kollidieren; erzwinge -# reale Unterscheidbarkeit der Runs nur als Doku — die Ausnahmeanforderung -# ist hier die ALLOWANCE, nicht die Erzwingung. Beide at-Werte extrahieren;--- -AT_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | sed -n 's/^ at: //p') -AT_B=$(git -C "$ROOT/wt-b" show 'HEAD:wiki/alpha.md' | sed -n 's/^ at: //p') -if [ -n "$AT_A" ] && [ -n "$AT_B" ]; then - if [ "$AT_A" = "$AT_B" ]; then - echo " BEFUND: at-Werte identisch ($AT_A) — zulässig (Ausnahme ist eine Allowance, keine Pflicht-Differenz)" - else - echo " BEFUND: at-Werte verschieden (A=$AT_A, B=$AT_B) — Ausnahme aktiv demonstriert" - fi +# --- E.6 Run-Receipt A-vs-B (D-2: Modell sandbox-3-8 DET-2, keine erwarteten Literale) --- +RA="$ROOT/wt-a/run-receipt.txt" +RB="$ROOT/wt-b/run-receipt.txt" +[ -s "$RA" ] || { echo "HARD-FAIL (E.6): run-receipt.txt A fehlt/leer" >&2; exit 1; } +[ -s "$RB" ] || { echo "HARD-FAIL (E.6): run-receipt.txt B fehlt/leer" >&2; exit 1; } +# Normalisierung: at-Cellen = Zeilen mit ISO-8601-Timestamp (Filter), Worktree- +# spezifische Pfade auf WT normalisiert. Danach MUSS der Rest byte-identisch sein. +receipt_norm() { + sed -E "/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|\+00:00)/d" "$1" \ + | sed "s|$ROOT/wt-a|WT|g; s|$ROOT/wt-b|WT|g" +} +receipt_norm "$RA" > "$ROOT/receipt-a.norm" +receipt_norm "$RB" > "$ROOT/receipt-b.norm" +echo " --- run-receipt.txt A (normalisiert) ---" +cat "$ROOT/receipt-a.norm" +if cmp -s "$ROOT/receipt-a.norm" "$ROOT/receipt-b.norm"; then + pass "E.6 Run-Receipt A==B byte-identisch (nach at-/Pfad-Filter, D-2)" else - echo "FAIL: at-Wert fehlt in mindestens einem Run-Output" >&2 + echo "HARD-FAIL (E.6): Run-Receipts A/B divergieren (Determinismus-Vertrag §5.14):" >&2 + diff "$ROOT/receipt-a.norm" "$ROOT/receipt-b.norm" >&2 || true exit 1 fi -pass "generated.at-Ausnahme dokumentiert (Allowance §5.14 Pkt. 3)" +AT_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | sed -n 's/^ at: //p' | head -1) +AT_B=$(git -C "$ROOT/wt-b" show 'HEAD:wiki/alpha.md' | sed -n 's/^ at: //p' | head -1) +[ -n "$AT_A" ] && [ -n "$AT_B" ] \ + || { echo "FAIL: generated.at fehlt in mindestens einem Run-Output" >&2; exit 1; } +echo " BEFUND: at-Werte A=$AT_A B=$AT_B (Ausnahme §5.14 Pkt. 3; Identität zulässig)" +pass "E.6b generated.at in A und B anwesend (nicht-leer)" -# --- E.7 alpha.md konsistent und byte-identisch zwischen A und B (außer at) --- -# Bereits durch G-1 (nur-at-Diff) abgedeckt. Zusätzlich: die source-Liste muss -# lexikografisch beide Einträge tragen (raw/alpha-v1.md, raw/alpha-v2.md). -for wt in "$ROOT/wt-a" "$ROOT/wt-b"; do - if ! git -C "$wt" show 'HEAD:wiki/alpha.md' | grep -q 'resource: raw/alpha-v2.md'; then - echo "HARD-FAIL (E): source-Liste in $wt ohne raw/alpha-v2.md (Konsolidierung fehlt)" >&2 - exit 1 - fi +# --- E.7 sources-Konsolidierung (alpha: beide Belege) --- +for wt in a b; do + for res in raw/alpha-v1.md raw/alpha-v2.md; do + git -C "$ROOT/wt-$wt" show 'HEAD:wiki/alpha.md' | grep -q "resource: $res" \ + || { echo "HARD-FAIL (E.7): alpha.md in $wt ohne source $res (Konsolidierung fehlt)" >&2; exit 1; } + done done -pass "sources-Liste in alpha.md (A und B) faktor konsolidiert (raw/alpha-v1.md + raw/alpha-v2.md)" +pass "E.7 sources-Liste alpha.md (A und B) konsolidiert (raw/alpha-v1.md + raw/alpha-v2.md)" -# --- E.8 gamma.md und index.md byte-identisch (AC-6: unabhängiges Wissen erhält) --- -for f in wiki/index.md wiki/gamma.md; do - HA=$(git -C "$ROOT/wt-a" show "HEAD:$f" | sha256sum | cut -d' ' -f1) - HB=$(git -C "$ROOT/wt-b" show "HEAD:$f" | sha256sum | cut -d' ' -f1) - BASEH=$(git -C "$CAGE" show "$BASE:$f" | sha256sum | cut -d' ' -f1) - [ "$HA" = "$HB" ] || { echo "HARD-FAIL (E): $f weicht zwischen A und B ab (byte-identisch gefordert)" >&2; exit 1; } - [ "$HA" = "$BASEH" ] || { echo "HARD-FAIL (E): $f in A B != Baseline (unabhängiges Wissen darf nicht mutieren)" >&2; exit 1; } +# --- E.8 Unabhängiges Wissen erhalten (AC-6): gamma byte-pinned; index = BASE + exakt 1 neue Link-Zeile --- +HA=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/gamma.md' | sha256sum | cut -d' ' -f1) +HB=$(git -C "$ROOT/wt-b" show 'HEAD:wiki/gamma.md' | sha256sum | cut -d' ' -f1) +BASEHG=$(git -C "$CAGE" show "$BASE:wiki/gamma.md" | sha256sum | cut -d' ' -f1) +[ "$HA" = "$HB" ] || { echo "HARD-FAIL (E.8): gamma.md weicht zwischen A und B ab" >&2; exit 1; } +[ "$HA" = "$BASEHG" ] || { echo "HARD-FAIL (E.8): gamma.md != Baseline (unabhängiges Wissen mutiert)" >&2; exit 1; } +IDX_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/index.md') +IDX_B=$(git -C "$ROOT/wt-b" show 'HEAD:wiki/index.md') +[ "$IDX_A" = "$IDX_B" ] || { echo "HARD-FAIL (E.8): index.md weicht zwischen A und B ab" >&2; exit 1; } +IDX_DIFF=$(git -C "$CAGE" diff "$BASE" "$HEAD_A" -- wiki/index.md) +IDX_ADDED=$(printf '%s\n' "$IDX_DIFF" | grep -c '^+[^+]' || true) +IDX_DELETED=$(printf '%s\n' "$IDX_DIFF" | grep -c '^-[^-]' || true) +[ "$IDX_DELETED" -eq 0 ] || { echo "HARD-FAIL (E.8): index.md trägt Deletions (nur Appending erlaubt):" >&2; printf '%s\n' "$IDX_DIFF" >&2; exit 1; } +[ "$IDX_ADDED" -eq 1 ] || { echo "HARD-FAIL (E.8): index.md Diff != exakt 1 Zeile (erwartet: CREATE-Link), ist: $IDX_ADDED" >&2; printf '%s\n' "$IDX_DIFF" >&2; exit 1; } +printf '%s\n' "$IDX_DIFF" | grep '^+[^+]' | grep -qF "$NEW_LEAF" \ + || { echo "HARD-FAIL (E.8): die einzige neue index-Zeile ist kein Link auf die CREATE-Datei $NEW_LEAF:" >&2; printf '%s\n' "$IDX_DIFF" >&2; exit 1; } +pass "E.8 Unabhängiges Wissen erhalten: gamma byte-identisch; index = BASE + exakt 1 CREATE-Link (A==B)" + +# --- E.9 log.md: Eintrag mit vollem Baseline-SHA (R-1; P-9; kein vorgeschriebener Wortlaut) --- +for wt in a b; do + LOG_BASE=$(git -C "$CAGE" show "$BASE:wiki/log.md" | sha256sum | cut -d' ' -f1) + LOG_HEAD=$(git -C "$ROOT/wt-$wt" show 'HEAD:wiki/log.md' | sha256sum | cut -d' ' -f1) + [ "$LOG_BASE" != "$LOG_HEAD" ] \ + || { echo "HARD-FAIL (E.9): log.md in $wt == Baseline (kein Lauf-Eintrag)" >&2; exit 1; } + git -C "$ROOT/wt-$wt" show 'HEAD:wiki/log.md' | grep -q "$BASE" \ + || { echo "HARD-FAIL (E.9): log.md-Eintrag in $wt ohne volles Baseline-SHA (R-1)" >&2; exit 1; } done -pass "Unabhängiges Wissen byte-identisch erhalten (gamma.md, index.md; AC-6)" +pass "E.9 log.md (A und B): Lauf-Eintrag vorhanden und trägt volles Baseline-SHA (R-1)" echo echo "ZWEI-FRESCHE-AGENTEN A/B abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" @@ -615,93 +670,117 @@ echo "ZWEI-FRESCHE-AGENTEN A/B abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FA # F. G-SZENARIEN (G-1..G-8) — harte Assertions # ============================================================================== runlabel "F. G-SZENARIEN G-1..G-8" -# G-1 (A/B nur-at) ist in E.4 hart geprüft — hier referenzieren. -pass "G-1: Zwei-frische-Agenten A/B deterministisch (nur at-Ausnahme, §5.14 Pkt. 2)" -# G-2: Validator-Integration — ein frischer Kontext hat schema/validator.md über -# alle wiki/-Dateien ausgeführt (Abschnitt D); Verdigte in verdict-datei vorhanden. -[ -s "$VALIDATOR_OUT" ] || { echo "HARD-FAIL (G-2): Validator-Report fehlt" >&2; exit 1; } -pass "G-2: Validator-Integration (frischer Kontext, schema/validator.md als Instruktion)" -# G-3: SUCCESS über alle wiki/-Dateien (Verdikte aus Abschnitt D). -if [ "$FAIL_VERDICTS" -ne 0 ]; then - echo "HARD-FAIL (G-3): $FAIL_VERDICTS FAIL-Verdikte im Validator-Report (alle wiki/ müssen SUCCESS sein)" >&2 - grep '^FAIL ' "$VALIDATOR_OUT" >&2 || true +# G-1 (A/B nur-at) ist in E.4 hart geprüft (geankerter Classifier, P-2) — Referenz. +pass "G-1: Zwei-frische-Agenten A/B deterministisch (nur at-Ausnahme, §5.14 Pkt. 2; E.4)" +# G-2: Validator-Integration — frischer Kontext hat schema/validator.md über alle +# wiki/-Dateien ausgeführt (Abschnitt D); Marker + Grammatik geprüft. +[ -s "$VAL_A" ] || { echo "HARD-FAIL (G-2): Validator-Report fehlt" >&2; exit 1; } +pass "G-2: Validator-Integration (frischer Kontext, schema/validator.md als Instruktion; D.1)" +# G-3: SUCCESS über alle Käfig-Bundle-Dateien (D.6: 0 FAIL-Verdikte — sonst rot) +if [ "${DEFICIT:-1}" -ne 0 ]; then + echo "HARD-FAIL (G-3): ${DEFICIT} FAIL-Verdikte im Validator-Report (alle wiki/ müssen SUCCESS sein)" >&2 exit 1 fi pass "G-3: Validator-SUCCESS über alle wiki/-Dateien (0 FAIL-Verdikte)" -# G-4: Fail-fast der 12 Sandbox-Sub-Runs (Abschnitt C) — bereits durch Exit-Checks -# in C abgedeckt; hier nur Referenz. +# G-4: Fail-fast der 12 Sandbox-Sub-Runs (Abschnitt C) — Referenz. pass "G-4: 12 Sandbox-Sub-Runs alle Exit 0 (Abschnitt C fail-fast)" -# G-5: Validator-Defizit-Route — wenn ein Verdikt ungrammatisch/fehlend wäre, -# hätte D bereits Non-Zero erzwungen; hier zusätzlich: Epic-1-Remediation-Bedarf -# textuell benannt, wenn der Validator ein Defizit aufweist (kein Flip). -# (Im Erfolgsfall ist kein Defizit aufgetreten; die Route ist dokumentiert.) -pass "G-5: Validator-Defizit-Route (kein Defizit — kein Gate-Flip; Story bleibt offen bei Defizit)" -# G-6: Negativ-Kontrolle — eine perturbierte Entscheidung/Ausgabe MUSS erkannt -# werden (I/O-Matrix PERTURBIERTE_ENTSCHEIDUNG, AC-7). Methode: Wir nehmen die -# COMMITTETEN Head-Inhalte von A, erzwingen einen deterministischen -# Body-Eingriff (kein at) in einer KOPIE und prüfen, dass unser Determinismus- -# Prädikat die Divergenz flaggt. Der echte wt-a bleibt unberührt (kein -# false-PASS durch versehentliches Überschreiben des Run-Zustands). -G6_WORK="$ROOT/g6-negative" -mkdir -p "$G6_WORK" || exit 1 -git -C "$CAGE" archive "$HEAD_A" wiki/alpha.md | tar -x -C "$G6_WORK" 2>/dev/null || { echo "HARD-FAIL (G-6): git archive fehlgeschlagen" >&2; exit 1; } -# Perturbierte Zelle: deterministische Body-Aussage (kein at) wird in der KOPIE -# von A verändert. Kein sed -i: sed => tmp => mv ist plattformportabel (AC-2). -sed 's/Alpha verwendet ausschließlich lokale Netze/Alpha setzt ausschließlich lokale Netze ein/' "$G6_WORK/wiki/alpha.md" > "$G6_WORK/wiki/alpha.perturb.tmp" -mv "$G6_WORK/wiki/alpha.perturb.tmp" "$G6_WORK/wiki/alpha.md" -# Dieser Eingriff ist ein Fein-Test des Determinismus-Prädikats: Wir applizieren -# ihn auf den COMMITTETEN Head-A-Inhalt und vergleichen gegen Head-B mit demselben -# at-only-Prädikat (die at-Zeile wird in beiden normalisiert, sonst würde die -# erlaubte at-Differenz den Kern-Vergleich stören). -NEG_BODY_A=$(sed 's/^ at: .*/ at: AT/' "$G6_WORK/wiki/alpha.md" | sha256sum | cut -d' ' -f1) -NEG_BODY_B=$(git -C "$CAGE" show "$HEAD_B:wiki/alpha.md" | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1) -if [ "$NEG_BODY_A" != "$NEG_BODY_B" ]; then - pass "G-6: Negativ-Kontrolle — perturbierte Entscheidung erkannt (deterministischer Kern weicht ab)" -else - echo "HARD-FAIL (G-6): Negativ-Kontrolle hat Perturbation NICHT erkannt (false-PASS)" >&2 - exit 1 -fi -# Perturbation wirkt nur auf die /tmp-Kopie — der echte wt-a ist unverändert. -# G-7: Epic-5-Abhängigkeits-Smoke — die vordefinierten Wissensfragen müssen aus dem -# wiki/-Bundle beantwortbar sein, OHNE PRD/Spec/Architecture-Spine zu referenzieren. -# (AC-8). Smoke: Alle *.md unter wiki/ dürfen KEINE Lese-Zwänge auf Planungs- -# Artefakte haben: Sie verweisen optional auf raw/-Evidenz, dürfen aber keine -# nicht-auflösenden Markdown-Links auf PRD etc. tragen. Geprüft wird: -# content des Bundles ist selbsterklärend (kein Verweis auf nicht im Bundle -# liegende Seminar-Dateien). Konkret: kein Link-Ziel außerhalb wiki//raw/. -# Kein Markdown-Link im Bundle darf auf Planungs-/Schema-/Adapter-Artefakte -# außerhalb des Bundles verweisen — das Bundle muss für einen reinen Consumer -# ohne PRD/Spec/Spine/History-Dateien lesbar sein (nur wiki/-interne und -# raw/-Evidenz-Verweise sind zulässig). -# Kontext-Abgleich (Compiler §5.6/§5.7 + AC-8): Die 3 `../schema/*`-Links der -# Bundleroot `wiki/index.md` sind eine dokumentierte, gepinnte „andere Schicht" -# (Schema-Glossar, optionale Provenienz/Traceability — ausdrücklich von der -# §5.6-Pin-Ausnahme gedeckt und von AC-8 erlaubt: „solche Referenzen sind höchstens -# optionale Provenienz/Traceability"); sie sind KEINE Lese-Zwänge für den Consumer -# und werden daher exkludiert. `wiki/log.md` ist (Compiler §5.6 Pkt. 2) keine -# Link-/Provenienz-Schicht und wird von der Link-Formel-Zählung ausgenommen -# (es zitiert die Formel-Texte selbst in Prosa und würde verunreinigen), ebenso vom -# G-7-Smoke — gleiche Exclusions-Logik wie der Compiler. JEDE andere Datei unter -# wiki/ (Concept-Bodies, Area-index.md) muss frei von `../schema|../adapters` bleiben. -SMOKE_BAD=0 +# G-5: Defizit-Route (P-7) — im grünen Zustand: kein Defizit, kein Gate-Flip; die +# Route selbst ist in D.6 real implementiert (FAIL-Verdikt → Non-Zero + +# Remediation-Text). Renegotiation-Kandidat: frozen I/O-Matrix-Zeile +# VALIDATOR_DEFIZIT „G-5 PASS" vs. Boundary/AC-4 (Non-Zero) — Boundary/AC-4 gilt. +pass "G-5: Validator-Defizit-Route implementiert (D.6; kein Defizit in diesem Lauf — kein Gate-Flip)" +# G-6 (D-3): echter perturbed committed Tree (wt-c) durch denselben E.4-Classifier. +# wt-c: Body-Eingriff (kein at) auf A-Commit, committet in 3. Worktree ⇒ MUSS NON_AT≠0 +# wt-c2: at-only-Eingriff (erlaubte Ausnahme) ⇒ MUSS NON_AT=0 (Positiv-/Kontroll-Kontrolle) +git -C "$CAGE" worktree prune +git -C "$CAGE" worktree add -q "$ROOT/wt-c" "$HEAD_A" \ + || { echo "HARD-FAIL (G-6): Worktree wt-c (perturbed) nicht aufgebaut" >&2; exit 1; } +git -C "$CAGE" worktree add -q "$ROOT/wt-c2" "$HEAD_A" \ + || { echo "HARD-FAIL (G-6): Worktree wt-c2 (at-only-Kontrolle) nicht aufgebaut" >&2; exit 1; } +# Perturbation 1: deterministischer Body-Eingriff (kein at) — Zelle wird umgestellt. +( cd "$ROOT/wt-c" \ + && sed 's/Alpha verwendet ausschließlich lokale Netze/Alpha setzt ausschließlich lokale Netze ein/' wiki/alpha.md > wiki/alpha.perturb.tmp \ + && mv wiki/alpha.perturb.tmp wiki/alpha.md \ + && git add wiki \ + && git commit -qm "G-6: perturbed Body (kein at)" ) \ + || { echo "HARD-FAIL (G-6): Perturbed-Commit wt-c fehlgeschlagen" >&2; exit 1; } +# Perturbation 2: at-only (erlaubte Ausnahme §5.14 Pkt. 3) +( cd "$ROOT/wt-c2" \ + && sed 's/^ at: .*/ at: 2026-08-22T00:00:00Z/' wiki/alpha.md > wiki/alpha.perturb.tmp \ + && mv wiki/alpha.perturb.tmp wiki/alpha.md \ + && git add wiki \ + && git commit -qm "G-6: at-only Perturbation" ) \ + || { echo "HARD-FAIL (G-6): at-only-Commit wt-c2 fehlgeschlagen" >&2; exit 1; } +HEAD_C=$(git -C "$ROOT/wt-c" rev-parse HEAD) +HEAD_C2=$(git -C "$ROOT/wt-c2" rev-parse HEAD) +G6_DIFF="$ROOT/g6-wiki.diff" +git -C "$CAGE" diff "$HEAD_C" "$HEAD_B" -- wiki/ > "$G6_DIFF" 2>&1 +G6_CLASS=$(classify_diff "$G6_DIFF") +echo " G-6-Classifier (C vs B): $G6_CLASS" +G6_NON_AT=$(printf '%s' "$G6_CLASS" | sed -n 's/.*NON_AT=\([0-9]*\).*/\1/p') +[ "$G6_NON_AT" != "0" ] \ + && pass "G-6 Negativ-Kontrolle: perturbed committed Tree C erkannt (NON_AT=$G6_NON_AT ≠ 0 — kein false-PASS)" \ + || { echo "HARD-FAIL (G-6): Perturbation NICHT erkannt (false-PASS)" >&2; exit 1; } +G6C2_DIFF="$ROOT/g6c2-wiki.diff" +git -C "$CAGE" diff "$HEAD_C2" "$HEAD_B" -- wiki/ > "$G6C2_DIFF" 2>&1 +G6C2_CLASS=$(classify_diff "$G6C2_DIFF") +echo " G-6-Classifier (C2 vs B, at-only-Kontrolle): $G6C2_CLASS" +G6C2_NON_AT=$(printf '%s' "$G6C2_CLASS" | sed -n 's/.*NON_AT=\([0-9]*\).*/\1/p') +[ "$G6C2_NON_AT" = "0" ] \ + && pass "G-6 Positiv-Kontrolle: at-only Perturbation C2 korrekt toleriert (NON_AT=0)" \ + || { echo "HARD-FAIL (G-6): at-only-Kontrolle C2 falsch-positiv (NON_AT=$G6C2_NON_AT ≠ 0)" >&2; exit 1; } +# G-7 (D-4/P-13): Epic-5-Smoke auf dem IST-Baum ($REPO_ROOT, nie der /tmp-Spiegel): +# (a) Link-Auflösung: jeder Markdown-Link in allen wiki/-Dateien muss entweder +# bundle-intern (relative .md, inkl. ../-Auflösung, Anchor gestrippt) oder +# eine raw/-Provenienz-Referenz sein — KEIN Ziel außerhalb wiki//raw/. +# (b) Geschlossene Planungstoken-Menge (am realen Bundle validiert: 0 bare +# Treffer nach raw/-Provenienz-Strich; Exkl. wiki/index.md + wiki/log.md): +# blosse Planungs-Referenzen (Prosa) auf PRD/Spec/Spine/Brief/Story-History +# sind Lese-Zwänge (AC-8) → Non-Zero. +G7_BAD=0 +G7_FILES=$(cd "$REPO_ROOT" && find wiki -type f -name '*.md' | LC_ALL=C sort) while IFS= read -r f; do - # Bundleroot-Schema-Glossar (§5.6 „andere Schicht") und log.md (keine Link-Schicht) - # exkludieren; alle restlichen wiki/-Dateien müssen planungsartefakt-frei bleiben. + [ -z "$f" ] && continue if [ "$f" = "wiki/index.md" ] || [ "$f" = "wiki/log.md" ]; then continue fi - if grep -qE '\]\(\.\./(schema|adapters)/' "$f"; then - echo "FAIL (G-7): $f verweist auf ein Nicht-Bundle-Artefakt (../schema|../adapters)" >&2 - SMOKE_BAD=$((SMOKE_BAD+1)) - fi -done <&2; exit 1; } -pass "G-7: Epic-5-Abhängigkeits-Smoke (AC-8: Bundle ohne Planungs-Lese-Zwänge lesbar; index.md-Schema-Glossar + log.md-Protokoll exkludiert, compiler §5.6)" -# G-8: Porcelain-Check — der IST-Baum muss nach dem Lauf unverändert sein. Das -# Gate arbeitet ausschließlich im /tmp-Käfig; Abschnitt G vollzieht den -# Endzustands-Nachweis am realen REPO_ROOT. + fpath="$REPO_ROOT/$f" + fdir=$(dirname "$fpath") + # (a) Link-Auflösung + LINKS=$(grep -oE '\]\([^)]+\)' "$fpath" | sed -E 's/^\]\(//; s/\)$//' || true) + while IFS= read -r tgt; do + [ -z "$tgt" ] && continue + tgt_clean=${tgt%%#*} + case "$tgt_clean" in + ''|http://*|https://*|mailto:*|raw/*) continue ;; # Provenanz/Evidenz/extern-ok + esac + # relative Auflösung gegen die Datei (root- oder area-/relativ) + if [ -f "$fdir/$tgt_clean" ]; then continue; fi + if [ -f "$REPO_ROOT/$tgt_clean" ]; then continue; fi + echo "FAIL (G-7a): $f — Link-Ziel nicht bundle-intern auflösbar: $tgt_clean" >&2 + G7_BAD=$((G7_BAD+1)) + done <&2 + G7_BAD=$((G7_BAD+1)) + fi + done +done <&2; exit 1; } +pass "G-7: Epic-5-Smoke auf Ist-Baum (AC-8): Link-Auflösung + geschlossene Planungstoken-Menge, 0 Verletzungen" +# G-8: Porcelain-Endzustands-Invariante — in Abschnitt G hart am Ist-Baum. pass "G-8 wird in Abschnitt G als Endzustands-Invariante am Ist-Baum geprüft" echo @@ -711,61 +790,50 @@ echo "G-SZENARIEN abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" # G. NACHWEISE / PORCELAIN — Endzustands-Invariante des Ist-Baums # ============================================================================== runlabel "G. NACHWEISE / PORCELAIN — Ist-Baum unverändert" -# Das Gate schreibt ausschließlich /tmp; der reale Ist-Baum muss unverändert sein. -# Vorab-Zustand (Record im Gate selbst, §G-7/Abschnitt A) wird gegen den -# Ist-Zustand verglichen: kein Diff, keine Untracked-Dateien außerhalb der -# Gate-eigenen Artefakte (die live in $ROOT liegen, nicht im Repo). -G8_PORCELAIN=$(git -C "$REPO_ROOT" status --porcelain --untracked-files=no 2>&1) -[ -n "$G8_PORCELAIN" ] && echo " Ist-Baum-Porcelain vor Abnahme:" +# G-8 (P-11 hart, inkl. Untracked): Das Gate schreibt ausschließlich /tmp; der +# reale Ist-Baum muss VOLLSTÄNDIG unverändert sein (kein --untracked-files=no). +cd "$REPO_ROOT" || hardfail "Repo-Root nicht mehr erreichbar (Abschnitt G)" +G8_PORCELAIN=$(git status --porcelain) +echo " Ist-Baum-Porcelain (vollständig, inkl. Untracked) nach dem Gate-Lauf:" printf '%s\n' "$G8_PORCELAIN" -# Der Ist-Baum darf durch den Gate-Lauf keine Spur hinterlassen (außer den -# durch die Story-3.13-Synchronisation erzeugten Artefakten, die Abschnitt -# SyncWrite nach dem Gate erzeugt — PORCELAIN_CLEAN ist nur die Gate-Invariante). -# Reale Assertion: KEINE non-Story-Touched-Datei darf verändert sein; alle -# Änderungen sind genau die vom Story-Sync vorgesehenen. -# Wir erfassen die Referenz-Dateien read-only (AD-3) — dürfen nie diffen. -for ro in schema/compiler.md schema/validator.md schema/wiki-compiler.md schema/canonical-terms.md adapters; do - if ! git -C "$REPO_ROOT" diff --quiet -- "$ro"; then +if [ -z "$G8_PORCELAIN" ]; then + pass "G-8: Porcelain-Clean (Ist-Baum nach Gate-Lauf unverändert; leer inkl. Untracked)" +else + echo "HARD-FAIL (G-8): Ist-Baum trägt uncommittete Änderungen/Untracked nach dem Gate-Lauf:" >&2 + printf '%s\n' "$G8_PORCELAIN" >&2 + exit 1 +fi +# Negativ-Kontrolle der Porcelain-Detection (IM KÄFIG, nie im realen Repo): +# eine in den Käfig gelegte Scratch-Datei MUSS vom vollständigen Porcelain erfasst +# werden — Beweis, dass die A.5/G-8-Guard nicht blind für Untracked sind. +NC_FILE="$CAGE/wiki/.g8-negative-control.md" +echo "negativ-kontrolle" > "$NC_FILE" +NC_PORC=$(git -C "$CAGE" status --porcelain -- wiki/) +git -C "$CAGE" -c core.hooksPath=/dev/null checkout -q -- . 2>/dev/null || true +rm -f "$NC_FILE" +case "$NC_PORC" in + *"?? wiki/.g8-negative-control.md"*) pass "G-8 Negativ-Kontrolle: Untracked-Datei wird vom Porcelain erfasst (Detection wach)";; + *) echo "HARD-FAIL (G-8): Negativ-Kontrolle — Untracked-Datei NICHT im Porcelain (Guard blind)" >&2; exit 1;; +esac +# Read-only-Artefakte (AD-3): kein Diff am Ist-Baum +for ro in schema/compiler.md schema/validator.md schema/wiki-compiler.md schema/canonical-terms.md adapters raw; do + if ! git diff --quiet -- "$ro" 2>/dev/null; then echo "HARD-FAIL (G-8): Read-only-Artefakt verändert: $ro (AD-3)" >&2 exit 1 fi done -for ro in raw; do - if ! git -C "$REPO_ROOT" diff --quiet -- "$ro"; then - echo "HARD-FAIL (G-8): raw/ verändert (AD-3)" >&2 - exit 1 - fi -done -pass "G-8: Read-only-Artefakte (schema/validator.md, compiler.md, wiki-compiler.md, canonical-terms.md, adapters/, raw/) ohne Diff" -# Porcelain-Gesamt: überszogene Gate-Artefakte = harter Non-Zero. Wenn Änderungen -# auftreten, müssen sie EXAKT die Story-3.13-Artefakte sein (run-sandbox.sh selbst -# + Dokumentations-Sync). Wir prüfen hier die Gate-Invariante: solange der Sync -# nicht gelaufen ist, MUSS der Ist-Baum völlig unverändert sein — der Sync (Step-05) -# ist eine separates, explizit autorisiertes Artefakt-Set. -G8_TRAIL=$(git -C "$REPO_ROOT" status --porcelain --untracked-files=no 2>&1) -echo " Ist-Baum-Porcelain nach dem Gate-Lauf: '$(printf '%s' "$G8_TRAIL" | head -c 200)'" -# G-8 hart (spec: "überszogene Gate-Artefakte = Non-Zero", "git status --porcelain -# --untracked-files=no leer"). Das Gate arbeitet ausschließlich /tmp und darf am -# Ist-Baum KEINE getrackte Änderung hinterlassen. Wenn der Story-3.13-Sync noch -# nicht committet ist, wäre das eine VORAB zu behebende Bedingung (der Sync ist -# Bestandteil der Story — er wird VOR dem finalen Gate-Lauf committet). -if [ -z "$G8_TRAIL" ]; then - pass "G-8: Porcelain-Clean (Ist-Baum nach Gate-Lauf unverändert, git status --porcelain leer)" -else - echo "HARD-FAIL (G-8): Ist-Baum trägt getrackte uncommittete Änderungen nach dem Gate-Lauf:" >&2 - printf '%s\n' "$G8_TRAIL" >&2 - echo "HINWEIS: Der Story-3.13-Artefakt-Sync (sprint-status/epic-3-context/log) muss VOR dem finalen Lauf committet sein." >&2 - exit 1 -fi +pass "G-8: Read-only-Artefakte (schema/*, adapters/, raw/) ohne Diff (AD-3)" echo echo "########################################" echo "# GATE-ENDE — FAILED=$FAILED PASS_COUNT=$PASS_COUNT" echo "########################################" if [ "$FAILED" -eq 0 ]; then + echo "SANDBOX-3-13-OK" echo "RUN_OK" exit 0 else + echo "SANDBOX-3-13-FAILED" >&2 echo "RUN_FAILED" >&2 exit 1 fi diff --git a/_bmad-output/implementation-artifacts/spec-3-13-epic-3-verifikations-und-abnahmegate.md b/_bmad-output/implementation-artifacts/spec-3-13-epic-3-verifikations-und-abnahmegate.md index fc9cb88..e03d09e 100644 --- a/_bmad-output/implementation-artifacts/spec-3-13-epic-3-verifikations-und-abnahmegate.md +++ b/_bmad-output/implementation-artifacts/spec-3-13-epic-3-verifikations-und-abnahmegate.md @@ -2,9 +2,9 @@ title: 'Story 3.13 — Epic-3-Verifikations- und Abnahmegate' type: 'feature' created: '2026-08-22' -status: 'done' +status: 'in-progress' baseline_commit: 'c234fc361e006043c3c972cc03211b52998c2ddb' -review_loop_iteration: 0 +review_loop_iteration: 1 context: - '_bmad-output/implementation-artifacts/epic-3-context.md' --- @@ -86,9 +86,48 @@ context: - Given nur das erzeugte Knowledge Bundle ohne Planungsartefakte, when ein unabhängiger Consumer im Epic-5-Abhängigkeits-Smoke-Test vordefinierte Wissensfragen beantwortet, then benötigt er weder Product Brief, SPEC, Architecture Spine noch Story-Historie zum semantischen Verständnis; solche Referenzen sind höchstens optionale Provenienz/Traceability (AC-8). - Given ein bestandenes Gate, when die Status-Synchronisation läuft, then wird der Key `3-13-…` auf `done` geflippt und Epic 3 erst nach bestandenem Gate und `done` aller Stories 3.8..3.13 auf `done` gesetzt; bis dahin bleibt Epic 3 `in-progress` (AC-9). +### Review Findings + +_Review-Loop 1 (bmad-code-review, 4 Layer — blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor, 2026-08-22; Diff `c234fc3..894ae34`+working tree; 28 eindeutige Befunde nach Dedup aus 43 rohen: 7 decision-needed / 15 patch / 2 defer / 6 dismissed. Hinweis: der Verification-Gap-Layer lieferte nach einem transienten API-Output-Limit-Fehler über einen Größenbegrenzten Retry)._ + +**Decision-Needed (7):** + +- [x] [Review][Decision] **D-3.13-1: Agent-Prompt hard-codiert erwartete Plan-/Entscheidungs-/Receipt-/Log-Literale** — `AGENT_PROMPT` (E.3) diktiert beide frischen Kontexte auf `candidates: alpha`, `decision: form=konsolidieren;zelle=UPDATE;ziel=alpha`, `sources_added: raw/alpha-v2.md`, die exakte Multi-Beleg-Ergebnisform und die wörtliche `log.md`-Zeile (inkl. ``). Verletzt den gefrorenen Boundary „hard-codiert keinerlei erwartete Bodies/Receipts" (AC-4/AC-5, epic-3-context Z. 46; compiler.md §5.14 Pkt. 2: „weder erwartete Pläne noch Concept-Bodies werden im Test hart codiert") und widerspricht dem eigenen deferred-work-„Aufgegriffen"-Block, der die DET-1/DET-2- und R-9-Defer (Plan-/Receipt-Literale) als durch diesen Lauf geschlossen markiert. A==B-Konvergenz ist dadurch prompt-gesteuert, nicht unabhängig aus `schema/compiler.md` abgeleitet. Options: (1) Prompt auf Instruktion + Fixture-Fakten beschränken, erwartete Formen/Receipts/Log-Zeile entfernen, Gate-Checks auf A-vs-B + Nicht-Vakuum-Witness gegen Baseline reduzieren (wahrer 3.8-Nachweis); (2) Literalen als „repräsentative Fixture-Vorgabe" dokumentieren und Spec/deferred-work-Klammern nachverhandeln (frisiert die AC-5-Präzedenz). +- [x] [Review][Decision] **D-3.13-2: AC-7-Hälfte „Run-Receipts … überein" wird nie verglichen** — Das Gate vergleicht nur den Bundle-State (`git diff HEAD_A HEAD_B -- wiki/` + sha256 je Datei); `run-receipt.txt` wird von den Agenten geschrieben, aber nie gelesen, gegreppt, gehasht oder A-vs-B-anglichen (grep: nur Prompt-/Kommentar-Treffer). AC-7 + §5.14 Pkt. 2 nennen den Receipt als „von außen vergleichbaren Nachweis je Lauf". Options: (1) Receipt-Vergleich implementieren (A vs B zeilenweise, keine erwarteten Literale — mit D-3.13-1-Option 1 konsistent); (2) AC-7-Auslegung als „nur Bundle-State" dokumentieren (braucht Intent-Nachverhandlung). +- [x] [Review][Decision] **D-3.13-3: G-6-Negativ-Kontrolle routet keinen perturbed Run** — Die I/O-Matrix-Zeile PERTURBIERTE_ENTSCHEIDUNG (frozen) verlangt: „eine der zwei Runs wird im Script über eine perturbed Option geroutet". Implementiert ist: `git archive` des committeden A-`alpha.md` in eine Scratch-Datei, `sed`-Worttausch, at-normalisierter sha256sum-Vergleich gegen B. Das prüft den Hash-Vergleich, aber weder das E.4-Line-Classifier-Prädikat (das tatsächlich G-1 abwartet) noch einen echten gelaufenen Run. Options: (1) Echten perturbed-Drittrun (3. Agent mit perturbed Option; A-vs-C-Classifier MUSS `NON_AT`≠0 flaggen) — teuer (~45 min Agent-Lauf); (2) Perturbed-Commit-Tree in einer wt-Kopie committen + denselben E.4-Diff-Classifier darüber fahren (MUSS `NON_AT`≠0) — deterministisch, ohne 3. Agent; (3) Ist-Lösung als „Prädikat-Negativ-Kontrolle" akzeptieren + I/O-Matrix-Zeile nachverhandeln. +- [x] [Review][Decision] **D-3.13-4: G-7-Smoke ist enger als die frozen I/O-Matrix CONSUMER_SMOKE** — Matrix-Zeile: „Referenz auf PRD/Spec/Spine = Non-Zero"; AC-8: „benötigt er weder Product Brief, SPEC, Architecture Spine noch Story-Historie". Implementiert ist nur `grep '\]\(\.\./(schema|adapters)/'` plus Exclusion `wiki/index.md`+`wiki/log.md`. Prosa-Referenzen auf PRD/Spine/Story-History in Concept-Bodies oder Links auf andere Nicht-Bundle-Pfade (z. B. `../../planning-artifacts/…`) passieren. Options: (1) G-7 auf Matrix-Formulierung erweitern (Token-/Link-Prüfung auf PRD/Spec/Spine/Brief/Story-History in allen nicht-exkludierten wiki-Dateien); (2) Enge Grep-Form als hinreichend dokumentieren (Intent-Nachverhandlung). +- [x] [Review][Decision] **D-3.13-5: AC-3-Negativ-Fixture-Hälfte wird nie ausgeführt** — AC-3: „fehlendes `generated.by`, kalenderinvalides `generated.at`, unzulässige Source-Pfade und gebrochene Index-Links verhindern SUCCESS und Commit". Abschnitt D validiert nur das real-grüne Bundle (7/7 SUCCESS); keine Negativ-Fixture (validator.md §7-Katalog: 4a `resolved=`, Innen-Ebenen `role:`, 11a unlinked Area, EC-3-Kalender) wird dem frischen Validator-Agenten vorgelegt. Options: (1) Mini-Baum mit 2–3 §7-Negativ-Fixtures in einen separaten /tmp-Käfig bauen, frischen Validator-Agenten darauf fahren, MUST `FAIL `-Verdikte erwarten; (2) Negativ-Hälfte als von Epic-1-Zertifizierungen (log.md-Rev-9-Nachweis) gedeckt akzeptieren + AC-3-Auslegung dokumentieren. +- [x] [Review][Decision] **D-3.13-6: AC-6-Fixture demonstriert keine „Anlage einer neuen Wissenseinheit"** — AC-6 verlangt sechs Demonstrationen, inkl. „Anlage einer neuen Wissenseinheit" und „kohärente Multi-Source-Synthese". Die Fixture ist ein rein CONFIRMING-Update auf das bestehende `alpha` (Prompt verbietet neue Concepts, §5.9 Pkt. 3); `gamma`/`index` bleiben byte-identisch. „Multi-Source-Synthese" reduziert sich auf einen Multi-Beleg-Anker an einer bestehenden Aussage. Anlage/Multi-Source-Synthese laufen nur als Sub-Runs sandbox-3-3/3-4 (eigene Isolation, nicht im Acceptance-Fixture-Run). Options: (1) Fixture um einen CREATE-Kandidaten + echten Multi-Source-Synthese-Baustein erweitern (größere Fixture, längere Agent-Läufe, komplexere A/B-Witness); (2) AC-6-Auslegung: „repräsentative Fixture" deckt Update-+Erhaltungs-Kern, CREATE/Synthese gelten über die 12 Sub-Runs als demostriert (Spec-Notiz, kein Code-Change). +- [x] [Review][Decision] **D-3.13-7: Status-Flip `done` geht dem konvergierten Review voran** — `sprint-status.yaml`-Key 3-13 ist seit Commit 6624e07 `done` + Spec-Frontmatter `status: done` (Commit 894ae34, während dieses Reviews angelegt), während `review_loop_iteration: 0` und dieses Review-Loop-1 noch offen ist. Präzedenz 3.7–3.12: „der finale `done`-Flip ist der Step-05-Status-Sync nach konvergiertem Review-Loop". Spec-Code-Map bestätigt: Sprint-Status „mutiert nach Gate-Freigabe (Step-05)". Options: (1) Flip auf `in-progress` zurücknehmen, erst nach konvergiertem Loop final `done` (Präzedenz-konform); (2) Ist-Lage akzeptieren, Loop-Nachschärfung im `done`-Eintrag dokumentieren. + +**Patch (15):** + +- [x] [Review][Patch] **P-1: sandbox-3-1-Sub-Run ist No-Op-Verifier — AC-1/G-4-Green-Claim lügt für Suite 3.1** — `sandbox-3-1/run-sandbox.sh` hat 0 `exit 1`/`fail()`-Aufrufe (alle 6 Szenarien drucken nur `--- Erwartet: …` + Ist-Ausgabe, impliziter Exit 0); 3-2..3-12 haben harte Exit-Pfade. Das Gate (Abschnitt C) beobachtet nur den Child-Exit-Code → eine Regression in den 3.1-Datenfluss-Szenarien (S1–S6) ist dem Gate unsichtbar; G-4 „12 Sandbox-Sub-Runs alle Exit 0" ist für n=1 vakuum. [sandbox-3-13/run-sandbox.sh:193-196] +- [x] [Review][Patch] **P-2: E.4-Classifier `*"at: "*` ist substring-breit** — Jede Diff-Zeile, die das Substring `at: ` enthält (z. B. Body-Zeilen mit „…dat: "), zählt als erlaubte at-Ausnahme; echte Content-Divergenz unter dieser Maske bleibt unsichtbar. Schärfe auf ankerierte at-Feld-Zeilen (Frontmatter `^ at: `), konsistent mit E.6/G-6. [sandbox-3-13/run-sandbox.sh:531-532] +- [x] [Review][Patch] **P-3: AC-2-Portabilität gebrochen** — `[ -n "${CLAUDE_BIN:-}" ] || CLAUDE_BIN="/d/Apps/nodejs/claude.cmd"` (MSYS-Pfad + `.cmd`), `cygpath -w`-Aufrufe, GNU-`timeout 900/1500/2700` (stock macOS/BSD: nicht vorhanden), GNU-`sha256sum` (BSD: `shasum -a 256`). Auf stock macOS scheitert das Gate vor der ersten Assertion. Options im Patch: Plattform-Feature-Checks (Kommandopresenz mit klaren Fehlern) + portablen Hash-Fallback + CLAUDE_BIN als Pflicht-Env (oder PATH-`claude`) statt hartem Default. [sandbox-3-13/run-sandbox.sh:227,237,248,299,306,451,514,556-558] +- [x] [Review][Patch] **P-4: E-Fixture ist nicht isoliert — echtes wiki//raw/-Overlay bleibt in `$BASE`** — B kopiert den vollen echten `wiki/` (7 Dateien) und `raw/` (prd/, epics/, architecture-spine/, assets/) in den Käfig; E.1 überlagert nur 4 wiki- + 3 raw-Dateien ohne Löschen des Rests, `git add -A` committet alles. `$BASE` ist damit kein dokumentiertes „Mini-Bundle" (Design Notes), der Prompt-Welt-Claim „enthält: …" (E.3) ist für beide Worktrees falsch, und die verwaisten echten Concepts sind aus der Fixture-Index nicht erreichbar (ein §6-validierender Agent würde Punkt-11-Verstoß melden). [sandbox-3-13/run-sandbox.sh:153-162,325-375] +- [x] [Review][Patch] **P-5: Kein Cage-Cleanliness-Check zwischen D und E** — Nach dem Validator-Agent (Write-enabled, Ausgabe-Pfad absolut außerhalb CWD) fehlt ein `git -C $CAGE status --porcelain`-Check; E.1 `git add -A` würde eine fremde Datei, die der Agent im Käfig hinterließ, still in `$BASE` committen und beide A/B-Runs kontaminieren, ohne Gate-Signal. [sandbox-3-13/run-sandbox.sh:255-296] +- [x] [Review][Patch] **P-6: „genau ein Verdikt je Datei" wird nicht erzwungen; Summe nie mit Dateizahl abgeglichen** — Exhaustivitäts-Loop nutzt `grep -qE` (Mindestens-1); duplizierte/konfligierende Verdikt-Zeilen für denselben Pfad passieren; `VERDICT_LINES`/`SUCCESS_VERDICTS` werden nur ausgegeben, nie mit `WIKI_MD_COUNT` verglichen — der Pass-Text-Claim „genau ein Verdikt je Datei" (und das abgeleitete „7/7 SUCCESS") ist damit überzeichnet. [sandbox-3-13/run-sandbox.sh:285-302] +- [x] [Review][Patch] **P-7: G-5/AC-4-Defizit-Route ist vakuüm + Remediation-Text nie emittiert** — Bei Defizit bricht D hart ab, ohne den geforderten Text „Bedarf einer separat autorisierten Epic-1-Remediation" zu benennen; F erreicht G-5 nur im grünen Zustand, wo es bedingungslos `pass`-t. Boundary (frozen) verlangt: „erkennt das Gate ein Validator-Defizit, schlägt G-5 fehl (Non-Zero) … benennt den Bedarf". (Zusätzlich: frozen I/O-Matrix-Zeile VALIDATOR_DEFIZIT sagt „G-5 PASS" — offenbar Tippfehler gegen Boundary/AC-4; im Patch mitnachführen, frozen-Block nur nach menschlicher Bestätigung.) [sandbox-3-13/run-sandbox.sh:277,296,633-638] +- [x] [Review][Patch] **P-8: Protokoll-Marker `VALIDATOR_DONE`/`RUN_DONE ` nie verifiziert** — Beide Prompts verlangen exakte Abschluss-Marker, das Gate greift die `.out`-Dateien nur im Fehlschlagsfall tail; ein Agent ohne Marker passiert trotzdem. [sandbox-3-13/run-sandbox.sh:244,306,432,451] +- [x] [Review][Patch] **P-9: Präskriptiver kanonischer `log.md`-Eintrag wird nie gegen den Run-Endzustand geprüft** — E.4 vergleicht nur A-vs-B, E.5/E.7 lesen nur `alpha.md`, E.8 pinnt `index.md`/`gamma.md`; eine Verletzung der §5-Log-Pflicht (fehlender Eintrag, ` --`-Form) ist A==B-konsistent möglich und passiert (Kultur-Vergleich: sandbox-3-4 prüft Wortlaut hart). [sandbox-3-13/run-sandbox.sh:512-540,560-571,599-609] +- [x] [Review][Patch] **P-10: AC-6 „Source-Immutabilität" fehlt als Assertion in E** — Nach beiden Agent-Läufen wird nie geprüft, dass `raw/` in wt-a/wt-b byte-identisch zu `$BASE` ist (Agenten haben Edit-Tool; Prompt-Verbot ist keine Assertion). [sandbox-3-13/run-sandbox.sh:512-609] +- [x] [Review][Patch] **P-11: G-8-Invariante mit `--untracked-files=no` + ohne Negativ-Kontrolle** — Der Spec-Wortlaut sagt „`git status --porcelain` leer" (inkl. Untracked); beide Checks (A Pre-Check + G End) exkludieren Untracked → eine im Repo abgelegte Gate-Datei passiert. Kein Negativ-Test (Kultur: 3-11/3-12 üben Guards negativ). [sandbox-3-13/run-sandbox.sh:107,717-757] +- [x] [Review][Patch] **P-12: log.md-Evidenz fehl-labelliert; `$BASE`-SHA nicht reproduzierbar** — Die „(20/27/40/51/60/68 PASS)"-Zahlen sind kumulative `PASS_COUNT`-Schnappschüsse, nicht pro-Sektion-Zählungen; `$BASE=5024d751…` ist ein /tmp-Commit (Zeitstempel-abhängig, nicht re-ableitbar). Beleg-Formulierungen an die tatsächliche Semantik ausrichten (kumulative Zählung deklarieren; SHA als Lauf-Beleg ohne Re-Derivations-Claim). (Reconciliation: `PASS_COUNT=70` selbst stimmt exakt mit dem aktuellen Skript.) [wiki/log.md Story-3.13-Eintrag] +- [x] [Review][Patch] **P-13: G-7 greift den /tmp-Käfig-Spiegel statt des Ist-Baums** — Nach `cd "$CAGE"` verbleibt die CWD im Käfig; der G-7-Loop liest `$f` relativ dazu → der Spiegel, nicht der reale Baum (heute äquivalent, da Spiegel aus dem Ist-Baum gebaut; bei Divergenz prüft der Smoke den falschen Baum; fehlende Datei → `grep` Exit 2 → still „keine Verletzung"). Relativ `$REPO_ROOT` greifen. [sandbox-3-13/run-sandbox.sh:166,686-700] +- [x] [Review][Patch] **P-14: Toter Code + fehlendes Cleanup** — `cmd_or_fail()` (definiert, nie aufgerufen), Restkommentar `$*`/`"$@"`/SC2034, `fail()`/`FAILED` (nie inkrementiert → `RUN_FAILED`-Zweig unerreichbar), `C_FAILED` (fail-fast-Exit vor Guard), doppelte Porcelain-Lesung `G8_PORCELAIN`+`G8_TRAIL`, kein `trap`/Cleanup für `$ROOT` (wächst je Lauf). [sandbox-3-13/run-sandbox.sh:52-61,193-207,717-746,765-771] +- [x] [Review][Patch] **P-15: Spec-Design-Notes-Beispiel invalid + ≠leer-Pin widerspricht Implementierung** — `git diff --stat "$ROOT/wt-a" "$ROOT/wt-b" -- wiki/` vergleicht Verzeichnis-Pfade (kein tree-ish → schlägt fehl); der gepinnte Kommentar „≠ leer (nicht-vakuum)" widerspricht der implementierten Allowance-leerer-Diff + Witness-Nicht-Vakuum. Beispiel auf `HEAD_A/HEAD_B`-Form korrigieren + Pin-Wortlaut an Implementierung angleichen (Design Notes sind editierbar; der frozen-Block oben bleibt unangetastet). [spec-3-13-epic-3-verifikations-und-abnahmegate.md:Design Notes] + +**Defer (2):** + +- [x] [Review][Defer] **W-1: Timeout-Budgets (2700 s) an der Grenze zu beobachteten Agent-Laufzeiten** [sandbox-3-13/run-sandbox.sh:451-453] — deferred, pre-existing: Laufzeit-Variation ist Modell-/Token-Latenz (Skript-Kommentar dokumentiert A:~30 min, B: >35 min); kein deterministischer Defekt, A0-20-konform; Beobachtung für spätere Sandbox-Härtung. +- [x] [Review][Defer] **W-2: `PASS_COUNT` driftet bei Bundle-/Referenzwachstum** [sandbox-3-13/run-sandbox.sh:42-109,184-196,285-288] — deferred, pre-existing: Loop-basierte Zählung (12 Referenz-Sandboxen + 7 Wiki-Dateien) ist design-immanent driftanfällig; Beleg-Zahl ist Lauf-Momentaufnahme (P-12 klärt die Label-Form); keine Gate-Semantik-Folge. + +_Dismissed (6): G-6-„Hash-Primitive"-Beobachtung (Duplikat von D-3.13-3/P-2); „E.5/E.7-Witness stärker als reiner A/B-Vergleich" (unter D-3.13-1 subsumiert); „Status-Dokumente stützen sich auf geschwächte Assertions" (Konsequenz, kein eigener Befund); „$BASE-SHA nicht reproduzierbar / PASS_COUNT-Drift" (Duplikat von P-12/W-2, Reconciliation: 70 stimmt exakt mit dem aktuellen Skript); „review_loop_iteration: 0" (unter D-3.13-7 subsumiert); „12 Suiten alle hart"-Claim (Duplikat von P-1)._ + ## Spec Change Log -_Leer bis zum ersten bad_spec-Loopback (Step-04)._ +**Review-Loop-1 (bmad-code-review, 4 Layer, 2026-08-22; 28 eindeutige Befunde: 7 decision-needed / 15 patch / 2 defer / 6 dismissed) — Befundauflösung 2026-08-23 (ProMods, „Ich folge Deinen Empfehlungen"):** keine intent_gap/bad_spec — **kein Loopback** (Step-04 nicht ausgelöst). **Nutzer-Entscheidungen (alle empfohlene Option):** D-3.13-1 = **Option 1** (AGENT_PROMPT nur Instruktion + Fixture-Fakten; Gate-Checks = A-vs-B + Nicht-Vakuum-Witness gegen Baseline); D-3.13-2 = **Option 1** (Run-Receipt A-vs-B-Vergleich, keine erwarteten Literale — Modell sandbox-3-8 DET-2, at-/Pfad-Zellen gefiltert); D-3.13-3 = **Option 2** (G-6 = perturbed committeter Tree `wt-c` + at-only-Kontrolle `wt-c2`, deterministisch durch denselben E.4-Classifier, kein 3. Agent — deterministische Realisierung der frozen-Matrix-Zeile PERTURBIERTE_ENTSCHEIDUNG: divergente Bäume werden erkannt, Non-Zero-Exit, kein false-PASS); D-3.13-4 = **Option 1** (G-7 = generische Link-Auflösung + geschlossene, am realen Bundle validierte Planungstoken-Menge); D-3.13-5 = **Option 1** (D-NEG: separater /tmp-Käfig mit 3 §7-/§6-Negativ-Fixtures + Positiv-Kontrolle; frischer Validator-Agent MUSS 3×FAIL + 1×SUCCESS liefern); D-3.13-6 = **Option 1** (Fixture um CREATE-Fall erweitert: neue raw-Quellen `beta-v1/v2` → neues Concept (Pfad term-geleitet, §5.15/§5.7 — nicht hart-codiert) + Index-Link; Multi-Beleg `raw/alpha-v1.md#S-2; raw/alpha-v2.md#S-3`); D-3.13-7 = **Option 1** (Status-Revert: `sprint-status.yaml`-Key + Spec-Frontmatter `done → in-progress`; finaler `done`-Flip erst nach konvergiertem Loop + grünem Re-Run, Präzedenz 3.7–3.12). **Patches (alle 15 angewendet):** P-1 (sandbox-3-1 harte Exit-Pfade, separat re-executiert: 14 PASS, FAILED=0, Exit 0), P-2..P-14 in `sandbox-3-13/run-sandbox.sh` (839 Z.), P-15 in dieser Spec (Design Notes: `--stat`-Beispiel auf `$HEAD_A/$HEAD_B`-Form; ≠leer-Pin auf Allowance-leerer-Diff + Witness-Nicht-Vakuum; Fixture-Satz um CREATE-Kandidat). **Renegotiation-Kandidat (frozen I/O-Matrix, unangetastet):** Zeile `VALIDATOR_DEFIZIT` trägt „G-5 PASS" — widerspricht der Boundary „schlägt G-5 fehl (Non-Zero)" und AC-4; **Boundary/AC-4 gilt implementiert** (Gate-Flip auf Non-Zero + Remediation-Text „Bedarf einer separat autorisierten Epic-1-Remediation"); die Matrix-Zeile ist als Nachverhandlungs-Kandidat notiert (frozen-Block erst nach menschlicher Bestätigung änderbar — Ask-First). **Spec-Frontmatter:** `status: 'done' → 'in-progress'`, `review_loop_iteration: 0 → 1` (D-3.13-7). Defer W-1/W-2 unverändert (bereits `[x]`). ## Design Notes @@ -98,11 +137,11 @@ Der Gate-Kern löst das 3.8-P-5-Defizit: die Zwei-frische-Agenten-Bestätigung l ```bash # im Gate-Skript: Bau beider Bäume auf denselben Commit git worktree add -q "$ROOT/wt-a" "$BASE" && git worktree add -q "$ROOT/wt-b" "$BASE" -# A vs B — nur die at-Zellen dürfen differieren: -git diff --stat "$ROOT/wt-a" "$ROOT/wt-b" -- wiki/ # ≠ leer (nicht-vakuum), at-only +# A vs B — nur die at-Zellen dürfen differieren (Commits der Worktrees als tree-ishes): +git diff --stat "$HEAD_A" "$HEAD_B" -- wiki/ # Allowance: leer (voll konvergiert) ODER at-only; Nicht-Vakuum sichert der Witness (E.5: alpha ≠ Baseline, CREATE-Datei, Index-Link) ``` -Die repräsentative Fixture nimmt das 3-12-Sandbox-Muster (Mini-Bundle `wiki/index.md`+`alpha.md`+`log.md`, `raw/alpha-v1.md` als Baseline, Zuwachs `raw/alpha-v2.md` → Update von `alpha.md`, ergänzt um Bereichs-Synthese/`gamma` und `wiki/`-bleibt-Byte-identisch für unabhängiges `delta`) — in den `/tmp`-Käfig kopiert und committet, nie das reale Bundle. +Die repräsentative Fixture nimmt das 3-12-Sandbox-Muster (Mini-Bundle `wiki/index.md`+`alpha.md`+`gamma.md`+`log.md`, `raw/alpha-v1.md`+`raw/gamma-v1.md` als Baseline, CONFIRMING-Zuwachs `raw/alpha-v2.md` → Update von `alpha.md` (Multi-Beleg `raw/alpha-v1.md#S-2; raw/alpha-v2.md#S-3`), CREATE-Kandidat `raw/beta-v1.md`+`raw/beta-v2.md` (eindeutiger Term `quanten-observatorium-kanal`, kein bestehendes Concept → neue Concept-Datei mit term-geleitetem Pfad gemäß §5.15/§5.7 + Index-Link), `gamma.md`/`index.md` bleiben für unabhängiges Wissen byte-identisch bzw. um exakt eine Link-Zeile wachsen) — in den `/tmp`-Käfig gebaut und als `$BASE` committet, nie das reale Bundle. ## Verification diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 06cf843..731747a 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -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-22-2026 19:10 +last_updated: 08-23-2026 project: wow20 project_key: NOKEY tracking_system: file-system @@ -63,7 +63,7 @@ development_status: 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: 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 Epic-3-Abnahmegate abgeschlossen 2026-08-22: run-sandbox.sh (A Setup, B Käfig-Bau, C 12 Sandbox-Sub-Runs, D Validator-Agent, E Zwei-frische-Agenten A/B, F G-1..G-8, G Porcelain) — voller Lauf PASS_COUNT=70, FAILED=0, RUN_OK/Exit 0; Validator 7/7 SUCCESS über reales Bundle; A/B byte-identisch nur at-Ausnahme (§5.14 Pkt. 2); G-6 Negativ-Kontrolle erkennt Perturbation; G-8 Ist-Baum-Porcelain leer. Aufgedeckte und behobene reale Befunde: (1) wiki/log.md:42 zitat das Literal 'type: bundle' in Prosa → Validator Punkt-9-Verletzung, bedeutungserhaltend de-literalisiert (Bundle-Compliance); (2) G-7-Smoke überstreng gegen Bundleroot-Schema-Glossar (../schema/, compiler §5.6 'andere Schicht') + log.md-Protokoll (§5.6 Pkt. 2 Log-Exclusion) → Exclusion im Gate, Smoke bleibt hart für Concept-Bodies. Epic-3 bleibt in-progress bis zur Epic-3-Retrospektive (AC-9: Epic 3 erst nach done aller Stories 3.8..3.13). + 3-13-epic-3-verifikations-und-abnahmegate: in-progress # Story 3.13 Review-Loop-1-Auflösung 2026-08-23 in Arbeit (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.7–3.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). Grüner Re-Run (Voll-Lauf, ~2.5–3.5 h) + Review-Loop-2 ausstehend → finaler `done`-Flip danach. Epic-3 bleibt in-progress (AC-9: erst nach done aller Stories 3.8..3.13). epic-3-retrospective: optional epic-4: backlog diff --git a/wiki/log.md b/wiki/log.md index 88f8add..e17f966 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -1,5 +1,8 @@ # Log +## 2026-08-23 +- **Story 3.13 → Review-Loop-1-Auflösung + `in-progress` (Epic-3-Verifikations- und Abnahmegate, bmad-code-review Re-Run 4 Layer — blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor, 2026-08-22; Befundauflösung 2026-08-23):** Re-Review der Erst-Lauf-Implementierung (`sandbox-3-13/run-sandbox.sh`, 771 Z. vor Härtung); **Triage:** 28 eindeutige Befunde nach Dedup aus 43 rohen — **7 decision-needed / 15 patch / 2 defer / 6 dismissed; keine intent_gap/bad_spec (kein Loopback, Step-04 nicht ausgelöst).** **Nutzer-Entscheidung (ProMods):** „Ich folge Deinen Empfehlungen" — **D-3.13-1..6 = Option 1/1/2/1/1/1, D-3.13-7 = Option 1**: (1) AGENT_PROMPT nur Instruktion + Fixture-Fakten, Gate-Checks = A-vs-B + Nicht-Vakuum-Witness gegen Baseline; (2) Run-Receipt A-vs-B-Vergleich (Modell sandbox-3-8 DET-2, at-/Pfad-Zellen gefiltert, keine erwarteten Literale); (3) G-6 = echter perturbed committeter Tree `wt-c` + at-only-Kontrolle `wt-c2` durch denselben E.4-Classifier (deterministisch, kein 3. Agent; Negativ-Kontrolle MUSS `NON_AT≠0`, Positiv-Kontrolle MUSS `NON_AT=0`); (4) G-7 = generische Link-Auflösung + geschlossene, am realen Bundle validierte Planungstoken-Menge, relativ `$REPO_ROOT` (Ist-Baum, P-13); (5) AC-3-Negativ-Hälfte = separater /tmp-Käfig (D-NEG) mit 3 §7-/§6-Negativ-Fixtures (Punkt 7 fehlendes `generated.by`, Punkt 14/EC-3 kalenderinvalides `at: 2026-02-31`, Punkt 4 `resource: raw/../neg-3.md` ..-Traversal) + Positiv-Kontrolle — frischer Validator-Agent MUSS 3×FAIL + 1×SUCCESS liefern; (6) Fixture um CREATE-Fall (neue raw-Quellen `beta-v1/v2`, eindeutiger Term `quanten-observatorium-kanal` → neue Concept-Datei mit term-geleitetem Pfad §5.15/§5.7 + Index-Link — nicht hart-codiert; Gate detektiert die Datei über Git und prüft ihre Semantik); (7) **Status-Revert `done → in-progress`** (D-3.13-7; Präzedenz 3.7–3.12: finaler `done`-Flip = Step-05-Status-Sync nach konvergiertem Review-Loop + grünem Re-Run). **Patches (alle 15 angewendet):** P-1 (sandbox-3-1: harte Assertions + Exit-Pfade, S1–S6; re-executiert: 14 PASS, FAILED=0, SANDBOX-3-1-OK, Exit 0), P-2 (E.4-Classifier Frontmatter-geankert `+ at: `/`- at: ` statt Substring), P-3 (CLAUDE_BIN aus PATH + Feature-Checks `timeout`/`sha256sum`/`tar`/`cygpath`/`bash` mit klaren Fehlern), P-4 (Käfig = isolierte Mini-Fixture, `rm -rf` des realen `raw/`/`wiki/`-Inhalts vor Fixture-Bau; Bundleroot-Frontmatter `type: bundle`/`okf_version: "0.2"`; Root-Concepts ohne Areas — Punkt-11-sauber), P-5 (E.1 baut `wiki/`+`raw/` vollständig neu — keine §B-Reste in `$BASE`), P-6 (exakt-ein-Verdikt-pro-Datei hart: 0 → Exhaustivitäts-FAIL, >1 → Eindeutigkeits-FAIL; Verdict-Summe vs. Dateizahl), P-7 (Defizit-Route: FAIL-Verdikt → Non-Zero-Exit + Remediation-Text „Bedarf einer separat autorisierten Epic-1-Remediation"; Boundary/AC-4 gilt — frozen-Matrix-Zeile VALIDATOR_DEFIZIT „G-5 PASS" = Renegotiation-Kandidat, spec Change Log, frozen-Block unangetastet), P-8 (Marker hart: `VALIDATOR_DONE`/`RUN_DONE a|b` greppt, Abweichung = HARD-FAIL), P-9 (E.9: log.md ≠ Baseline + voller Baseline-SHA im Eintrag, kein vorgeschriebener Wortlaut), P-10 (raw/-Immutabilität hart: `git diff $BASE HEAD -- raw/` leer je Worktree), P-11 (Porcelain vollständig inkl. Untracked: A.5-Pre-Check + G-8-Endcheck + Negativ-Kontrolle im Käfig), P-12 (dieser Eintrag trägt die korrigierende Formulierung, s. u.), P-13 (G-7 relativ `$REPO_ROOT`), P-14 (Dead Code entfernt, `trap rm -rf $ROOT` EXIT, `fail()`/`FAILED`-Zweig lebendig), P-15 (spec Design Notes: `git diff --stat "$HEAD_A" "$HEAD_B" -- wiki/` — Commits statt Verzeichnis-Pfaden; ≠leer-Pin auf Allowance-leerer-Diff + Witness-Nicht-Vakuum; Fixture-Satz um CREATE-Kandidat). **Ergebnis:** gehärtetes Gate-Skript (839 Z., `bash -n` clean); grüner Voll-Lauf-Re-Run (~2.5–3.5 h, Agent-Läufe) + Review-Loop-2 ausstehend — finaler `done`-Flip danach (Präzedenz-konform). **KORREKTUR-Teileintrag (P-12, 2026-08-23-Beobachtung zum 2026-08-22-Eintrag oben):** die dortigen Zahlen „(20/27/40/51/60/68 PASS)" sind **kumulative `PASS_COUNT`-Schnappschüsse** am Ende der jeweiligen Gate-Abschnitte A/B/C/D/E/F/G (nicht pro-Sektion-Zählungen; Gesamt `PASS_COUNT=70` ist der Endergebnis-Wert — Reconciliation: 70 stimmt exakt mit dem damaligen Skript). `$BASE=5024d751c75e6d6135c33a02e08967099b2f887f` ist ein **/tmp-Käfig-Commit** (Zeitstempel-/Umgebungs-abhängig): Beleg für den damaligen Lauf, **nicht re-ableitbar** — kein Re-Derivations-Claim. Der Erst-Lauf-Befund (PASS_COUNT=70, FAILED=0, RUN_OK/Exit 0, Validator 7/7 SUCCESS, A/B nur-at, G-6-Perturbation erkannt, G-8-Porcelain leer) bleibt als Erst-Lauf-Evidenz gültig. **Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt:** `git status --porcelain -- wiki/` zeigt ausschließlich `wiki/log.md` (dieser 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-Abnahme (Story 3.13) bleibt offen (epic-3 in-progress). + ## 2026-08-22 - **Story 3.13 → Epic-3-Verifikations- und Abnahmegate abgeschlossen + `done` (run-sandbox.sh, pass, 2026-08-22):** Das repositoryweite Epic-3-Abnahmegate `_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh` ist erstellt und voll grün ausgeführt (RUN_OK, Exit 0, **PASS_COUNT=70, FAILED=0**), ausschließlich als `/tmp`-Käfig-Kopie des realen Clone-Baums — nie im Ist-Baum (AC-2). **Abschnitte:** (A) Setup — Repo-/Bundle-/Referenz-Check + Porcelain-Pre-Check (20 PASS); (B) Käfig-Bau — /tmp-Kopie von `schema/adapters/raw/wiki`, `git init` + Baseline, `core.autocrlf false`/`core.filemode false` (27 PASS); (C) **12 Sandbox-Sub-Runs** fail-fast (3-1..3-12, jede `exit 0`; 40 PASS); (D) **Validator-Agent** — frischer Kontext liest `schema/validator.md` (Rev 9) und stellt Verdikte `SUCCESS`/`FAIL ` über alle **7** `wiki/`-Dateien des realen Käfigs; das Gate prüft nur Verdikt-Grammatik (§5/§5.1) + Exhaustivität, urteilt nie selbst (D-3) — **7/7 SUCCESS, 0 FAIL** (51 PASS); (E) **Zwei-frische-Agenten A/B (AC-7)** — zwei getrennte Worktrees auf demselben committeten `$BASE` (Fixture: `wiki/index.md`+`alpha.md`+`gamma.md`+`log.md`, `raw/alpha-v1.md` Baseline, Zuwachs `raw/alpha-v2.md` als CONFIRMING-Beleg, `raw/gamma-v1.md`; $BASE=5024d751c75e6d6135c33a02e08967099b2f887f), jeder frischer Kontext mit `schema/compiler.md` als einziger Instruktionsquelle; Harness schreibt weder erwartete Bodies noch Lease-/Log-/Git-Ausgänge (AC-5); Commit-Boundary = Mutations-Boundary (Harness committet die Agent-Mutationen mechanisch, run-receipt.txt uncommittet); **A/B-byte-identisch bis auf `generated.at`-Ausnahme** (§5.14 Pkt. 2; diff` wiki/` zeigt nur at-Zeilen, NON-at-0), Multi-Beleg-Synthese `raw/alpha-v1.md#S-2; raw/alpha-v2.md#S-3` im Body, `sources` lexikografisch konsolidiert, `gamma.md`/`index.md` byte-identisch (AC-6) (60 PASS); (F) **G-1..G-8** — G-1 A/B-Determinismus, G-2 Validator-Integration, G-3 Validator-SUCCESS 7/7, G-4 12-Sub-Runs, G-5 Validator-Defizit-Route (kein Defizit), **G-6 Negativ-Kontrolle** (perturbierte Entscheidung im at-normalisierten Hash-Vergleich erkannt — kein false-PASS), G-7 Epic-5-Consumer-Smoke (AC-8; Bundleroot-Schema-Glossar per compiler §5.6 „andere Schicht" + `log.md`-Protokoll per §5.6 Pkt. 2 exkludiert, Concept-Bodies hart), G-8 wird in Abschnitt G geprüft (G-Szenarien abgeschlossen, PASS_COUNT=68); (G) **Endzustands-Invariante** — AD-3 read-only ohne Diff (`schema/validator.md`, `compiler.md`, `wiki-compiler.md`, `canonical-terms.md`, `adapters/`, `raw/`) + **Porcelain-Check** `git status --porcelain --untracked-files=no` leer (G-8 PASS) — **Gesamt RUN_OK, Exit 0**. **Aufgedeckte und behobene reale Befunde des Gates (Erst-Lauf 6):** (1) G-3-Fail — der frische Validator-Agent deckte einen realen Punkt-9-Verstoß im Bundle auf: `wiki/log.md` (Story-2.4-Review-Eintrag) zitierte das Punkt-9-Bundleroot-Typ-Literal in Prosa; Punkt 9 verbietet das Token in jeder Nicht-Bundleroot-Bundle-Datei (auch `log.md`). Validator korrekt, Defekt im Bundle: **bedeutungserhaltend de-literalisiert** (Bundleroot-`type`-Verbot statt Literal) — Bundle ist Punkt-9-valide; (2) G-7-Smoke überstreng gegen den dokumentierten Bundleroot-Schema-Glossar (`../schema/`-Links der `index.md`, compiler §5.6 „andere Schicht", AC-8 optionale Provenienz/Traceability) und das `log.md`-Protokoll (§5.6 Pkt. 2 „keine Link-/Provenienz-Schicht"): **G-7 im Gate auf die Compiler-Exclusion-Fassung geschärft** (exkludiert `wiki/index.md` + `wiki/log.md`, hält die Smoke-Pflicht für alle Concept-Bodies/Area-`index.md` hart — kein Planungs-Lese-Zwang). Keine Schema-/Validator-/Adapter-/`raw/`-Änderung (AD-3); keine neue §7-Klasse; kein Standalone (D-3); keine Wanduhr-Steuerung (A0-20). **Defer-Home geschlossen** (`deferred-work.md` append, Aufgegriffen-Block Story 3.13): DET-1/DET-2-Plan-/Form-Literale (Z. 551), R-9-Receipt-Literale (Z. 560), externer Validator gegen das reale committete Bundle (Z. 576), Zwei-Producer-``-Gewinner (Z. 594), A-2-Barriere/Überlappung (Z. 597), Zwei-Worktree-Übernahme-Rest (Z. 611), geteilter Ref-Namespace-/Zwei-Worktree-Übernahme-Beweis (Z. 629), DF1 Stash-/AC-5-kombiniert (Z. 633) — Home „Story-3.13-Abnahme" realisiert (Original-Defer-Blöcke unverändert; benannte Rest-Terme der Sandbox-Härtung bleiben offen). `sprint-status.yaml`-Key `3-13-epic-3-verifikations-und-abnahmegate` → **`done`** (finaler Step-05-Flip; AC-9: Epic 3 bleibt bis zur Epic-3-Retrospektive `in-progress`), `last_updated` → 08-22-2026 19:10. **Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt:** `git status --porcelain -- wiki/` zeigt ausschließlich `wiki/log.md` (dieser Eintrag); AD-3 read-only (`schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`/`schema/canonical-terms.md` unverändert); kein Standalone (D-3), keine neue §7-Invaliditätsklasse, keine Vertragsänderung; die Epic-3-Retrospektive (Story epic-3-retrospective) bleibt offen. - **Story 3.12 → Review-Loop-2-Abschluss + `done` (Lease-Lifecycle und Commit-Abschluss transaktional schließen, bmad-code-review 4 Layer — blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor, Diff `80480af..HEAD`, 2026-08-22):** Re-Review der konvergierten Loop-1-Implementierung (Diffform `80480af..HEAD`, 4 Dateien); **Triage:** 1 decision-needed / 7 patch / 2 defer / 6 dismissed; **keine intent_gap/bad_spec (kein Loopback).** **Nutzer-Entscheidung:** D-3.12-1 = **Option 1 (empfohlene Option)** — AC-6/§5.18-Pkt.-6-Grenze an die eigene `wiki/log.md`-Praxis angeglichen: dokumentierte Run-/Review-/Sandbox-Nachweise sind **notwendige Koordinationsereignisse** (Vertrag §5; 3.10/3.11-Praxis); die verbotene Kategorie ist Build-/Review-/Story-/Sandbox-**Historie**; §5.18 Pkt. 6 und AC-6 bleiben textuell unverändert; L-8-Kategorie-Grep schärft auf Kategorie-Hyphenate `(Build|Review|Story|Sandbox)-(Histor|Log|Protokoll|Bericht)` + Negativ-Kontrolle (verbotene Form auf Probe-Datei außerhalb `wiki/` muss den Guard auslösen). **Patches (7, angewendet):** Sandbox — P1 `scopelock_healthy()`-Helfer + LOCK_READ_ERROR-Propagation vor jeder Liveness-/Release-/Takeover-Entscheidung (ein korrupter Lock wird nie mehr als „Halter hält nicht mehr" klassifiziert, AC-1-Gate bleibt geschlossen), P2 Release-Fehler negativ geübt (L-7: `scopelock_release` mit falscher Inhaber-Run-ID → harter Fehlschlag, Lock unverändert) + L-6 „valide committet" hart verifiziert (HEAD ≠ Baseline, Frontmatter im Commit, Commit-Betreff), P3 Setup-Robustheit (Exit-Checks `mktemp`/`mkdir`/`cd`/`git init`, verifizierter `$BASE`-Baseline-Commit, L-3/L-4-Backup-`cp` byte-identisch vor `checkout`/`clean`; bewusst kein `set -e` — negatives Assertion-Idiom), P4 L-9-KP2/KP3 Ghost-Diff-Probe (`assert_invariant` via `inv_viol`, Index **und** Worktree) + `reg_write` real geübt in L-1, P5 AC-1-Ownership-Stale-Zweig positiv getestet (L-1-Ende) + L-2 Takeover-Exactly-once direkt am Takeover-Pfad (zweiter CAS mit altem Old-Value schlägt atomar fehl, kein Clobber); Spec/Sync — S1 Frontmatter `review_loop_iteration: 0 → 1`, doppelte `## Spec Change Log` entfernt, ``-Schließtag ergänzt, Status-Sync vereinheitlicht (dieser Eintrag); S2 SRO-/Code-Map-/deferred-work-Anker auf Ist-Zeilen neu gesetzt. **Defer (2, → deferred-work.md append-only, Block „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). **Sandbox re-executiert:** `bash _bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` (Windows/Git-Bash, `/tmp`-Baum, nie der reale `wiki/`-/`raw/`-Baum) → **L-1..L-9, 9/9 harte PASS, Exit 0**. **Abschlussklausel:** keine Änderung an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/` (AD-3); keine neue §7-Invaliditätsklasse; kein neuer Frontmatter-/Format-Key; kein Standalone (D-3); keine Vertragsänderung; §5.11-Pkt.-1/2-Wortlaut, §5.12-, §5.13- und §5.17-Anker textuell unverändert (additive Klammer, Fugen-Identität); keine Wanduhr-/Systemzeitsteuerung (A0-20). **Validator-Verdikt** (human-mechanisch, `schema/validator.md` Rev 9, D-3 — kein CLI; keine Concept-Inhalts-Mutation): alle `wiki/`-Dateien **SUCCESS**. `sprint-status.yaml`-Key `3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen` → **`done`** (finaler Step-05-Flip nach konvergiertem Review-Loop-2, Präzedenz Story 3.7/3.8/3.9/3.10/3.11), `last_updated` → 08-22-2026 07:44. **Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt:** `git status --porcelain -- wiki/` zeigt ausschließlich `wiki/log.md` (dieser Eintrag); AD-3 read-only (`schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`/`schema/canonical-terms.md` unverändert); kein Standalone (D-3), keine neue §7-Invaliditätsklasse, keine Vertragsänderung; Epic-3-Abnahme (Story 3.13) bleibt offen (epic-3 in-progress); Staleness-Nachfolge bleibt offen.