diff --git a/_bmad-output/implementation-artifacts/deferred-work.md b/_bmad-output/implementation-artifacts/deferred-work.md index 3d25ec2..f898edb 100644 --- a/_bmad-output/implementation-artifacts/deferred-work.md +++ b/_bmad-output/implementation-artifacts/deferred-work.md @@ -551,5 +551,10 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein - **DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert":** `plan=$(printf 'cand=alpha;form=update;baseline=%s')` (run-sandbox.sh:189) und `echo "form=update"` in `bs_a()` (Z. 221) sind wörtliche Skript-Konstanten; B1==B2-Identität damit trivial. — Defer-Grund: Loop-1-/Loop-2-Präzedenz (Loop-1 hat „DET-2-Plan-Literal (dev-demo, echte Content-Hashes decken)" rejected; DET-1 trägt den Demo-Kommentar Z. 211-214); die Nicht-Vakuum-Assertion tragen die echten Content-Hashes. Home: Story-3.13-Abnahme (echte Gate-Runs über die Instruktion). — status: offen - **`norm()`-Locale-Pinning (`LC_ALL`) fehlt:** unter C/POSIX-Locale zerfällt die Byte-Klasse `[–—]` in {E2,80,93,94} (übermatcht z. B. `…`/`„`), `tr 'A-Z' 'a-z'` ist locale-abhängig → die Kanon-Form hängt am Host-Locale statt am committeten Git-State. — Defer-Grund: Testharness-Host-Locale-Abhängigkeit (Host = Ubuntu-Sandbox, dort UTF-8-Default; auf C-Locale-Hosts falsch), keine normative Semantik der Instruktion. Home: Sandbox-Härtung (gemeinsam mit den Hards-Guards). — status: offen - **Mehrfach-Term-Vereinigung unübend:** §3.2 Pkt. 1c/§5.14 Pkt. 5 pinnen die Vereinigung der Treffer über alle Terme (besuchte Menge, Zuwachs-Sicht-Ordnung) als „vollständig pinbar" — die Sandbox übt nur den Einzel-Term-Pfad; eine Regressions in Union-/Dedup-/visited-Set-Logik würde unbemerkt durchgehen. — Defer-Grund: Story-3.8-Scope (Bestätigungs-Mechanik + Lückenschließung) ist erfüllt; ausführbare Union-Coverage gehört zur deterministischen Term-Gewinnung/Routing. Home: Story 3.9. — **status: offen → aufgegriffen (Story 3.9, 2026-08-20):** in `schema/compiler.md` §5.15 Pkt. 1 verankert (Mehrfach-Term-Vereinigung D-8: kollabierter Gesamt-Name als primärer Term + jedes ≥2-Zeichen-Segment als weiterer Term; Einzelzeichen-Segmente -a/-b tragen keinen eigenen Term); Sandbox R-1 übt die Vereinigung real aus (`beta-kommunikation` → `beta beta-kommunikation kommunikation`, Sidecar-exkludiert, Diff↔Manifest-Äquivalenz) -- **Orphan-Term hand-gesteuert + feste 2-Datei-Scan:** DET-8 pinnen den Term `fremdes` und scannen nur `wiki/alpha.md wiki/gamma.md` (run-sandbox.sh:669); die Determinismus der Orphan-ermittlung (Term-Ableitung aus dem `raw/`-Zuwachs, voller Kandidaten-Scan über Stufen a/b/c) ist nicht ausgeübt — nur die Log-Seite wird hart assertiert. — Defer-Grund: Mechanik ist textuell korrekt (DET-8-Assertions Z. 688-692 solide); Term-Derivation ist Story-3.9-AC („geschlossener, geordneter Algorithmus oder persistiertes Term-Manifest"). Home: Story 3.9. — **status: offen → aufgegriffen (Story 3.9, 2026-08-20):** Sandbox R-5 leitet die Terme AUS dem committeten `raw/`-Zuwachs ab (`git diff --name-only HEAD~1 -- raw/`, Dateiname→Term) und scannt den VOLLEN Kandidaten-Baum (`wiki/*.md`-Glob) statt eines festen 2-Datei-Scans — Orphan-/CREATE-Term-Derivation mechanisch ausgeübt (D-9; §5.15 Pkt. 1) +- **Orphan-Term hand-gesteuert + feste 2-Datei-Scan:** DET-8 pinnen den Term `fremdes` und scannen nur `wiki/alpha.md wiki/gamma.md` (run-sandbox.sh:669); die Determinismus der Orphan-ermittlung (Term-Ableitung aus dem `raw/`-Zuwachs, voller Kandidaten-Scan über Stufen a/b/c) ist nicht ausgeübt — nur die Log-Seite wird hart assertiert. — Defer-Grund: Mechanik ist textuell korrekt (DET-8-Assertions Z. 688-692 solide); Term-Derivation ist Story-3.9-AC („geschlossener, geordneter Algorithmus oder persistiertes Term-Manifest"). Home: Story 3.9. — **status: offen → aufgegriffen (Story 3.9, 2026-08-20):** Sandbox R-5 leitet die Terme AUS dem committeten `raw/`-Zuwachs ab (`git diff --name-only HEAD~1 -- raw/`, Dateiname→Term) und scannt **Stufe a** über den Root-`wiki/*.md`-Glob statt eines festen 2-Datei-Scans — Orphan-/CREATE-Term-Derivation auf der Stufe-a-Ebene mechanisch ausgeübt (D-9; §5.15 Pkt. 1). **Scope-Präzisierung (Loop-3, 2026-08-21):** die ausgeübte Termination-Derivation + Scan beschränkt sich auf **Stufe a über den Root-`wiki/*.md`-Glob** (keine Area-Rekursion, keine Stufen b/c); die volle Stufen-b/c-Traversal-Coverage des Kandidaten-Baums bleibt verbleibend offen (Home: Story 3.13-Abnahme / Sandbox-Härtung — benannter Defer, keine instruktionsseitige Lücke, §5.15 Pkt. 2/3 verankern die Stufen) - **Umlaut-vs-Transkription im Match-Pfad unübend:** die §3.2-Normalisierung deckt Umlaut-Divergenzen („Schlüssel" vs. „Schluessel") nicht ab, und kein DET-Szenario treibt einen Umlaut-Term durch den Match-Pfad. — Defer-Grund: bereits „Aufgegriffen (teilweise)" dokumentiert (Block oben, Home: Sandbox-Vereinheitlichung oder folgende Compiler-Instruktions-Revision; „kein Instruktions-Defekt") — keine neue offene Frage, nur fehlende Sandbox-Abdeckung. Home: wie dort. — status: offen + +## Deferred from: code review of spec-3-9-deterministische-relevanz-und-reconcile-routing-schliessen (2026-08-21) + +- **`norm()`/`tr` ohne `LC_ALL`-Pinning (Sandbox-3.9, 952 Zeilen):** unter C/POSIX-Locale zerfällt die Byte-Klasse `[–—]` in {E2,80,93,94} (übermatcht z. B. `…`/`„`), `tr 'A-Z' 'a-z'` ist locale-abhängig → die Kanon-Form hängt am Host-Locale statt am committeten Git-State. — Defer-Grund: bekannter Story-3.8-Defer (D-7, Block oben „`norm()`-Locale-Pinning fehlt"), hier in den Sandbox-3.9-Scope ausgeweitet (alle R-Szenarien + R-9-Worktree-Läufe laufen auf derselben `norm()`); keine normative Instruktion-Semantik (Instruktion nennt Kollaps-Klasse + lowercasing, keine Locale-Bindung). Home: Sandbox-Härtung (gemeinsam mit den Story-3.8-Hard-Guards). — status: offen +- **Positive Zwei-Run-Identität strukturell trivial (R-9 clean A==B):** Plan-/Form-/Routing-Literale der `run9_worktree()`-Receipts sind Skript-Konstanten; die A==B-Identität folgt daraus, nicht aus realer Ausführung; die Nicht-Vakuum-Last tragen die echten Content-Hashes (Bundle-BYTE-Vergleich) und die Negativ-Kontrolle (divergent ≠ clean, hart FAIL). — Defer-Grund: Präzedenz Story-3.8-Defer „DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert"" (Block oben, Home: Story-3.13-Abnahme, echte Gate-Runs über die Instruktion); Story-3.9-Sandbox folgt demselben Demo-Muster. Home: Story-3.13-Abnahme. — status: offen diff --git a/_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh b/_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh index 9d1d374..a60647d 100644 --- a/_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh +++ b/_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh @@ -1,30 +1,43 @@ #!/usr/bin/env bash # ============================================================================= # Sandbox Story 3.9 — Deterministische Relevanz- & Reconcile-Routing schließen -# (Loop-2, Re-Ableitung nach Review-Loop-2 bad_spec: BS-L2-1/BS-L2-2 + Patch-Fixes) +# (Loop-2-Re-Ableitung nach bad_spec BS-L2-1/BS-L2-2 + Loop-3-Minimal-Härtung +# D-3.9-3 Option 2: tautologische Szenarien => echte Mechanik/real gefeuerte +# negative Kontrollen; strukturelle Restlimits als benannte Defers, Home 3.13) # # Re-executierbarer Nachweis der §5.15-Verankerung (schema/compiler.md, Rev 3.4). # Baut einen isolierten /tmp-Baum auf, berührt NIE den realen Ist-Baum # (wiki/ raw/ lease/ registry/ scratch/ unter $ROOT im /tmp). +# Re-Run-idempotent: veraltete Worktrees/Admin-Verzeichnisse unter $ROOT werden +# vor jedem Lauf hart entfernt (Loop-3/P-3.9-14). # Exit 0 nur bei: alle R-Szenarien harte PASS, 0 HARD-FAIL, AD-3-/Erhaltungs-Claims. # # R-1 TERMGEWINNUNG_GESCHLOSSEN (AC-1: Termgewinnung, Sidecar-Exklusion, -# Status-Codes, Diff<->Manifest-Äquivalenz + Negativ-Manifest = FAIL) -# R-1b STATUS_GUARD_RC (AC-4-Loop-2-BS-L2-2: R => Guard-FAIL, -# C wie A — Status-Codes über die Zuordnung geschlossen) +# Status-Codes, Diff<->Manifest-Äquivalenz + Negativ-Manifest = echter +# Run-FAIL-Sub-Run vor jeder Mutation, Loop-3/D-3.9-3) +# R-1b STATUS_GUARD_RC (AC-4/BS-L2-2: R => Guard-FAIL, C wie A — +# Status-Codes in sauberen je-Status-Sub-Runs geschlossen) # R-2 NORMALISIERUNG_SYMMETRISCH (AC-2: symmetrische Normalisierung + # literal-sichere Suche; index.md-Treffer => Traversal-Stufe, real gefeuert) # R-3 ROUTING_TABELLE_EXKLUSIV (AC-3: UPDATE/CREATE/ORPHAN-HOLD/NO_OP; -# NO_OP als Update-Unter-Entscheidung; NO_MATCH neuer Einheit => CREATE, nicht NO_OP) -# R-5 NEUE_EINHEIT_KEIN_NOOP (Loop-2-BS-L2-1: neue Einheit mit leerer -# Candidate-Liste => CREATE-Bewertungsraum, NICHT NO_OP) -# R-6 RESERVED_ZIEL_INDEX (AC-5: Slug 'index', abgeleitet, nie hart; -# git ls-tree-Ist-Dateimenge) -# R-7 RESERVED_ZIEL_LOG (AC-5: Slug 'log', abgeleitet) +# NO_OP als Update-Unter-Entscheidung mit echtem Full-Containment-Check; +# NO_MATCH neuer Einheit => CREATE, nicht NO_OP; Stufe-b via echtem +# index.md-Link-Parsing + Zielpfad-Existenz, Loop-3/D-3.9-3) +# R-4 RAW_GUARD (AC-4: M/D/R => Run-FAIL vor jeder Mutation +# — reale je-Status-Sub-Runs mit Mutation-Sentinel; A akzeptiert) +# R-5 NEUE_EINHEIT_KEIN_NOOP (BS-L2-1: neue Einheit mit leerer +# Candidate-Liste => CREATE-Bewertungsraum via echter route()-Funktion, +# NICHT NO_OP; D-8-Vereinigung ausgeübt, Loop-3/D-3.9-3) +# R-6 RESERVED_ZIEL_INDEX (AC-5: Slug 'index' rein aus committetem +# Dateinamen abgeleitet (nur dokumentierte Regel), case-insensitive +# Reserviertheits-Prüfung; git ls-tree-Ist-Dateimenge) +# R-7 RESERVED_ZIEL_LOG (AC-5: Slug 'log', abgeleitet; Hold) # R-8 HOLD_ZWEI_WORKTREE (BS-L2-4: Hold über zwei getrennte Worktrees -# statt Doppel-Read; tote CAND einzeln hart assertiert, kein || true) +# statt Doppel-Read; Receipt aus echter Mechanik (Slug/CAND aus committetem +# Zuwachs); tote CAND einzeln hart; Hold-Assert auf BEIDE Worktrees) # R-9 ZWEI_RUN_NEGATIV (AC-6/BS-L2-6: positive Identität NICHT-VAKUUM -# + negative AD-16-Abweichung hart FAIL + at-Exzeption praktisch + Norm-Pin) +# + negative AD-16-Abweichung hart FAIL + at-Exzeption praktisch + Norm-Pin; +# Bundle-Byte-Vergleich mit benannter, ankerter index/log-Ausnahme) # ============================================================================= set -u # P-1: set -e im Hauptkörper (sandbox-3-8-Härtung) — kein false-PASS. @@ -175,8 +188,8 @@ for f in $ZUWACHS; do [ -n "$t" ] || { echo "HARD-FAIL (R-1): leerer Term aus $f" >&2; exit 1; } [ -n "$TERMS" ] && TERMS="$TERMS $t" || TERMS="$t" done -# deterministisch sortieren (STDIN-stabil) -TERMS=$(printf '%s\n' $TERMS | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//') +# deterministisch sortieren (STDIN-stabil; -u: Vereinigung dedupliziert, C-Kollation) +TERMS=$(printf '%s\n' $TERMS | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//') echo " Gewonnene Term-Liste (deterministisch geordnet): $TERMS" # F9: Erwartung AUS dem Zuwachs ableiten (kein hart kodierter Erwartungswert): # gleiche Dateiname->Term-Synthese wie der Produzentenpfad (norm + Segmente >= 2). @@ -193,7 +206,7 @@ for f_ in $ZUWACHS; do esac done done -EXPECT=$(printf '%s\n' $EXPECT | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//') +EXPECT=$(printf '%s\n' $EXPECT | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//') [ "$TERMS" = "$EXPECT" ] || { echo "HARD-FAIL (R-1): abgeleitete Term-Liste '$TERMS' != erwartete Ableitung '$EXPECT' (D-8 aus Zuwachs; Sidecar trägt keinen Term; Datums-Suffix gestrippt)" >&2; exit 1; } echo " D-8-Vereinigung aus dem Zuwachs abgeleitet und gegengeprüft (primärer Term + >=2-Zeichen-Segmente; Einzelzeichen-Segmente tragen keinen Term)" echo " D-8-Vereinigung: primärer Term 'beta-kommunikation' + Segmente 'beta','kommunikation' (=>=2 Zeichen), Einzelzeichen-Segmente tragen keinen Term" @@ -219,59 +232,92 @@ sources: terms: - beta-KOMMUNIKATION EOF +# Run-FAIL-Sub-Run (Loop-3/D-3.9-3: echte Mechanik statt String-Vergleich): +# Der Sub-Run übt die Manifest-Äquivalenz-Regel aus — Divergenz => Exit 1 VOR +# jeder Mutation. Mutation-Nachweis: nur ein äquivalentes Manifest darf den +# Mutation-Sentinel anlegen; nach dem Run-FAIL muss er ABSEIT bleiben. BAD_TERMS=$(awk '/^terms:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); print}' "$ROOT/manifest-r1-bad.yaml" | tr '\n' ' ' | sed 's/ $//') -if [ "$BAD_TERMS" = "$TERMS" ]; then - echo "HARD-FAIL (R-1): divergentes Manifest wurde als gleich erfolgreich behandelt (Negativ-Zweig defekt)" >&2; exit 1 -else - echo " Negativ-Manifest erkannt (divergente Terme '$BAD_TERMS' != '$TERMS') -> Run-FAIL-Pfad korrekt vorbereitet (§5.15 Pkt. 1 Rn-FAIL vor Mutation)" +R1MUT="$r1dir/.r1-mutation-sentinel" +rm -f "$R1MUT" +G_R1=0 +( + set -e + MT="" + while IFS= read -r t; do + [ -n "$t" ] && MT="$MT $(norm "$t")" + done < <(awk '/^terms:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); print}' "$ROOT/manifest-r1-bad.yaml") + MT=$(printf '%s\n' $MT | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//') + if [ "$MT" != "$TERMS" ]; then + echo " GUARD-FAIL (R-1 Negativ): divergentes Manifest-Term-Set '$MT' != Zuwachs-Terme '$TERMS' => Run-FAIL vor jeder Mutation (exit 1)" >&2 + exit 1 + fi + # Nur erreichbar bei äquivalentem Manifest: Mutation wird ausgeführt (Sentinel). + touch "$R1MUT" +) || G_R1=$? +if [ "$G_R1" -ne 1 ]; then + echo "HARD-FAIL (R-1): Negativ-Sub-Run endete mit Exit $G_R1 (1 erwartet — Run-FAIL vor Mutation)" >&2; exit 1 fi -echo "RESULT: PASS — R-1: TERMGEWINNUNG_GESCHLOSSEN — Zuwachs-Sicht -> deterministic Term-Liste (AC-1); Sidecar exkludiert; Mehrfach-Term-Vereinigung (D-8) abgeleitet; Diff↔Manifest-Äquivalenz Positiv+N Negativ assertiert; kein freies Producer-Urteil; Norm-Kollaps §3.2-Pkt.-1b" +[ -e "$R1MUT" ] && { echo "HARD-FAIL (R-1): Mutation trotz Run-FAIL ausgeführt (Sentinel vorhanden)" >&2; exit 1; } +echo " Negativ-Manifest => realer Run-FAIL (exit 1 vor jeder Mutation, Mutation-Sentinel abseits) — §5.15 Pkt. 1" +echo "RESULT: PASS — R-1: TERMGEWINNUNG_GESCHLOSSEN — Zuwachs-Sicht -> deterministic Term-Liste (AC-1); Sidecar exkludiert; Mehrfach-Term-Vereinigung (D-8) abgeleitet; Diff↔Manifest-Äquivalenz Positiv + Negativ (echter Run-FAIL-Sub-Run) assertiert; kein freies Producer-Urteil; Norm-Kollaps §3.2-Pkt.-1b" # ============================================================================= # R-1b STATUS_GUARD_RC (Loop-2 / BS-L2-2) — Status-Codes inkl. R/C # ============================================================================= -runlabel "R-1b: STATUS_GUARD_RC — R => Run-FAIL (AD-3-Rename-Verletzung), C wie A akzeptiert (AC-4, BS-L2-2)" +runlabel "R-1b: STATUS_GUARD_RC — R => Run-FAIL (AD-3-Rename-Verletzung), C wie A akzeptiert (AC-4, BS-L2-2); saubere je-Status-Sub-Runs (Loop-3/D-3.9-3: keine kumulative B1-Baseline)" +# Fall R: Rename einer committeten raw/-Datei (AD-3-Verletzung => Guard-FAIL). +# Der Sub-Run prüft den Eigen-Zuwachs (HEAD~1..HEAD, genau R) und MUSS als +# Run-FAIL mit Exit 1 enden — keine kumulative Baseline, kein A-Mitbefund. r1bdir=$(isolate r1b) cd "$r1bdir" -cat > wiki/alpha.md <<'EOF' ---- -type: concept -sources: - - resource: raw/alpha-v1.md - id: s1 -generated: - by: wow-compiler/0.1.0 - at: 2026-08-16T09:00:00Z ---- -Alpha-Protokoll. -EOF cat > raw/alpha-v1.md <<'EOF' ### S-1 Evidenz Alpha. EOF git add -A && git commit -qm "R-1b Baseline" -B1=$(git rev-parse HEAD) -# Fall R: Rename einer committeten raw/-Datei (AD-3-Verletzung => Guard-FAIL). git mv raw/alpha-v1.md raw/alpha-umbenannt.md git commit -qm "Rename committeter raw/-Datei" -ST_R=$(git diff --name-status "$B1" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') -echo " Status nach Rename: $ST_R" -case "$ST_R" in - *R*) echo " BEFUND: Status R (Rename) erkannt — Raw-Immutability-Guard aktiviert (AD-3-Rename = Run-FAIL)";; - *) echo "HARD-FAIL (R-1b): Rename wurde nicht als Status R erkannt (Guard blind für R)" >&2; exit 1;; -esac -# Fall C: Copy als neue/versionierte Source (C zählt wie A) — akzeptiert. -cd "$r1bdir" -# (einfacher: neue added Datei, oder direkter copy-Status via --find-copies) -cp raw/alpha-umbenannt.md raw/alpha-copy.md +G_R1B=0 +( + set -e + ST_R=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') + echo " Status nach Rename (Eigen-Zuwachs): $ST_R" + case "$ST_R" in + *R*) echo " GUARD-FAIL (R-1b): Zuwachs-Befund R (Rename) -> Run-FAIL vor Mutation (exit 1)" >&2; exit 1;; + *) echo "HARD-FAIL (R-1b): Rename wurde nicht als Status R erkannt (Guard blind für R)" >&2; exit 2;; + esac +) || G_R1B=$? +if [ "$G_R1B" -ne 1 ]; then + echo "HARD-FAIL (R-1b): Guard-Sub-Run (R) endete mit Exit $G_R1B (1 erwartet — Run-FAIL)" >&2; exit 1 +fi +echo " BEFUND: Status R (Rename) => Run-FAIL real gefeuert (AD-3-Rename = Run-FAIL, §5.15 Pkt. 4)" +# Fall C: Copy als neue/separat versionierte Source (C zählt wie A) — akzeptiert. +# Separater sauberer Sub-Run: Eigen-Zuwachs enthält NUR die Copy (A/C) => +# Guard passiert, Run-Status ok. +r1cdir=$(isolate r1c) +cd "$r1cdir" +cat > raw/alpha-v1.md <<'EOF' +### S-1 +Evidenz Alpha. +EOF +git add -A && git commit -qm "R-1c Baseline" +cp raw/alpha-v1.md raw/alpha-copy.md git add -A && git commit -qm "Copy einer Source" -ST_C=$(git diff --name-status "$B1" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') -# git meldet Copy nur mit -C/--find-copies; wir akzeptieren A oder C als "wie A". -case "$ST_C" in - *A*|*C*) echo " BEFUND: Copy/neue Source (A/C) akzeptiert — separat versioniert, Guard passiert";; - *) echo "HARD-FAIL (R-1b): Copy wurde nicht als A/C akzeptiert (Status $ST_C)" >&2; exit 1;; -esac -echo "RESULT: PASS — R-1b: STATUS_GUARD_RC — Status-Codes geschlossen (R => Guard-FAIL, C/A wie A akzeptiert) — AC-4, BS-L2-2, §5.15 Pkt. 4" +G_R1C=0 +( + set -e + ST_C=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') + echo " Status nach Copy (Eigen-Zuwachs): $ST_C" + case "$ST_C" in + *M*|*D*|*R*) echo "HARD-FAIL (R-1c): unerwarteter M/D/R im Copy-Sub-Run (Soll rein A/C)" >&2; exit 2;; + *A*|*C*) echo " BEFUND: Copy/neue Source (A/C) akzeptiert — separat versioniert, Guard passiert (exit 0)";; + *) echo "HARD-FAIL (R-1c): Copy wurde nicht als A/C erkannt (Status $ST_C)" >&2; exit 2;; + esac +) || G_R1C=$? +if [ "$G_R1C" -ne 0 ]; then + echo "HARD-FAIL (R-1c): Copy-Sub-Run endete mit Exit $G_R1C (0 erwartet — akzeptiert)" >&2; exit 1 +fi +echo "RESULT: PASS — R-1b: STATUS_GUARD_RC — Status-Codes geschlossen (R => Guard-FAIL real gefeuert, C/A wie A akzeptiert) in sauberen je-Status-Sub-Runs — AC-4, BS-L2-2, §5.15 Pkt. 4" # ============================================================================= # R-2 NORMALISIERUNG_SYMMETRISCH (AC-2) — index.md real gefeuert @@ -381,13 +427,27 @@ new_content=$(tr -d '\r' < raw/alpha-identisch.md | awk 'BEGIN{n=0}/^###/{n++;ne body_content=$(tr -d '\r' < wiki/alpha.md | awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr '\n' ' ') new_norm=$(norm "$new_content") body_norm=$(norm "$body_content") -# Ein echter neuer Beleg müsste im Body NICHT vorhanden sein; hier enthalten die -# Body-Aussagen „deterministische init-sequenz" bereits — kein Neu-Beleg. -if printf '%s' "$body_norm" | grep -qF "$(printf '%s' "$new_norm" | cut -d' ' -f1-2)"; then - echo " Fall 2: neue Evidenz saemtlich im bestehenden Body enthalten — kein neuer Beleg -> NO_OP (Update-Unter-Entscheidung, Zelle 4)" +# ECHTER Containment-Check (Loop-3/D-3.9-3): jeder Token der neuen Evidenz muss +# im bestehenden Body vorkommen (Full-Containment, kein N-Wort-Präfix-Abbruch). +MISSING="" +for tok in $new_norm; do + printf '%s' "$body_norm" | grep -qF -- "$tok" || MISSING="$MISSING $tok" +done +MISSING="${MISSING# }" +if [ -z "$MISSING" ]; then + echo " Fall 2: neue Evidenz vollständig im bestehenden Body enthalten (Full-Containment, keine fehlenden Tokens) — kein neuer Beleg -> NO_OP (Update-Unter-Entscheidung, Zelle 4)" else - echo "HARD-FAIL (R-3): NO_OP-Kriterium nicht auf echte Evidenz gegründet (Kein-New-Beleg-Vergleich inkonsistent)" >&2; exit 1 + echo "HARD-FAIL (R-3): NO_OP-Kriterium nicht auf echte Evidenz gegründet (fehlende Tokens im Body: '$MISSING')" >&2; exit 1 fi +# Negativ-Kontrolle des Checks: eine Evidenz mit einem NICHT enthaltenen Token +# muss denselben Token-Loop als NICHT vollständig enthalten melden (der +# Containment-Vergleich ist kein stilles Immer-Wahr). +NEG_MISS="" +for tok in $new_norm gar-nicht-im-body-token; do + printf '%s' "$body_norm" | grep -qF -- "$tok" || NEG_MISS="$NEG_MISS $tok" +done +NEG_MISS="${NEG_MISS# }" +[ "$NEG_MISS" = "gar-nicht-im-body-token" ] || { echo "HARD-FAIL (R-3): Negativ-Kontrolle defekt — Token-Loop meldet '$NEG_MISS' (Soll exakt das fehlende Token)" >&2; exit 1; } # --- Fall 3: CREATE (neue eigenständige Einheit, leerer Match) --- cat > raw/delta-protokoll.md <<'EOF' ### S-1 @@ -414,96 +474,116 @@ cat > wiki/area/index.md <<'EOF' - [Verwaister-Concept](verwaister-concept.md) EOF git add -A && git commit -qm "Area-Index mit Dangling-Link" +# ECHTE Stufe-b-Resolution (Loop-3/D-3.9-3): Links AUS dem realen index.md +# parsen; Zielpfad-Existenz pro Link prüfen. Dangling-Link (Ziel fehlt) => +# ORPHAN/HOLD; auflösender Link (Ziel existiert) => kein Orphan. +STUFE_B_DANGLING="" +STUFE_B_RESOLVED="" +while IFS= read -r link; do + [ -n "$link" ] || continue + if [ -f "wiki/area/$link" ]; then + STUFE_B_RESOLVED="$STUFE_B_RESOLVED $link" + else + STUFE_B_DANGLING="$STUFE_B_DANGLING $link" + fi +done < <(grep -oE '\]\([^)]+\.md\)' wiki/area/index.md | sed -E 's/^\]\(//; s/\)$//') +STUFE_B_DANGLING="${STUFE_B_DANGLING# }" +STUFE_B_RESOLVED="${STUFE_B_RESOLVED# }" +[ "$STUFE_B_DANGLING" = "verwaister-concept.md" ] || { echo "HARD-FAIL (R-3): Stufe-b-Dangling-Erkennung '$STUFE_B_DANGLING' (Soll verwaister-concept.md)" >&2; exit 1; } +[ -z "$STUFE_B_RESOLVED" ] || { echo "HARD-FAIL (R-3): unerwartete aufgelöste Links '$STUFE_B_RESOLVED'" >&2; exit 1; } [ -f "wiki/area/verwaister-concept.md" ] && { echo "HARD-FAIL (R-3): Stufe-b-Dangling existiert doch" >&2; exit 1; } -STUFE_B_CAND="verwaister-concept" -[ "$STUFE_B_CAND" = "verwaister-concept" ] && ROUTE_STUFE_B="ORPHAN/HOLD" || true +ROUTE_STUFE_B="ORPHAN/HOLD" [ "$ROUTE_STUFE_B" = "ORPHAN/HOLD" ] || { echo "HARD-FAIL (R-3): Stufe-b-Dangling nicht ORPHAN/HOLD" >&2; exit 1; } -echo " Stufe-b-Zelle: Dangling-Link -> ORPHAN/HOLD (kein CREATE, §5.15 Pkt. 3)" +echo " Stufe-b-Zelle: Dangling-Link aus echtem index.md-Parsing (Zielpfad existiert nicht) -> ORPHAN/HOLD (kein CREATE, §5.15 Pkt. 3)" echo "RESULT: PASS — R-3: ROUTING_TABELLE_EXKLUSIV — UPDATE/CREATE/ORPHAN-HOLD/NO_OP; NO_OP via echtem Evidenzvergleich (Update-Unter-Entscheidung); NO_MATCH neuer Einheit => CREATE; Stufe-b konsistent (AC-3, §5.15 Pkt. 3)" # ============================================================================= # R-4 RAW_GUARD (AC-4) — M/D/R, A/C # ============================================================================= -runlabel "R-4: RAW_GUARD — committete raw/-Datei M/D/R => Run-FAIL vor jeder Mutation; nur A/separat versioniert akzeptiert (AC-4, AD-3)" -r4dir=$(isolate r4) -cd "$r4dir" +runlabel "R-4: RAW_GUARD — committete raw/-Datei M/D/R => Run-FAIL vor jeder Mutation (Mutation-Sentinel bleibt abseits); nur A/separat versioniert akzeptiert (AC-4, AD-3); saubere je-Status-Sub-Runs (Loop-3/D-3.9-3)" +# --- Fall M: Modifikation => Run-FAIL VOR jeder Mutation --- +r4mdir=$(isolate r4m) +cd "$r4mdir" cat > raw/alpha-v1.md <<'EOF' ### S-1 Evidenz Alpha. EOF +git add -A && git commit -qm "R-4m Baseline" +printf '\n### S-9\nNachlage.\n' >> raw/alpha-v1.md +git add -A && git commit -qm "Modifikation committeter raw/-Datei" +G_M=0 +( + set -e + # GUARD vor jeder Mutation: der Sub-Run prüft den Eigen-Zuwachs (HEAD~1..HEAD) + # und darf die Mutation (Sentinel) NUR nach Guard-Pass ausführen. + ST_M=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') + case "$ST_M" in + *M*) echo " GUARD-FAIL (R-4m): Zuwachs-Befund M -> Run-FAIL vor jeder Mutation (exit 1)" >&2; exit 1;; + *) echo "HARD-FAIL (R-4m): M nicht erkannt (Status $ST_M)" >&2; exit 2;; + esac + # Unerreicht bei Guard-FAIL: Mutation würde hier ausgeführt (Sentinel). + touch "$r4mdir/.guard-mutation-sentinel" +) || G_M=$? +if [ "$G_M" -ne 1 ]; then + echo "HARD-FAIL (R-4m): Guard-Sub-Run (M) endete mit Exit $G_M (1 erwartet — Run-FAIL)" >&2; exit 1 +fi +[ -e "$r4mdir/.guard-mutation-sentinel" ] && { echo "HARD-FAIL (R-4m): Mutation trotz Guard-FAIL ausgeführt (Sentinel vorhanden)" >&2; exit 1; } +echo " BEFUND: M => Run-FAIL real gefeuert (exit 1 VOR jeder Mutation; Sentinel abseits, §5.15 Pkt. 4)" +# --- Fall D: Löschung => Run-FAIL vor jeder Mutation --- +r4ddir=$(isolate r4d) +cd "$r4ddir" cat > raw/gamma-v1.md <<'EOF' ### S-1 Evidenz Gamma. EOF -git add -A && git commit -qm "R-4 Baseline" -B4=$(git rev-parse HEAD) -# Fall A — M -printf '\n### S-9\nNachlage.\n' >> raw/alpha-v1.md -git add -A && git commit -qm "Modifikation committeter raw/-Datei" -ST_M=$(git diff --name-status "$B4" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') -case "$ST_M" in - *M*) echo " BEFUND: Status M -> Guard aktiviert (Run-FAIL vor Mutation)" ;; - *) echo "HARD-FAIL (R-4): M nicht erkannt" >&2; exit 1;; -esac -# GUARD-ABBRUCH real üben (F8): bei M/D/R bricht der Run mit Exit 1 ab (Run-FAIL -# vor jeder Mutation); die wiki-Mutation wird NICHT effektiv. Als deterministischer -# Nachweis führen wir einen bewusst abreißenden Unterlauf aus, der an der -# Guard-Schranke hart mit Exit 1 enden MUSS — der Exit-Code wird über einen -# `|| true`-Fänger capturiert (der Unterlauf-`set -e`-Kontext nach außen dichtet -# den nicht-0-Exit von der Haupt-Subshell ab). -printf '\nNeues Concept aus raw/alpha-v1.md (Mutation).\n' >> wiki/alpha.md -G_ABBRUCH=0 -( - ST_M2=$(git diff --name-status "$B4" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') - case "$ST_M2" in - *M*) echo " GUARD-FAIL (Modellierung): Zuwachs-Befund M -> Run-FAIL vor Mutation (exit 1)" >&2; exit 1;; - *) echo "HARD-FAIL (R-4): Guard-Abbruch nicht ausgelöst (M nicht mehr erkannt)"; exit 2;; - esac -) || G_ABBRUCH=$? -if [ "$G_ABBRUCH" -ne 1 ]; then - echo "HARD-FAIL (R-4): Guard-Abbruch endete mit Exit $G_ABBRUCH (1 erwartet — Run-FAIL)" >&2; exit 1 -fi -# Nachweis: keine neue wiki-Datei wurde effektiv generiert (die hinzugefügte Zeile -# wäre nur ein Konzept-Body-Zusatz, kein CREATE); die Guard-Semantik bleibt. -if [ -f wiki/alpha-v1-neu.md ]; then - echo "HARD-FAIL (R-4): wiki/CREATE trotz Guard-FAIL angelegt" >&2; exit 1 -fi -echo " BEFUND: Guard-Abbruch real geübt — bei M endet der Run mit Exit 1 (Run-FAIL vor Mutation), keine neue wiki-Datei verblieben" -# Fall B — D -cd "$r4dir" +git add -A && git commit -qm "R-4d Baseline" git rm -q raw/gamma-v1.md && git commit -qm "Loeschung committeter raw/-Datei" -ST_D=$(git diff --name-status "$B4" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') -case "$ST_D" in - *D*) echo " BEFUND: Status D -> Guard aktiviert (Fall D)" ;; - *) echo "HARD-FAIL (R-4): D nicht erkannt" >&2; exit 1;; -esac -# GUARD-ABBRUCH real üben (F8, Fall D): D im Zuwachs-Befund => Run-FAIL vor Mutation. -G_ABBRUCH_D=0 +G_D=0 ( - ST_D2=$(git diff --name-status "$B4" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') - case "$ST_D2" in - *D*) echo " GUARD-FAIL (Modellierung): Zuwachs-Befund D -> Run-FAIL vor Mutation (exit 1)" >&2; exit 1;; - *) echo "HARD-FAIL (R-4): Guard-Abbruch (D) nicht ausgelöst"; exit 2;; + set -e + ST_D=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') + case "$ST_D" in + *D*) echo " GUARD-FAIL (R-4d): Zuwachs-Befund D -> Run-FAIL vor jeder Mutation (exit 1)" >&2; exit 1;; + *) echo "HARD-FAIL (R-4d): D nicht erkannt (Status $ST_D)" >&2; exit 2;; esac -) || G_ABBRUCH_D=$? -if [ "$G_ABBRUCH_D" -ne 1 ]; then - echo "HARD-FAIL (R-4): Guard-Abbruch (D) endete mit Exit $G_ABBRUCH_D (1 erwartet)" >&2; exit 1 + # Unerreicht bei Guard-FAIL: Mutation würde hier ausgeführt (Sentinel). + touch "$r4ddir/.guard-mutation-sentinel" +) || G_D=$? +if [ "$G_D" -ne 1 ]; then + echo "HARD-FAIL (R-4d): Guard-Sub-Run (D) endete mit Exit $G_D (1 erwartet — Run-FAIL)" >&2; exit 1 fi -echo " BEFUND: Guard-Abbruch real geübt — bei D endet der Run mit Exit 1 (Run-FAIL vor Mutation)" -# Fall C — nur A -cd "$r4dir" +[ -e "$r4ddir/.guard-mutation-sentinel" ] && { echo "HARD-FAIL (R-4d): Mutation trotz Guard-FAIL ausgeführt (Sentinel vorhanden)" >&2; exit 1; } +echo " BEFUND: D => Run-FAIL real gefeuert (exit 1 VOR jeder Mutation; Sentinel abseits, §5.15 Pkt. 4)" +# --- Fall A: nur A => Guard passiert, Run ok --- +r4adir=$(isolate r4a) +cd "$r4adir" +cat > raw/alpha-v1.md <<'EOF' +### S-1 +Evidenz Alpha. +EOF +git add -A && git commit -qm "R-4a Baseline" cat > raw/fresh-v1.md <<'EOF' ### S-1 Evidenz: frische neue Source (Status A). EOF git add -A && git commit -qm "Zuwachs nur A" -ST_A=$(git diff --name-status "$B4" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') -# nach M/D/C/A sind mehrere Status im Zuwachs-Befund — wir prüfen, dass frisch neu (A) nicht blockiert -case "$ST_A" in - *A*) echo " BEFUND: frische neue Source (A) im Zuwachs — separat versioniert, Guard passiert (Run ok)" ;; - *) echo "HARD-FAIL (R-4): neu-addierte Source nicht als A erkannt" >&2; exit 1;; -esac -echo "RESULT: PASS — R-4: RAW_GUARD — M/D -> Run-FAIL vor Mutation; frische A akzeptiert (AC-4, AD-3; R/C in R-1b geschlossen)" +G_A=0 +( + set -e + ST_A=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//') + case "$ST_A" in + *M*|*D*|*R*) echo "HARD-FAIL (R-4a): unerwarteter M/D/R im A-Sub-Run (Soll rein A)" >&2; exit 2;; + *A*) echo " BEFUND: frische neue Source (A) im Zuwachs — separat versioniert, Guard passiert (exit 0)";; + *) echo "HARD-FAIL (R-4a): neu-addierte Source nicht als A erkannt (Status $ST_A)" >&2; exit 2;; + esac + # Guard-Pass => Mutation darf ausgeführt werden (Sentinel). + touch "$r4adir/.guard-mutation-sentinel" +) || G_A=$? +if [ "$G_A" -ne 0 ]; then + echo "HARD-FAIL (R-4a): A-Sub-Run endete mit Exit $G_A (0 erwartet — akzeptiert)" >&2; exit 1 +fi +[ -e "$r4adir/.guard-mutation-sentinel" ] || { echo "HARD-FAIL (R-4a): Mutation trotz Guard-PASS nicht ausgeführt (Sentinel fehlt)" >&2; exit 1; } +echo "RESULT: PASS — R-4: RAW_GUARD — M/D => Run-FAIL vor jeder Mutation (reale Sub-Runs, Sentinel-Nachweis); frische A akzeptiert (AC-4, AD-3; R/C in R-1b geschlossen)" # ============================================================================= # R-5 NEUE_EINHEIT_KEIN_NOOP (Loop-2 / BS-L2-1) @@ -539,70 +619,115 @@ git add -A && git commit -qm "Zuwachs: zeta-a + zeta-b" R5_ZUWACHS=$(git diff --name-only HEAD~1 -- raw/ | LC_ALL=C sort) R5_COUNT=$(printf '%s\n' "$R5_ZUWACHS" | wc -l | tr -d ' ') [ "$R5_COUNT" = "2" ] || { echo "HARD-FAIL (R-5): Zuwachs $R5_COUNT != 2 ($R5_ZUWACHS)" >&2; exit 1; } +# Term-Ableitung aus dem Zuwachs mit D-8-Mehrfach-Term-Vereinigung (gleiche +# Regeln wie R-1: primärer Term + >=2-Zeichen-Segmente; Datums-Suffix gestrippt). R5_TERMS="" for f in $R5_ZUWACHS; do + case "$f" in *.source.md) continue;; esac stem=$(basename "$f" .md | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//') t=$(norm "$stem") - R5_TERMS="$R5_TERMS $t" + [ -n "$R5_TERMS" ] && R5_TERMS="$R5_TERMS $t" || R5_TERMS="$t" + for seg in $(printf '%s' "$stem" | tr '-' '\n'); do + case "$seg" in ''|'-'|'?'*) continue;; *) if [ "${#seg}" -ge 2 ] && [ "$seg" != "$t" ]; then R5_TERMS="$R5_TERMS $(norm "$seg")"; fi;; esac + done done -R5_TERMS=$(printf '%s\n' $R5_TERMS | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//') +R5_TERMS=$(printf '%s\n' $R5_TERMS | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//') echo " R-5 Zuwachs: $R5_ZUWACHS" -echo " R-5 Terme (aus Zuwachs abgeleitet): $R5_TERMS" -# Einzelzeichen-Segmente -a/-b sind kein eigener Term (Länge >= 2 Filter). In den -# Zuwachs-Termen: zeta-a, zeta-b. Die Vereinigung je Datei wäre zeta + a/b(<-1,exkludiert). +echo " R-5 Terme (D-8-Vereinigung, aus Zuwachs abgeleitet): $R5_TERMS" +# Zeta-a/Zeta-b enthalten das >=2-Zeichen-Segment 'zeta' -> die Vereinigung MUSS +# 'zeta' tragen (D-8-Prüfung, kein nur-primärer-Scan). +case " $R5_TERMS " in *" zeta "*) ;; *) echo "HARD-FAIL (R-5): D-8-Segment 'zeta' fehlt in der Vereinigung ($R5_TERMS)" >&2; exit 1;; esac +# Echter Stufe-a-Scan über den VOLLEN wiki/*.md-Scan (kein fester 2-Datei-Scan). R5_HITS="" for t in $R5_TERMS; do h=$(match_stufe_a "$t" wiki/*.md) [ -n "$h" ] && R5_HITS="$R5_HITS $t($h)" done -R5_HITS=$(printf '%s\n' $R5_HITS | sed 's/^ *//') +R5_HITS="${R5_HITS# }" [ -z "$R5_HITS" ] || { echo "HARD-FAIL (R-5): Stufe-a der neuen Einheit trifft unerwartet Concept-Body: '$R5_HITS'" >&2; exit 1; } -echo " BEFUND: Candidate-Liste leer über den VOLLEN wiki/*.md-Scan (kein fester 2-Datei-Scan); Terme aus dem Zuwachs abgeleitet (D-9)" -# LOOP-2-KERN: leere Candidate-Liste bei NEUER Einheit ist NICHT NO_OP. -# Es ist der CREATE/ORPHAN-Bewertungsraum (Zellen 2/3). -# Die Einheit ist eigenständig interpreterbar + Ziel-Pfad ableitbar => CREATE. -ROUTE_ZETA="CREATE" +echo " BEFUND: Candidate-Liste leer über den VOLLEN wiki/*.md-Scan (kein fester 2-Datei-Scan); Terme aus dem Zuwachs abgeleitet (D-9/D-8)" +# ECHTE Routing-Funktion (Loop-3/D-3.9-3): implementiert die §5.15-Pkt.-3- +# Reihenfolge — Kandidaten => UPDATE; sonst CREATE-Prädikat (eigenständig +# interpreterbar + Ziel-Pfad ableitbar) => CREATE; sonst ORPHAN/HOLD. NO_OP ist +# NIE erreichbar aus leerer Candidate-Liste (nur Update-Unter-Entscheidung). +route() { # $1 = Kandidaten-Liste (leer erlaubt); $2 = eigenständig-interpreterbar (ja/nein) + local cands="$1" interp="$2" + if [ -n "$cands" ]; then + printf 'UPDATE' + elif [ "$interp" = "ja" ]; then + printf 'CREATE' + else + printf 'ORPHAN/HOLD' + fi +} +ROUTE_ZETA=$(route "$R5_HITS" "ja") [ "$ROUTE_ZETA" = "CREATE" ] || { echo "HARD-FAIL (R-5): R-5-Routing ist $ROUTE_ZETA (Soll CREATE — neue Einheit kein NO_OP)" >&2; exit 1; } -echo " BEFUND: 2-Datei-Scan (zeta-a, zeta-b) -> Terme zeta-a/zeta-b -> Stufe-a leer -> CREATE-Bewertungsraum (nicht NO_OP; kein Anlage-Zwang für Einzelzeichen-Segmente -a/-b)" -echo "RESULT: PASS — R-5: NEUE_EINHEIT_KEIN_NOOP — leere Candidate-Liste bei neuer Einheit => Zelle 2 CREATE (BS-L2-1, §5.15 Pkt. 3-Absatz 'Leere Candidate-Liste'); keine NO_OP-Fehlklassifikation; Einzelzeichen-Segmente kein eigener Term" +# Negativ-Kontrolle der Routing-Funktion: leere Liste + nicht-interpreterbar +# => ORPHAN/HOLD. Eine NO_OP-Regression (leere Kandidaten direkt auf NO_OP +# gemappt) würde den Soll-Wert verfehlen und MUSS hier HARD-FAIL. +ROUTE_NEG=$(route "" "nein") +[ "$ROUTE_NEG" = "ORPHAN/HOLD" ] || { echo "HARD-FAIL (R-5): Negativ-Route (leer, nicht-interpreterbar) ist $ROUTE_NEG (Soll ORPHAN/HOLD — NO_OP aus leerer Liste ist die BS-L2-1-Regression)" >&2; exit 1; } +echo " BEFUND: route() (echte Mechanik) — leere Candidate-Liste + eigenständig => CREATE; nicht-interpreterbar => ORPHAN/HOLD; NO_OP aus leerer Liste NICHT erreichbar (BS-L2-1)" +echo "RESULT: PASS — R-5: NEUE_EINHEIT_KEIN_NOOP — leere Candidate-Liste bei neuer Einheit => Zelle 2 CREATE via echter route() (BS-L2-1, §5.15 Pkt. 3-Absatz 'Leere Candidate-Liste'); D-8-Vereinigung ausgeübt; NO_OP-Regression negativ geprüft" # ============================================================================= # R-6 RESERVED_ZIEL_INDEX (AC-5) — Slug abgeleitet + ls-tree # ============================================================================= -runlabel "R-6: RESERVED_ZIEL_INDEX — Ziel-Slug 'index' (abgeleitet, nicht hart) + Ist-Dateimenge via git ls-tree-Schnittmenge (AC-5)" +runlabel "R-6: RESERVED_ZIEL_INDEX — Ziel-Slug 'index' (rein aus dem committeten Dateinamen abgeleitet, keine Zusatz-Stripps) + case-insensitive Reserviertheits-Prüfung + Ist-Dateimenge via git ls-tree-Schnittmenge (AC-5)" r6dir=$(isolate r6) cd "$r6dir" -cat > raw/index-doc.md <<'EOF' -### S-1 -Evidenz: Index-Struktur des Bundles selbst. -EOF +# Fixture mit DATUMS-SUFFIX: der Slug entsteht ausschließlich über die +# dokumentierte §5.15-Pkt.-1-Regel (Datums-Suffix strip + Norm-Kollaps) — +# kein nicht-instruktioneller Zusatz-Strip (Loop-3/D-3.9-3). +# Zwei-Commit-Aufbau (Basis → Zuwachs), damit der Zuwachs-Dateiname über +# `git diff HEAD~1 -- raw/` aus dem COMMITTETEN State kommt (analog R-8/R-9). cat > wiki/index.md <<'EOF' # Index EOF cat > wiki/log.md <<'EOF' # Änderungen EOF -git add -A && git commit -qm "R-6 Zuwachs index-doc" -# Ziel-Slug AUS der Zuwachs-Datei ableiten (Dateiname->Term, §5.15 Pkt. 1). -STEM=$(printf 'index-doc' | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//' | sed 's/-doc$//') +git add -A && git commit -qm "R-6 Basis (Index/Log-Struktur)" +cat > raw/index-2026-08-20.md <<'EOF' +### S-1 +Evidenz: Index-Struktur des Bundles selbst. +EOF +git add -A && git commit -qm "R-6 Zuwachs index-2026-08-20" +# Ziel-Slug AUS dem committeten Dateinamen ableiten (Dateiname->Term, §5.15 Pkt. 1): +# basename ohne .md, Datums-Suffix gestrippt, Norm-Kollaps. Keine weitere Regel. +R6_FILE=$(basename "$(git diff --name-only HEAD~1 -- raw/)") +STEM=$(printf '%s' "$(basename "$R6_FILE" .md)" | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//') TARGET_SLUG=$(norm "$STEM") -echo " Abgeleiteter Ziel-Slug: '$TARGET_SLUG'" +echo " Abgeleiteter Ziel-Slug (aus committetem Dateinamen '$R6_FILE', nur dokumentierte Regel): '$TARGET_SLUG'" [ "$TARGET_SLUG" = "index" ] || { echo "HARD-FAIL (R-6): Slug-Ableitung ergab '$TARGET_SLUG' (Soll index)" >&2; exit 1; } -case "$TARGET_SLUG" in - index|log|source|README) echo " BEFUND: Ziel-Slug '$TARGET_SLUG' ist reservierter Bundle-Name — Hold aktiviert (§5.15 Pkt. 5)";; +# Reserviertheits-Prüfung case-INSENSITIV (Loop-3/P-3.9-2): der Slug ist +# lowercased (norm), die Bundle-Reservation umfasst auch das gemischte +# 'README' — beide Seiten lowercased vergleichen, sonst ist 'readme' unerreichbar. +case "$(tolower <<< "$TARGET_SLUG")" in + index|log|source|readme) echo " BEFUND: Ziel-Slug '$TARGET_SLUG' ist reservierter Bundle-Name (case-insensitiv geprüft) — Hold aktiviert (§5.15 Pkt. 5)";; *) echo "HARD-FAIL (R-6): Slug '$TARGET_SLUG' nicht in Reserviert-Menge" >&2; exit 1;; esac +# Negativ-Kontrolle der case-insensitiven Prüfung: ein gemischt-geschriebenes +# reserviertes Wort MUSS ebenso erkannt werden (sonst wäre die Prüfung +# nur für kleine Buchstaben scharf). +case "$(tolower <<< 'README')" in + readme) echo " BEFUND: Negativ-Kontrolle — gemischt-geschriebenes 'README' wird über tolower in die Reserviert-Menge eingeordnet";; + *) echo "HARD-FAIL (R-6): tolower-Kontrolle defekt" >&2; exit 1;; +esac # Tote CAND: der abgeleitete Candidate wird NICHT als Datei geschrieben. -[ -e "wiki/index-doc.md" ] && { echo "HARD-FAIL (R-6): Ziel als eigenständige Datei geschrieben — Hold verletzt (AC-5)" >&2; exit 1; } +# (wiki/index.md existiert bereits als Fixture — die echte Invariante ist der +# saubere wiki/-Baum: keine neue/veränderte Datei, via git-status-Assert.) ST_R6=$(git status --porcelain -- wiki/ | awk '{print $2}' | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//') [ -z "$ST_R6" ] || { echo "HARD-FAIL (R-6): Hold mutierte den wiki/-Baum — [$ST_R6]" >&2; exit 1; } # Ist-Dateimenge deterministisch via git ls-tree-Schnittmenge (§5.15 Pkt. 5). +# Basenamen behalten ihre Schreibweise (z. B. 'README') — beide Seiten +# lowercased für die Reserviert-Schnittmenge (Konsistenz mit der Slug-Prüfung). LS_BASES=$(git ls-tree -r --name-only HEAD -- wiki/ | sed 's#wiki/##' | sed 's#\.md$##' | LC_ALL=C sort | tr '\n' ' ') echo " git ls-tree-Dateibasen unter wiki/: $LS_BASES" -# Schnittmenge (b): committete Datei-Basen (hier index, log) ∩ erschöpfende Liste. +# Schnittmenge: committete Datei-Basen (hier index, log) ∩ Reserviert-Menge. HIT_INTERSECTION="" for b in $LS_BASES; do - case "$b" in index|log|source|README) HIT_INTERSECTION="$HIT_INTERSECTION $b";; esac + case "$(tolower <<< "$b")" in index|log|source|readme) HIT_INTERSECTION="$HIT_INTERSECTION $b";; esac done HIT_INTERSECTION=$(printf '%s\n' $HIT_INTERSECTION | tr '\n' ' ' | sed 's/ $//') echo " Schnittmenge Ist-Dateimenge: ${HIT_INTERSECTION:-leer}" @@ -615,28 +740,36 @@ echo "RESULT: PASS — R-6: RESERVED_ZIEL_INDEX — abgeleiteter Slug 'index' => # ============================================================================= # R-7 RESERVED_ZIEL_LOG (AC-5) # ============================================================================= -runlabel "R-7: RESERVED_ZIEL_LOG — Slug 'log' (abgeleitet); Hold => keine Anlage, wiki/-unverändert (AC-5)" +runlabel "R-7: RESERVED_ZIEL_LOG — Slug 'log' (rein aus dem committeten Dateinamen abgeleitet, keine Zusatz-Stripps); Hold => keine Anlage, wiki/-unverändert (AC-5)" r7dir=$(isolate r7) cd "$r7dir" -cat > raw/log-doc.md <<'EOF' -### S-1 -Evidenz: Log-Thema. -EOF +# Fixture mit DATUMS-SUFFIX (analog R-6): Slug ausschließlich über die +# dokumentierte §5.15-Pkt.-1-Regel (Datums-Suffix strip + Norm-Kollaps). +# Zwei-Commit-Aufbau (Basis → Zuwachs), damit der Zuwachs-Dateiname über +# `git diff HEAD~1 -- raw/` aus dem COMMITTETEN State kommt (analog R-8/R-9). cat > wiki/index.md <<'EOF' # Index EOF cat > wiki/log.md <<'EOF' # Änderungen EOF -git add -A && git commit -qm "R-7 Zuwachs log-doc" -STEM7=$(printf 'log-doc' | sed 's/-doc$//') +git add -A && git commit -qm "R-7 Basis (Index/Log-Struktur)" +cat > raw/log-2026-08-20.md <<'EOF' +### S-1 +Evidenz: Log-Thema. +EOF +git add -A && git commit -qm "R-7 Zuwachs log-2026-08-20" +R7_FILE=$(basename "$(git diff --name-only HEAD~1 -- raw/)") +STEM7=$(printf '%s' "$(basename "$R7_FILE" .md)" | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//') TARGET_SLUG7=$(norm "$STEM7") +echo " Abgeleiteter Ziel-Slug (aus committetem Dateinamen '$R7_FILE', nur dokumentierte Regel): '$TARGET_SLUG7'" [ "$TARGET_SLUG7" = "log" ] || { echo "HARD-FAIL (R-7): Slug-Ableitung '$TARGET_SLUG7'" >&2; exit 1; } -case "$TARGET_SLUG7" in - index|log|source|README) echo " BEFUND: Ziel-Slug '$TARGET_SLUG7' reserviert — Hold aktiviert (§5.15 Pkt. 5)";; +# Reserviertheits-Prüfung case-insensitiv (Konsistenz mit R-6, P-3.9-2). +case "$(tolower <<< "$TARGET_SLUG7")" in + index|log|source|readme) echo " BEFUND: Ziel-Slug '$TARGET_SLUG7' reserviert (case-insensitiv) — Hold aktiviert (§5.15 Pkt. 5)";; *) echo "HARD-FAIL (R-7): Slug nicht in Reserviert-Menge" >&2; exit 1;; esac -[ -e "wiki/log-doc.md" ] && { echo "HARD-FAIL (R-7): Ziel als Datei geschrieben — Hold verletzt (AC-5)" >&2; exit 1; } +# Tote CAND: Invariante = saubere wiki/-Baum (log.md ist Fixture, bleibt unverändert). ST_R7=$(git status --porcelain -- wiki/ | awk '{print $2}' | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//') [ -z "$ST_R7" ] || { echo "HARD-FAIL (R-7): Hold mutierte den wiki/-Baum — [$ST_R7]" >&2; exit 1; } echo "RESULT: PASS — R-7: RESERVED_ZIEL_LOG — abgeleiteter Slug 'log' => Hold, keine Anlage, wiki/-unverändert (AC-5, §5.15 Pkt. 5)" @@ -644,38 +777,53 @@ echo "RESULT: PASS — R-7: RESERVED_ZIEL_LOG — abgeleiteter Slug 'log' => Hol # ============================================================================= # R-8 HOLD_ZWEI_WORKTREE (BS-L2-4) # ============================================================================= -runlabel "R-8: HOLD_ZWEI_WORKTREE — Hold-Befund über zwei getrennte Worktrees desselben committeten Baums (kein Doppel-Read derselben Datei); tote CAND einzeln hart assertiert, kein || true (BS-L2-4)" +runlabel "R-8: HOLD_ZWEI_WORKTREE — Hold-Befund über zwei getrennte Worktrees desselben committeten Baums (kein Doppel-Read derselben Datei); tote CAND einzeln hart assertiert, kein || true (BS-L2-4); Re-Run-idempotent (Loop-3/P-3.9-14); rc-Lebendig (Loop-3/P-3.9-15); Hold-Assert auf BEIDE Worktrees (Loop-3/P-3.9-8)" r8dir=$(isolate r8) cd "$r8dir" -cat > raw/log-doc.md <<'EOF' -### S-1 -Evidenz: ein Thema mit Log-Bezug (Kandidat: log-thema). -EOF +# Fixture mit DATUMS-SUFFIX (kein -doc-Zusatz-Strip, analog R-6/R-7). +# Zwei-Commit-Aufbau (Basis → Zuwachs): der Zuwachs-Dateiname kommt über +# `git diff HEAD~1 -- raw/` aus dem COMMITTETEN State — in den Worktrees +# (Checkout auf R8BASE = Zuwachs-Tip) ist HEAD~1 = Basis-Commit. cat > wiki/index.md <<'EOF' # Index EOF cat > wiki/log.md <<'EOF' # Änderungen EOF -git add -A && git commit -qm "R-8 Basis (log-doc + Index/Log-Struktur)" +git add -A && git commit -qm "R-8 Basis (Index/Log-Struktur)" +cat > raw/log-2026-08-20.md <<'EOF' +### S-1 +Evidenz: ein Thema mit Log-Bezug. +EOF +git add -A && git commit -qm "R-8 Zuwachs log-2026-08-20" R8BASE=$(git rev-parse HEAD) # Zwei getrennte, saubere Worktrees über demselben committeten Baum. +# Re-Run-Idempotenz (Loop-3/P-3.9-14): veraltete Worktrees aus einem früheren +# Lauf unter demselben $ROOT werden hart entfernt + Admin-Verzeichnisse +# aufgeräumt, bevor neu angelegt wird. git worktree prune -git worktree add -q "$ROOT/wt-hold-a" "$R8BASE" || { echo "HARD-FAIL (R-8): worktree A" >&2; exit 1; } -git worktree add -q "$ROOT/wt-hold-b" "$R8BASE" || { echo "HARD-FAIL (R-8): worktree B" >&2; exit 1; } +rm -rf "$ROOT/r8-wt" +mkdir -p "$ROOT/r8-wt" +git worktree add -q "$ROOT/r8-wt/hold-a" "$R8BASE" || { echo "HARD-FAIL (R-8): worktree A" >&2; exit 1; } +git worktree add -q "$ROOT/r8-wt/hold-b" "$R8BASE" || { echo "HARD-FAIL (R-8): worktree B" >&2; exit 1; } hold_run() { # $1 = worktree-Pfad ; $2 = Run-Name local wt="$1" name="$2" + local rc=0 ( set -e cd "$wt" git config user.email "sandbox@test" && git config user.name "Sandbox" - # Der abgeleitete Ziel-Slug (Dateiname->Term, §5.15 Pkt. 1): log-doc -> log. - local slug - slug=$(norm "$(printf 'log-doc' | sed 's/-doc$//')") - local befund="" - case "$slug" in - index|log|source|README) + # ECHTE Mechanik (Loop-3/D-3.9-3): Slug und CAND werden aus dem + # COMMITTETEN Zuwachs-Dateinamen abgeleitet (Dateiname->Term, §5.15 Pkt. 1: + # Datums-Suffix strip + Norm-Kollaps) — kein hart gesetztes Literal. + local inc_file slug cand befund + inc_file=$(git diff --name-only HEAD~1 HEAD -- raw/) + [ -n "$inc_file" ] || { echo "HARD-FAIL (R-8): Worktree $name ohne committeten Zuwachs" >&2; exit 1; } + slug=$(norm "$(basename "$inc_file" .md | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')") + cand="$(basename "$inc_file" .md)" + case "$(tolower <<< "$slug")" in + index|log|source|readme) # Hold: keine Datei schreiben, textuell benannt (NFR-4). befund="RESERVED_ZIEL_$slug" ;; @@ -686,20 +834,28 @@ hold_run() { # $1 = worktree-Pfad ; $2 = Run-Name echo "run: $name" echo "slug: $slug" echo "befund: $befund" - # tote CAND: Candidate wird bestimmt, aber nicht geschrieben. - echo "cand: log-thema" + # tote CAND: Candidate wird bestimmt, aber NICHT geschrieben. + echo "cand: $cand" echo "geschrieben: nein" } > "$ROOT/receipts/$name.receipt" - ) - local rc=$? - [ $rc -eq 0 ] || { echo "HARD-FAIL (R-8): Hold-Lauf $name (rc=$rc)" >&2; exit 1; } + ) || rc=$? + [ $rc -eq 0 ] || { echo "HARD-FAIL (R-8): Hold-Lauf $name (rc=$rc — lebendige rc-Diagnostik, Loop-3/P-3.9-15)" >&2; exit 1; } } # Zwei getrennte Ausführungskontexte: A und B über demselben Baum. -hold_run "$ROOT/wt-hold-a" hold-a -hold_run "$ROOT/wt-hold-b" hold-b +hold_run "$ROOT/r8-wt/hold-a" hold-a +hold_run "$ROOT/r8-wt/hold-b" hold-b +# Hold-Dichtheit: kein Worktree hat den wiki/-Baum berührt (keine Anlage). +for wtp in "$ROOT/r8-wt/hold-a" "$ROOT/r8-wt/hold-b"; do + ST_WT=$(git -C "$wtp" status --porcelain -- wiki/ | awk '{print $2}' | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//') + [ -z "$ST_WT" ] || { echo "HARD-FAIL (R-8): Hold-Lauf mutierte den wiki/-Baum in $wtp — [$ST_WT]" >&2; exit 1; } +done HA=$(grep -E '^(slug|befund|cand|geschrieben):' "$ROOT/receipts/hold-a.receipt") HB=$(grep -E '^(slug|befund|cand|geschrieben):' "$ROOT/receipts/hold-b.receipt") [ -n "$HA" ] && [ -n "$HB" ] || { echo "HARD-FAIL (R-8): Hold-Receipts leer" >&2; exit 1; } +# ECHTE Werte: Slug ist 'log' (abgeleitet), CAND ist der abgeleitete Dateiname. +# (Loop-3/D-3.9-3: keine handgeschriebenen Receipt-Literale mehr.) +echo "$HA" | grep -q '^slug: log$' || { echo "HARD-FAIL (R-8): abgeleiteter Slug nicht 'log' (hold-a)" >&2; exit 1; } +echo "$HA" | grep -q '^cand: log-2026-08-20$' || { echo "HARD-FAIL (R-8): abgeleiteter CAND nicht 'log-2026-08-20' (hold-a)" >&2; exit 1; } if [ "$HA" = "$HB" ]; then echo " BEFUND: Hold-Befund über zwei getrennte Worktrees identisch (Zwei-Run-Hold-Identität, NICHT-VAKUUM):" echo "$HA" | sed 's/^/ /' @@ -708,16 +864,20 @@ else diff -u <(printf '%s\n' "$HA") <(printf '%s\n' "$HB") >&2 || true exit 1 fi -# tote CAND: kein wiki/$CAND.md unter dem Worktree (einzelnes hartes Assert, kein || true). -[ -e "$ROOT/wt-hold-a/wiki/log-thema.md" ] && { echo "HARD-FAIL (R-8): tote CAND doch geschrieben (wt-a)" >&2; exit 1; } -[ -e "$ROOT/wt-hold-b/wiki/log-thema.md" ] && { echo "HARD-FAIL (R-8): tote CAND doch geschrieben (wt-b)" >&2; exit 1; } +# tote CAND: kein wiki/.md unter den Worktrees (CAND = abgeleiteter +# Dateiname; ein hartes Assert je Worktree, kein || true). +[ -e "$ROOT/r8-wt/hold-a/wiki/log-2026-08-20.md" ] && { echo "HARD-FAIL (R-8): tote CAND doch geschrieben (hold-a)" >&2; exit 1; } +[ -e "$ROOT/r8-wt/hold-b/wiki/log-2026-08-20.md" ] && { echo "HARD-FAIL (R-8): tote CAND doch geschrieben (hold-b)" >&2; exit 1; } # log.md trägt den Hold-Befund (textuell benannt, NFR-4): wir schreiben den Befund -# deterministisch in beide log.md (analog §5.15-Pkt.-5-Hold-Bullet) und verifizieren. -for wtp in "$ROOT/wt-hold-a" "$ROOT/wt-hold-b"; do - printf '%s\n' "- Reconcile-Hold (Story 3.9, §5.15 Pkt. 5): Ziel 'log' (reserviert) — keine Datei geschrieben; deterministischer Hold (CAND=log-thema; Run teilweise erfolgreich; tote CAND nicht angelegt)" >> "$wtp/wiki/log.md" +# deterministisch in beide log.md (analog §5.15-Pkt.-5-Hold-Bullet) und +# verifizieren auf BEIDEN Worktrees (Loop-3/P-3.9-8). +for wtp in "$ROOT/r8-wt/hold-a" "$ROOT/r8-wt/hold-b"; do + printf '%s\n' "- Reconcile-Hold (Story 3.9, §5.15 Pkt. 5): Ziel 'log' (reserviert) — keine Datei geschrieben; deterministischer Hold (CAND=log-2026-08-20; Run teilweise erfolgreich; tote CAND nicht angelegt)" >> "$wtp/wiki/log.md" +done +for wtp in "$ROOT/r8-wt/hold-a" "$ROOT/r8-wt/hold-b"; do + grep -qF "Reconcile-Hold" "$wtp/wiki/log.md" || { echo "HARD-FAIL (R-8): log.md-Hold-Befund fehlt ($wtp, NFR-4)" >&2; exit 1; } + grep -qF "teilweise erfolgreich" "$wtp/wiki/log.md" || { echo "HARD-FAIL (R-8): 'teilweise erfolgreich' fehlt ($wtp)" >&2; exit 1; } done -grep -qF "Reconcile-Hold" "$ROOT/wt-hold-a/wiki/log.md" || { echo "HARD-FAIL (R-8): log.md-Hold-Befund fehlt (wt-a, NFR-4)" >&2; exit 1; } -grep -qF "teilweise erfolgreich" "$ROOT/wt-hold-a/wiki/log.md" || { echo "HARD-FAIL (R-8): 'teilweise erfolgreich' fehlt (wt-a)" >&2; exit 1; } echo "RESULT: PASS — R-8: HOLD_ZWEI_WORKTREE — Hold-Befund deterministisch und identisch über zwei getrennte Worktrees; tote CAND einzeln hart; log.md trägt NFR-4-Benennung; Zwei-Run-Hold-Identität NICHT-VAKUUM (AC-5, §5.15 Pkt. 5/6)" # ============================================================================= @@ -759,17 +919,22 @@ sources: terms: - kopplung EOF +# Re-Run-Idempotenz (Loop-3/P-3.9-14): veraltete Worktrees aus einem früheren +# Lauf unter demselben $ROOT werden hart entfernt + Admin-Verzeichnisse +# aufgeräumt, bevor neu angelegt wird. git worktree prune -git worktree add -q "$ROOT/wt-a" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-a" >&2; exit 1; } -git worktree add -q "$ROOT/wt-b" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-b" >&2; exit 1; } -git worktree add -q "$ROOT/wt-n" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-n" >&2; exit 1; } -git worktree add -q "$ROOT/wt-at" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-at" >&2; exit 1; } +rm -rf "$ROOT/r9-wt" +mkdir -p "$ROOT/r9-wt" +git worktree add -q "$ROOT/r9-wt/a" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-a" >&2; exit 1; } +git worktree add -q "$ROOT/r9-wt/b" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-b" >&2; exit 1; } +git worktree add -q "$ROOT/r9-wt/n" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-n" >&2; exit 1; } +git worktree add -q "$ROOT/r9-wt/at" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-at" >&2; exit 1; } # Ein konstanter at-Wert für die Sandbox (Wanduhr-frei) — die at-Exzeption wird -# bewusst als legitime Abweichung modelliert, nicht gemashat. +# bewusst als legitime Abweichung modelliert, nicht gematcht. SANDBOX_AT="2026-08-20T12:00:00Z" run9_worktree() { # $1=wt $2=name $3=modus(clean|divergent|atgap) - local wt="$1" name="$2" mode="$3" at_val wan + local wt="$1" name="$2" mode="$3" at_val rc=0 ( set -e cd "$wt" || exit 9 @@ -853,7 +1018,13 @@ EOF R9_COMMIT_HASH=$(git rev-parse HEAD) full_list="" masked_list="" - for f in $(git ls-tree -r --name-only HEAD -- wiki/ | grep -vE '/?(index|log)\.md$'); do + # index.md/log.md bleiben bewusst AUS dem Byte-Vergleich (generierte + # Struktur-/Logdateien, kein deterministischer Concept-Inhalt) — die + # Ausnahme ist jetzt ANCHORED (nur exakte Basenamen index.md/log.md, nicht + # jede Datei, die DARAUF ENDET, z. B. myindex.md) und benannt. + # (Loop-3/P-3.9-7: unanchoredes '/?(index|log)\.md$' schnitt fälschlich + # Enden-Präfixe aus; 'myindex.md' würde still vom Vergleich verschwinden.) + for f in $(git ls-tree -r --name-only HEAD -- wiki/ | grep -vE '(^|/)(index|log)\.md$'); do full=$(sha256sum "$f" | awk '{print $1}') masked=$(sed 's|^ at:.*| at: |' "$f" | sha256sum | awk '{print $1}') full_list="$full_list $f=$full" @@ -863,14 +1034,13 @@ EOF printf 'run: %s\ncommit: %s\nfull: %s\nmasked: %s\n' \ "$name" "$R9_COMMIT_HASH" "$full_list" "$masked_list" >> "$ROOT/receipts/$name.bundle" echo " [bundle] $name => commit $R9_COMMIT_HASH full=$full_list" - ) - local rc=$? - [ $rc -eq 0 ] || { echo "HARD-FAIL (R-9): Lauf $name (rc=$rc)" >&2; exit 1; } + ) || rc=$? + [ $rc -eq 0 ] || { echo "HARD-FAIL (R-9): Lauf $name (rc=$rc — lebendige rc-Diagnostik, Loop-3/P-3.9-15)" >&2; exit 1; } } echo "--- Lauf A (clean) ---" -run9_worktree "$ROOT/wt-a" run9a "clean" +run9_worktree "$ROOT/r9-wt/a" run9a "clean" echo "--- Lauf B (clean) ---" -run9_worktree "$ROOT/wt-b" run9b "clean" +run9_worktree "$ROOT/r9-wt/b" run9b "clean" R9A=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9a.receipt" | grep -v '^generated.at') R9B=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9b.receipt" | grep -v '^generated.at') [ -n "$R9A" ] && [ -n "$R9B" ] || { echo "HARD-FAIL (R-9): Receipts leer (Vakuum)" >&2; exit 1; } @@ -885,7 +1055,7 @@ echo "$R9A" | sed 's/^/ /' case "$R9A" in *alpha*) ;; *) echo "HARD-FAIL (R-9): nicht-vakuum Assertion — alpha fehlt" >&2; exit 1;; esac # NEGATIVE Kontrolle: konstruierte Abweichung hart FAIL. echo "--- NEGATIVE Kontrolle (divergent) ---" -run9_worktree "$ROOT/wt-n" run9n "divergent" +run9_worktree "$ROOT/r9-wt/n" run9n "divergent" R9N=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9n.receipt" | grep -v '^generated.at') [ -n "$R9N" ] || { echo "HARD-FAIL (R-9): Negativ-Receipt leer (Vakuum)" >&2; exit 1; } if [ "$R9N" = "$R9A" ]; then @@ -896,7 +1066,7 @@ fi # at-Exzeption (P-L2-6): eine legitime at-Abweichung wird NICHT als AD-16 gemeldet; # die übrigen deterministischen Felder sind byte-identisch. echo "--- at-Exzeption (atgap) ---" -run9_worktree "$ROOT/wt-at" run9at "atgap" +run9_worktree "$ROOT/r9-wt/at" run9at "atgap" R9AT_REST=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9at.receipt" | grep -v '^generated.at') R9AT_AT=$(grep '^generated.at:' "$ROOT/receipts/run9at.receipt") if [ "$R9AT_REST" != "$R9A" ]; then @@ -940,7 +1110,7 @@ if [ "$B9NM" = "$B9AM" ]; then echo "HARD-FAIL (R-9-bundle): divergenter Run-Bundle ununterscheidbar vom clean-Run (Negativ-Kontrolle muss FAIL)" >&2; exit 1 fi echo " BEFUND: divergenter Run-Bundle an der mutierten Datei nachweisbar ungleich (hart FAIL, kein Vakuum)" -echo "RESULT: PASS — R-9: ZWEI_RUN_IDENTISCH — positive Identität NICHT-VAKUUM (2 getrennte Worktrees, gegeneinander verglichen, kein hart codierter Erwartungswert) + NEGATIVE Kontrolle hart FAIL + at-Exzeption praktisch ausgeübt (nur-at-differiert, nicht-mashat); Manifest datei-pfadbasiert (AC-6, AD-17h/A0-19, §5.15 Pkt. 6)" +echo "RESULT: PASS — R-9: ZWEI_RUN_IDENTISCH — positive Identität NICHT-VAKUUM (2 getrennte Worktrees, gegeneinander verglichen, kein hart codierter Erwartungswert) + NEGATIVE Kontrolle hart FAIL + at-Exzeption praktisch ausgeübt (nur-at-differiert, nicht gematcht); Manifest datei-pfadbasiert (AC-6, AD-17h/A0-19, §5.15 Pkt. 6)" # ============================================================================= # Abschluss — Gesamt-Exit 0 (nur wenn alle R-Szenarien harte PASS) diff --git a/_bmad-output/implementation-artifacts/spec-3-9-deterministische-relevanz-und-reconcile-routing-schliessen.md b/_bmad-output/implementation-artifacts/spec-3-9-deterministische-relevanz-und-reconcile-routing-schliessen.md index 89e4adc..c4db8f8 100644 --- a/_bmad-output/implementation-artifacts/spec-3-9-deterministische-relevanz-und-reconcile-routing-schliessen.md +++ b/_bmad-output/implementation-artifacts/spec-3-9-deterministische-relevanz-und-reconcile-routing-schliessen.md @@ -4,7 +4,7 @@ type: 'feature' created: '2026-08-20' status: 'done' baseline_commit: '2f079ee3c87311cda9c9f07614c5e20c869b4500' -review_loop_iteration: 2 +review_loop_iteration: 3 context: - '_bmad-output/implementation-artifacts/epic-3-context.md' --- @@ -45,7 +45,7 @@ context: | ROUTING_CREATE | eigenständige neue Wissenseinheit, kein Match | Zelle CREATE → §5.1/§5.7-Neu-Anlage | Index-Regel, §5.7-Routing | | ROUTING_ORPHAN | neu committete Evidenz ohne Ziel-Pfad-Treffer | Zelle ORPHAN/HOLD → §5.10-Pkt. 8-Reconcile-Orphan-Regel (log.md-verwaist, kein Banner, keine Mutation) | fail-closed, AD-16-Default | | ROUTING_NOOP | Evidenz bereits vollständig repräsentiert | Zelle NO_OP → §5.9-Pkt. 2-No-Op / §3.2-Pkt. 3d, byte-identisches Ziel | keine Mutation, kein `at`-Bump | -| RAW_GUARD_MODIFIZIERT | committete `raw/`-Datei im Zuwachs modifiziert/gelöscht | Run-FAIL **vor jeder Mutation** (AD-3: keine modifizierte/gelöschte committete Source als Input) | textuell benannt (NFR-4) | +| RAW_GUARD_MODIFIZIERT | committete `raw/`-Datei im Zuwachs modifiziert/gelöscht/umbenannt (M/D/**R**, §5.15 Pkt. 4) | Run-FAIL **vor jeder Mutation** (AD-3: keine modifizierte/gelöschte/umbenannte committete Source als Input) | textuell benannt (NFR-4) | | RESERVED_ZIEL_INDEX_LOG | neues Concept-Ziel mit Slug `index`/`log`/reserviertem Bundle-Name | keine Datei geschrieben; deterministischer Hold verlangt disambiguierte Identität | textuell benannt, Run „teilweise erfolgreich" | | ZWEI_RUN_IDENTISCH | gleicher Git-State + gleiches Eingabemanifest, zwei Läufe | identische Candidate-Liste, Reihenfolge und Routing-Entscheidung; Assertion nicht-vakuum | PASS; Abweichung = AD-16-Klassifikationsdefekt (§5.14 Pkt. 4) | @@ -53,7 +53,7 @@ context: - `schema/compiler.md` — **primär mutiert** (D-3): neue Sektion **§5.15 „Deterministische Relevanz- & Reconcile-Routing (Story 3.9)"** (nach §5.14 Z. 377, vor §6 Z. 379; Pkt. 1–6); §7-Relevanzbestimmung-Bullet Z. 434 („Story-3.9-ACs noch nicht verankert"/P-8-Scope-Präzisierung → auf §5.15-Verankerung nachgeführt); §8-Revisionslog **Revision 3.4** nach Z. 477 (Abschlussklausel AD-3/D-3/keine neue §7-Klasse); optional §3.2-Verweis-Anker (Pkt. 1 Term-Ziehverfahren Z. 49–54, Pkt. 3d NO_MATCH Z. 63 → §5.15-Pkt.-1/-3-Verweis). Bestehende §3/§5.9/§5.10/§5.14-Mechaniken bleiben **textuell unverändert** (Referenz statt Re-Negotiation). - `_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh` — **neu** (re-executierbar, Muster `sandbox-3-8/run-sandbox.sh`, Exit 0): Szenarien R-1..R-9 (Matrix-Zeilen als harte Assertionen; Zwei-Run-Identität in getrennten Läufen; negative Kontrolle je Guard/Hold). -- `_bmad-output/implementation-artifacts/sprint-status.yaml` — **mutiert**: Key `3-9-deterministische-relevanz-und-reconcile-routing-schliessen` `backlog` → `in-progress`; `last_updated` (Format `MM-DD-YYYY HH:MM`). +- `_bmad-output/implementation-artifacts/sprint-status.yaml` — **mutiert**: Key `3-9-deterministische-relevanz-und-reconcile-routing-schliessen` `backlog` → `review` (Implementierungs-Commit; finaler `done`-Flip im Step-05-Status-Sync nach konvergiertem Review-Loop); `last_updated` (Format `MM-DD-YYYY HH:MM`). - `_bmad-output/implementation-artifacts/deferred-work.md` — **append**: Defers D-8 (Mehrfach-Term-Vereinigung) und D-9 (Orphan-Term-Ableitung, fester 2-Datei-Scan) → aufgegriffen/geschlossen; Umlaut-Defer bleibt offen (wie notiert, kein Instruktions-Defekt). - `wiki/log.md` — **append** (Vertrag §5, bestehende Bullets unverändert): Story-3.9-Eintrag (Verankerung §5.15, Sandbox-Nachweis R-1..R-9, Status-Flip, Validator-Verdikt). - `_bmad-output/implementation-artifacts/epic-3-context.md` — **mutiert** (gemäß genehmigtem Sprint-Change-Proposal 2026-08-20 „Artifact Impact: epic-3-context.md synchronisiert"; Header „Edit freely"): Technical Decision Z. 39 „Zielzustand … geplant für Story 3.9 … noch nicht verankert; §3.2 ist bis dahin maßgeblich" → Ist/§5.15-Verankerung; Z. 41/43 (Story 3.11/3.12) bleiben Zielzustand. @@ -63,10 +63,10 @@ context: ## Tasks & Acceptance **Execution:** -- [ ] `schema/compiler.md` — §5.15 einfügen (nach §5.14, vor §6): Pkt. 1–6 gemäß Intent; keine neuen Prädikate/§7-Klassen/Keys; §7-Vorbehalt und §8-Revisionslog **Revision 3.4** nachführen -- [ ] `_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh` — R-1..R-9, harte PASS/FAIL, Zwei-Run nicht-vakuum, Exit 0 -- [ ] `_bmad-output/implementation-artifacts/sprint-status.yaml` — Key 3-9 → `in-progress`; `deferred-work.md` — D-8/D-9 aufgegriffen; `wiki/log.md` — Story-3.9-Eintrag (Verankerung, Sandbox, Validator-Verdikt) -- [ ] `_bmad-output/implementation-artifacts/epic-3-context.md` — Z. 39 Zielzustand → §5.15-Ist (Z. 41/43 unverändert) +- [x] `schema/compiler.md` — §5.15 einfügen (nach §5.14, vor §6): Pkt. 1–6 gemäß Intent; keine neuen Prädikate/§7-Klassen/Keys; §7-Vorbehalt und §8-Revisionslog **Revision 3.4** nachführen +- [x] `_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh` — R-1..R-9, harte PASS/FAIL, Zwei-Run nicht-vakuum, Exit 0 +- [x] `_bmad-output/implementation-artifacts/sprint-status.yaml` — Key 3-9 → `in-progress` (→ `review` im Impl-Commit; finaler `done`-Flip Step-05); `deferred-work.md` — D-8/D-9 aufgegriffen; `wiki/log.md` — Story-3.9-Eintrag (Verankerung, Sandbox, Validator-Verdikt) +- [x] `_bmad-output/implementation-artifacts/epic-3-context.md` — Z. 39 Zielzustand → §5.15-Ist (Z. 41/43 unverändert) **Acceptance Criteria:** - Given einen committeten Git-State und ein kanonisches Eingabemanifest, when Candidate-Terme gewonnen werden, then folgt die Gewinnung einem geschlossenen, geordneten Algorithmus oder einem expliziten, persistierten Term-Manifest — keine freie Producer-Auswahl (AC-1). @@ -78,6 +78,8 @@ context: ## Spec Change Log +- **Review-Loop-3 (2026-08-21, bmad-code-review Re-Run — 4 Layer, `full`-Modus):** Blind Hunter (2 Durchläufe) + Edge Case Hunter + Acceptance Auditor + Verification Gap (über Retry; kein ausgefallener Layer) gegen den Story-3.9-Impl-Commit (7 Dateien). **Triage: 3 decision-needed, 15 patch, 2 defer, 8 verworfen.** **Decision-Resolutionen (Nutzer „Ich folge Deinen Empfehlungen"):** **(D-3.9-1 → Option 1)** die retrospektive §8-Revision-3.3-Nachführung (`in-progress` → `done` für Key 3-8) wird **behalten** — faktisch korrekt (Story 3.8 ist done); Dokumentation über diesen append-only Change-Log statt frozen-Code-Map-Edit; kein Rückbau. **(D-3.9-2 → Option 1)** Status-Kontraktion aufgelöst auf die 3.7/3.8-Präzedenz: maßgeblich ist `review` (Impl-Commit-Flip); Frontmatter-`done` wird zurückgesetzt, der finale `done`-Flip erfolgt im Step-05-Status-Sync nach konvergiertem Loop; Revision-3.4-/Code-Map-/SRO-Wortlaut „`in-progress`" auf `review`-Ist korrigiert. **(D-3.9-3 → Option 2 Minimal-Härtung)** die tautologischen/echte-Mechanik-fehlenden Sandbox-Szenarien werden gehärtet (R-1 echtes `manifest_check`-Run-FAIL, R-1b Guard-Sub-Runs ohne kumulative-Baseline-Kontraktion, R-3 Fall-2 echte NO_OP-Containment-Prüfung + Fall-4 echte `index.md`-Link-Resolution, R-5 echte `route()`-Funktion über den CREATE/Bewertungsraum, R-6/R-7 Slug aus echter Ableitung ohne `-doc$`-Strip, R-8 Receipt aus echter Mechanik); die strukturellen Restlimits (volle Stufen-b/c-Traversal-Coverage, Zwei-Run über realen Contents statt Receipt-Literale) bleiben **benannte Defers** (Home Story 3.13, Präzedenz DET-1/2-Defer). **Patch-Liste (angewandt):** §5.15 Pkt. 1 (`/dev/null`-Sort → `LC_ALL=C`-byttreue Stable-Sortierung; Status-Code-Entflechtung Pkt. 1 ↔ Pkt. 4: Befund A/M/D/R/C, Akzeptanz A/C vs. Run-FAIL M/D/R); Pkt. 2 (Actor-Body → **Concept-Body**); Pkt. 5 (hängender „§3.2-Endergebnis"-Verweis → §5.8-Instruktions-Hold als Run-Status-Definition); Revision 3.4 (Status-Wortlaut `review` + Status-Code-Korrektur). Sandbox: Escaper-Fix (sed-Trenner-Kollision `|`, verifiziert: `v1.0` → stilles Nicht-Match) + quoting, `README`-Reserviertheits-Fall (case-insensitiv), R-9-Bundle-Grep-Anchorung (Area-Pfade nicht mehr still ausgeschlossen) + Benennung der index/log-Ausnahme, R-8-Hold-Assert auf beide Worktrees, Worktree-Re-Run-Idempotenz (unter `$ROOT`), `local rc=$?`-Dead-Diagnostics, Term-Quoting + `sort -u`. Peripherie: I/O-Matrix-`RAW_GUARD_MODIFIZIERT`-Zeile um Rename (M/D/**R**); ``-Schließtag nach diesem Change Log ergänzt (Präzedenz Spec-3-7/3-8 — fehlender Tag ließ 7 Sektionen de facto frozen-intern liegen); `sprint-status.yaml` `last_updated`-Format `MM-DD-YYYY HH:MM`; `deferred-work.md` D-9-Aufgegriffen-Text auf den tatsächlich ausgeübten Umfang präzisiert (Stufe-a, Root-Glob); `wiki/log.md` 2026-08-21-Eintrag mit echtem Validator-Verdikt. **Frozen-Änderungen (dieser Eintrag autorisiert, dokumentiert im Frozen-Block):** I/O-Matrix-`RAW_GUARD_MODIFIZIERT`-Zelle (Rename-Nachführung — AC-4/BS-L2-2-Konsequenz, kein AC-Wortlaut-Wechsel) + Schließtag-Nachtrag. **Neu-Verhandlung des Frozen-Intents nötig: Nein** (keine AC-/Intent-Wortlaute geändert; die I/O-Zelle führt den bereits in §5.15 Pkt. 4 verankerten R-Status in die Matrix nach). **Korrektur-Postscript (2026-08-21, Re-Verifikation während der Patch-Anwendung):** der oben genannte „Escaper-Fix (sed-Trenner-Kollision `|`)" ist ein **Fehlalarm (P-3.9-1 verworfen)** und wurde **nicht** angewandt — die „Ausführung"-Evidenz des Verification-Gap-Layers war durch die Bash-Tool-Transport-Schicht korrumpiert (Backslash-Ebene halbiert, getesteter Code ≠ Datei-Code). Definitive Gegenprüfung (od-Beweis: Datei-Z. 74/790 enthalten `\\&`; Negativ-/Positiv-Kontrolle aus Datei-Bytes: `v1.0` matcht `v10`-Body **nicht** und `v1.0`-Body **ja**) belegt: der Escaper ist literal-sicher, `run-sandbox.sh` Z. 74/790 bleiben unverändert. Die anzuwendenden Pflaster reduzieren sich damit auf 14 (von 15); die übrigen Patch-Positionen dieses Eintrags gelten unverändert. + - **Review-Loop-2 bad_spec-Loopback (2026-08-20):** bmad-code-review (Blind Hunter 28 / Edge Case Hunter 10 / Verification Gap 9 verifizierte Findings, D-3-Gewichtung) ergab **2 bad_spec** (innerer Instruktions-Widerspruch), 4 patch, 2 defer, restliche als reject/verworfen; **kein intent_gap** (kein Eingriff in ``). **Kern-Defekt (BS-L2-1):** die §5.15-Pkt.-3-Routing-Tabelle definiert **NO_OP (Zelle 4) ausschließlich als Unter-Entscheidung des Update-Pfads** („bereits vollständig repräsentierte, identische Evidenz"), während der **§3.2-Pkt.-3d-Verweis-Anker (compiler.md Z. 63)** die NO_MATCH-Leer-Candidate-Liste pauschal an **„Zelle 4 (NO_OP)“** koppelt — eine **neue Einheit ohne bestehenden Treffer** gehört aber in den Bewertungsraum **Zelle 2 (CREATE)** / **Zelle 3 (ORPHAN/HOLD)**, nicht NO_OP. Die Sandbox setzt diesen Fehler fort und verletzt damit zusätzlich das KEEP-Design (Design Notes Z. 87: „neue Wissenseinheit → CREATE“): R-5 (`run-sandbox.sh` Z. 462–467) assertiert für neue Terme `zeta-a`/`zeta-b` explizit „NO_OP-Pfad, kein Anlage-Zwang“ — neue Einheiten würden unter dieser Interpretation nie angelegt. **(BS-L2-2):** die Status-Codes sind inkonsistent — §5.15-Pkt.-1-Satz nennt `--diff-filter=ACMR` (impliziert R/C behandelt), der Guard Pkt. 4 definiert nur `A`/`M`/`D`, R/C-Zuordnung bleibt undefiniert (Rename einer committeten `raw/`-Datei = AD-3-Verletzung, Copy = neu). **KEEP (muss in der Loop-2-Re-Ableitung überleben):** §5.15 als eigene Sektion nach §5.14 mit **einer** exklusiven Tabelle (vier Zellen UPDATE/CREATE/ORPHAN-HOLD/NO_OP, NO_OP als Update-Unter-Entscheidung gemäß Design Notes, CREATE-vs-ORPHAN-Prädikat, Stufe-b-Zelle); Referenz-statt-Re-Negotiation (§3/§3.2/§5.9/§5.10/§5.14 textuell unverändert — lediglich der §3.2-Pkt.-3d-Verweis-Anker wird nach Paragraph-Korrektur auf „Zelle 2 (CREATE)?/Zelle 3 (ORPHAN/HOLD)“-Bewertungsraum nachgeführt); Raw-Immutability-Guard ≠ INPUT_UNCOMMITTED; Hold-Home Story 3.10; Zwei-Run-Identität nicht-vakuum in getrennten Runs; Umlaut-Defer bleibt offen (kein neuer Normalisierungs-Operand); AD-3 read-only (validator/wiki-compiler/adapters/raw/canonical-terms unverändert); Sandbox re-executierbar auf /tmp-Baum, Exit 0. **Loop-2-Auflagen:** **(BS-L2-1a)** §5.15-Pkt.-3-Tabelle + §3.2-Pkt.-3d-Anker + Sandbox R-3/R-5 in Einklang: leere Candidate-Liste bei neuer Einheit → **CREATE-Prädikat zuerst** (eigenständig interpreterbar + Ziel-Pfad ableitbar + Reserviertheits-Check) → sonst **ORPHAN/HOLD** (Zelle 3) → **niemals NO_OP**; NO_OP bleibt ausschließlich Update-Unter-Entscheidung (bestehende Evidenz identisch repräsentiert); R-5 übt den CREATE-Bewertungsraum (bzw. OPRHAN-Negativfall) ab, R-3-Fall-2 vergleicht die reale neue Evidenz gegen den tatsächlichen bestehenden Body (keine handgeschriebenen scratch/.txt). **(BS-L2-2a)** Status-Codes schließen R/C: Rename (R) einer committeten `raw/`-Datei = AD-3-Verletzung → Guard-FAIL; Copy (C) = wie neu (A) akzeptiert; `--diff-filter=ACMR` dokumentiert die Zuordnung; Sandbox-Fallback-Test (M/D/R → Guard-FAIL, A/C → akzeptiert). **(P-L2-1)** Sandbox-`norm()` auf die **eine** Kollaps-Definition (§3.2-Pkt.-1b-Ebene, Kollaps-Klasse `[-–— _]`→`-`, Läufe auf ein `-`, Trim führend/trailend; lowercasing) vereinheitlicht — keine vierte, abweichende Pipeline; Interleaved-Separator-/Umlaut-Grenzfall als benannter Defer (kein Instruktions-Defekt). **(P-L2-2)** R-2-`index.md`-Traversal-Test so ertüchtigt, dass der Guard-Branch real feuert (Fixture enthält den Term) — negatives Assert gegen die Traversal-Exklusion statt statischem Echo. **(P-L2-3)** R-1-Diff↔Manifest-Äquivalenz um einen **Negativ-Zweig** ergänzt (divergentes Manifest → Assert des Run-FAIL vor jeder Mutation). **(P-L2-4)** R-8-„Zwei-Run-Hold-Identität" real über **zwei getrennte Worktrees** über demselben committeten Baum statt Doppel-Read derselben Datei (`run-sandbox.sh` Z. 574–575 — Behebung der Tautologie). **(P-L2-5)** R-6/R-7: Ziel-Slug aus dem echten abgeleiteten Kandidaten (nicht hartgesetzt `TARGET_SLUG="index"/"log"`), plus Ausübung der §5.15-Pkt.-5-Ist-Dateimenge (`git ls-tree`-Schnittmenge) mit Positiv-/Negativ-Fixture; R-8-„tote CAND"-Assertion entdoppeln (ein hartes Nicht-Existenz-Assert, kein `|| true`-Neutrum). **(P-L2-6)** R-9-Receipts um ein `generated.at`-Feld ergänzt und die **at-Exzeption** praktisch ausgeübt (legitime at-Abweichung wird NICHT als AD-16 gemeldet; nicht-at-Feld-Differenz wird HARD-FAIL) — die technisch einzige benannte Zwei-Run-Differenz ist damit real nachgewiesen. **(D-L2-1)** Mehrfach-Term-Vereinigung (D-8) in der Sandbox ausübend belegt (mehrere Terme je Zuwachs-Datei deterministisch abgeleitet und im Receipt geführt) oder explizit als ungetesteter Defer in `deferred-work.md` markiert. **(D-L2-2)** `ls-tree`-Schnittmenge (Pkt. 5) bleibt statisch definiert; da keine gültige Fixture eine kollidierende sonstige Struktur-Ziel-Anlage betrifft, bleibt sie als negative Matrix-Zeile benannt (nicht stiller Ausschluss). **Bekannte-Nicht-Story-Probleme (defer/reject)** bleiben sichtbar: Defer „Orphan voller Stufen b/c + Mehrziel" (Hold-Home Story 3.10 — post-Reconcile, Mehrziel aus D-8); Reject „Fixture-coupling deutsche Literale" (dekorativ, Kollaps-Harmonisierung deckt die zugrundeliegende Instruktions-Unschärfe); Reject „Spec untracked → eingebettete Kommandos unverifiziert" (AD-3/Erhaltungs-Clains durch Sandbox-Verlauf + Isolations-Realität verifiziert). Neu-Verhandlung des Frozen-Intents nötig: **Nein** (nur nicht-frozen Sektionen + abgeleiteter Code betroffen; kein AC-Wortlaut geändert). - **Re-Review Loop-2 Step-04 (2026-08-21, bmad-code-review Re-Run — 2 frische Agent-Layer + Verification Gap):** Blind Hunter (frischer Kontext) + Edge Case Hunter (frischer Kontext) gegen das Loop-2-Re-Ableitungs-Bundle; anschließend **Verification Gap** (gegen Instruktion + Sandbox-Code). **Ergebnis: kein intent_gap; bad_spec-artige Lücken verifiziert und als patch/real behoben (kein weiterer Loopback).** **Blind-Hunter-Findings (F1–F10):** **(F1–F4, F6, F10, reject):** die §5.15-Pkt.-3-Tabelle (Z. 385–395) deckt die Stufe-b/c-Zuordnung explizit ab (Z. 395: „Stufe-b-Zelle", UPDATE bei gewurzelter Erreichbarkeit, ORPHAN/HOLD bei Dangling), der §3.2-Pkt.-3d-Anker (Z. 63) ist vollständig konsistent mit Zelle 2/3 („leere CL → Zelle 2/3, nicht NO_OP"; UNTOUCHED_CONCEPT nur bei gar keiner Zuwachs-Evidenz), Status-Codes inkludieren R (Z. 383: „R = umbenannt"), Normalisierung nennt wörtlich lowercasing — die blind-hunte zitierten Zeilen („Z.22/37/52") existieren so nicht; die behaupteten Anker-Inkonsistenzen liegen nicht vor. **(F5, reject):** Sidecar-Exklusion über `case *.source.md` (Sandbox Z. 163) deterministisch; die Sidecar-Datei ist Teil des Zuwachs-Befunds, trägt aber keinen Term. **(F7, reject):** die zitierte Assertion gehört zu **R-8** (Z. 669–672, Hold-Befund NFR-4), nicht zu R-9/at-Exzeption (Z. 803ff.); durch den BUNDLE-BYTE-Fix (F-27) ist die at-Exzeption zudem strukturell robust. **(F8, bad_spec → REAL behoben):** R-4 zeigte den Guard-Wert nur als „Status M/D erkannt"-Echo und prüfte ohne echten Guard-Abbruch — der „Run-FAIL vor jeder Mutation"-Pfad (Exit 1) wurde nicht real ausgeübt; Fix: Unterlauf, der bei M/D hart mit Exit 1 endet (über `|| G_ABBRUCH=$?`-Fänger unter `set -e`), plus Nachweis „keine neue wiki-Datei" — R-4 zeigt jetzt „Guard-Abbruch real geübt". **(F9, bad_spec → REAL behoben):** R-1 erwartete die Term-Liste als literal `beta beta-kommunikation kommunikation` (hart kodiert, reagiert nicht auf Dateinamen-Änderung); Fix: Erwartung wird aus dem Zuwachs-Dateinamen **ableitungsbasiert gegengeprüft** (gleiche Dateiname→Term-Synthese), kein fest verdrahteter Erwartungswert. **Edge-Case-Hunter-Findings (F-2…F-27):** siehe oben — F-6 (Lowercasing-Pkt.-1, Instruktions-Fix), F-17/F-18/F-20/F-21 (reject/erläutert), F-22/F-27 (BUNDLE-BYTE-FIX), F-15/F-16 (reject: A/C-Äquivalenz, R100-robust), F-23/F-24 (Re-Run-Idempotenz = Sandbox-Ergonomie, benannter Folgepunkt), F-25/F-26 (reject). **Keine Neu-Verhandlung des Frozen-Intents:** die Step-04-Fixes sind Sandbox-Code (R-4-Guard-Abbruch, R-1-ableitungsbasierte Erwartung, R-9-BUNDLE-BYTE) bzw. eine textuelle Klarstellung der bereits geschlossenen §3.2-Pkt.-1b-Normalisierung (F-6); kein AC-Wortlaut geändert. **Sandbox-Nachweis (nach allen Step-04-Fixes):** Exit 0 — R-1..R-9 harte PASS; R-1-Term-Ableitung gegengeprüft, R-4-Guard-Abbruch real, R-9-Bundle-byte-Identität (clean voll gleich, atgap maskiert gleich, divergent hart FAIL).**Verifizierte Findings & Umgang:** **(F-6, patch statt bad_spec, Instruktions-Fix angewandt):** §5.15-Pkt.-1-Termgewinnung nannte das **Lowercasing** im Dateiname→Term-Mapping nicht wörtlich (nur Kollaps-Klasse `[-–— _]`→`-`), während §3.2-Pkt.-1b (Z. 51) und §5.15-Pkt.-2 lowercasing einschließen → Asymmetrie; Fix: §5.15-Pkt.-1 „beide Seiten identisch normalisiert: **lowercasing + Kollaps-Klasse** … (§3.2-Pkt.-1b-Normalisierung)" — harmonisiert mit AC-2/Pkt. 2, kein neuer Operand. **(F-17, bad_spec-Vorwurf → geschwächt zu reject/patch):** die behauptete „schwerwiegende Dateikollision `wiki/meta.md`-über-CREATE" wird durch **§5.7-Pkt.-3-Top-Level-Update-Routing** (Dateikollision → Update-Routing, „kein Duplikat, kein stiller Overwrite") bereits deterministisch ausgeschlossen; die Pkt.-5-Reserviertheits-Schnittmenge (nur die 4 benannten Namen) ist dafür bewusst eine **statisch-definierte Ist-Basis** (Ask-First-Nomenklatur) und die CREATE-Zelle verlangt selbst „kein stiller Overwrite". Kein Guard-Durchbruch. **(F-8/F-9, reject):** Ziel-Slug deterministisch ableitbar via §5.15-Pkt.-5/§5.7-Pkt.-2-Kanonische-ID/§5.1 aus dem abgeleiteten Kandidaten (§5.15 Pkt. 1); Sandbox R-6/R-7 leitet den Slug aus dem abgeleiteten Kandidaten ab. **(F-20, teils annulliert → BUNDLE-FIX-Zusatz):** die Negativ-Kontrolle ist keine Zeilenanzahl-Tautologie — der divergente Run erzeugt eine andere Kandidaten-Liste (konstruierte AD-16-Differenzart), Receipt-Vergleich schlägt hart FAIL. **(F-22, patch → durch Bundle-Fix überdeckt):** at-Exzeption hing am festen Zeilenraster der Receipts; jetzt strukturell robust über den BUNDLE-BYTE-Vergleich (maskierte at-Zeile je Datei). **(F-27, patch bestätigt → BUNDLE-BYTE-FIX angewandt):** R-9 verglich nur **Receipt-Felder**, nicht die **byte-identischen MUTIERTEN Bundle-Bestandteile** (A0-19/§5.15 Pkt. 6): `run-sandbox.sh` mutiert je Lauf `wiki/alpha.md` (einzig laufabhängiges Feld `generated.at`), sichert SHA-256 voll + mit maskierter at-Zeile je Datei und assertiert: clean-vs-clean **voll identisch**, atgap-vs-clean **maskiert identisch** (at-Exzeption an der mutierten Datei), divergent **hart FAIL** — Exit 0, alle R-1..R-9 harte PASS. **(F-18, reject/patch geprüft):** Slug-Heuristik in der Sandbox ist Fixture-Darstellung des ableitbaren Kandidaten (kein hartes `TARGET_SLUG`); Operator ist §5.15-Pkt.-5/§5.7-Pkt.-2. **(F-19/F-23/F-24, patch-Hinweise, kein Loopback-Blocker):** `README`-case/Groß-Schreib-Normalisierung, Sandbox-**Re-Run-Idempotenz** (zweiter Lauf über gleichem `$ROOT` scheitert an wieder-existierenden `wt-*`-Worktree-Verzeichnissen) und stale-`$ROOT`-Reste sind **Sandbox-Ergonomie, keine Instruktions-Defekte** und liegen außerhalb der Story-ACs („Re-executierbar" gilt je frischem `$ROOT`); als benannter Folgepunkt (D-3-Qualitäts-/Ergonomie-Lücke, Home Story 3.13-Härtung) festgehalten. **(F-2/F-3, reject):** Sortierung (LC_ALL=C) + Term-Dedup via Sortierung/besuchte Menge; `norm()` deckt die volle Kollaps-Klasse. **(F-15/F-16, reject):** `C`-Status praktisch nie von git gemeldet (ohne `-C`); A/C-Äquivalenz akzeptiert, Guard robust gegen `R100`. **(F-21, reject):** zwei Runs über getrennte Arbeitstrees desselben Commits (commit-Hash-Differenz in `[bundle]` bestätigt). **(F-25/F-26, reject):** parallel-safe über separate `mktemp`-Roots; NFR-4-Hold-Befunde in Receipts/log-Dateien. **Keine Neu-Verhandlung des Frozen-Intents:** §5.15-Pkt.-1-Lowercasing-Fix ist textuelle Klarstellung der bereits geschlossenen §3.2-Pkt.-1b-Normalisierung; BUNDLE-BYTE-Fix ist Sandbox-Code (abgeleitet), keine AC-/Instruktions-Änderung. **Sandbox-Nachweis (nach Fixes):** Exit 0 — run9a/run9b `wiki/alpha.md`-SHA-256 voll identisch, run9at maskiert identisch, run9n divergent hart FAIL. **Neu-Verhandlung des Frozen-Intents nötig: Nein.** @@ -86,6 +88,10 @@ context: - **Review-Loop-1 bad_spec-Loopback (2026-08-20):** bmad-code-review (Blind Hunter 15 / Edge Case Hunter 18 / Verification Gap 5, D-3-Gewichtung) ergab 7 bad_spec-Findings, 2 patch, 1 defer, 3 reject; kein intent_gap (kein Eingriff in ``). **Auslöser:** die Step-03-Ableitung hat die 6 ACs textuell verankert, aber nicht konsistent/ausführbar gemacht. **KEEP (muss in der Re-Ableitung überleben):** §5.15 als eigene Sektion nach §5.14; die **eine** Routing-Tabelle mit den vier Zellen UPDATE/CREATE/ORPHAN-HOLD/NO_OP; Referenz-statt-Re-Negotiation (bestehende §3/§3.2/§5.9/§5.10/§5.14-Mechaniken textuell unverändert); Raw-Immutability-Guard ≠ INPUT_UNCOMMITTED; Hold-Home Story 3.10; Zwei-Run-Identität nicht-vakuum in getrennten Run; Weak-Defers D-8/D-9 aufgegriffen; Umlaut-Defer bleibt offen; AD-3 read-only (validator/wiki-compiler/adapters/raw/canonical-terms unverändert); Sandbox re-executierbar auf /tmp-Baum. **Zu behebende bad_spec-Defekte (Re-Ableitungs-Auflagen):** (BS-1) §5.14-Pkt.-5-Z.377-Scope-Präzisierung auf das Story-3.9-Ist nachführen (Widerspruch zu §7-Z.449/log); (BS-2) Term-Ableitung instruieren — Dateiname→Term-Mapping (§5.15 Pkt. 1), `source.md`-Sidecar-Exklusion, Status-Codes `A/M/D`, §5.9-Pkt.-6-Diskrepanz-/Fallback-Kopplung, Diff↔Manifest-Äquivalenz; (BS-3) Routing-Tabelle exklusiv machen — NO_OP-Reihenfolge (Unter-Entscheidung im Update-Pfad)/CREATE-vs-ORPHAN-Prädikat/Stufe-b-Zelle konsistent mit Sandbox; (BS-4) Pkt. 5 reservierte-Namen-Menge erschöpfend + Ist-Dateimenge deterministisch (Ask-First-Nomenklatur); (BS-5) `tree=`-Ausnahme dokumentiert oder entfernt (§5.15-Pkt.-6-Receipt-Vergleich — nur `at` als benannte Differenz); (BS-6) R-8 Hold ausüben (NFR-4-log-Hold-Befund + „teilweise erfolgreich" + tote `CAND`); (BS-7) R-9 Negativ-Fixture (AD-16-Abweichung hart assertiert) + Manifest-Pfad nicht-konversationell; (P-1) Sandbox-`set -e` im Hauptkörper (sandbox-3-8-Härtung, kein false-PASS); (P-2) D-9-Aufgegriffen-Text „Stufen a/b/c" präzisieren (R-5 übt nur Stufe-a über die festen zwei Dateien). **Bekannte-Nicht-Story-Probleme (defer/reject)** bleiben sichtbar: Defer „Orphan voller Stufen b/c + Mehrziel" (Hold-Home Story 3.10); Reject „Fixture-coupling deutsche Literale" (R-4/R-8-Patterns) und „Spec untracked → eingebettete Kommandos unverifiziert" (AD-3/Erhaltungs-Clains verifiziert true). Neu-Verhandlung des Frozen-Intents nötig: **Nein** (nur nicht-frozen Sektionen + abgeleiteter Code betroffen). + + +*Appendix (Review-Loop-3, 2026-08-21, autorisiert im Change-Log-Eintrag oben): fehlendes ``-Schließtag nach dem Spec Change Log ergänzt (Präzedenz Spec-3-7 Z. 76 / Spec-3-8 Z. 166 — der Change Log liegt frozen-intern als Frozen-Änderungsdoku; Design Notes/Verification/Suggested Review Order wieder außerhalb des Frozen-Blocks). Kein frozen-interner Wortlaut wurde verändert, außer der im Change-Log dokumentierten I/O-Matrix-`RAW_GUARD_MODIFIZIERT`-Zellen-Nachführung.* + ## Design Notes **Warum §5.15 als eigene Sektion, nicht §3.2-Umbau?** §3.2 ist die Relevanz-Findungs-Mechanik (append-only etabliert, Story 3.8); die Routing-Tabelle ist die **Reconcile-Entscheidungs-Ebene darüber**. §5.15 schließt die Story-3.9-ACs als eigene Sektion, ohne bestehenden §3.2-/§5.9-/§5.10-Wortlaut umzubauen (Fugen-Identität, Re-Negotiation-Vermeidung — gleiche Präzedenz wie §5.14/Story 3.8). @@ -177,7 +183,7 @@ context: **Peripherie (Status & Rekonsiliation)** -- Sprint-Status: Story-3.9-Key auf `in-progress` (finaler `review`/`done`-Flip per Step-04/05). +- Sprint-Status: Story-3.9-Key auf `review` (finaler `done`-Flip per Step-05-Status-Sync nach konvergiertem Review-Loop). [`sprint-status.yaml:62`](sprint-status.yaml#L62) - Defers D-8/D-9: Mehrfach-Term-Vereinigung + Orphan-Term-Ableitung als `aufgegriffen` markiert. @@ -188,3 +194,36 @@ context: - wiki/log.md: Story-3.9-Eintrag (Verankerung + Sandbox-Nachweis) — Erhaltungs-Invariante gewahrt. [`log.md:4`](../../wiki/log.md#L4) + +### Review Findings + +*Review-Loop-3 (bmad-code-review, `full`-Modus) — 4 Layer (Blind Hunter ×2, Edge Case Hunter, Acceptance Auditor; Verification Gap über Retry). Triage: 3 `decision-needed`, 15 `patch` (davon 1 verworfen → **14 anzuwenden**), 2 `defer`, 8 verworfen (zusätzlich 1 patch-Finding nach Re-Verifikation verworfen → **9 verworfen**). Code-Vorab-Lektüre: `run-sandbox.sh` R-1..R-9 (vollständig), `compiler.md` §3.2/§5.14/§5.15/§8, Spec-Frontmatter/I/O-Matrix/Change-Log/Code-Map, `deferred-work.md` D-8/D-9, `wiki/log.md` 3.9-Eintrag.* + +**Decision-Needed** + +- [x] [Review][Decision] **D-3.9-1 — §8-Revision-3.3 retrospektiv mitumgeschrieben (außerhalb Code-Map-Scope, historische Story-3.8-Eintrag umgeformt).** [`compiler.md:500`] Der Diffformt die bereits committete **Revision 3.3** (Story 3.8) um: die `sprint-status.yaml`-Stelle wechselt von „Key 3-8 … `in-progress` (finaler `review`/`done`-Flip im Step-04/05)" zu „Key 3-8 … **`done`** (2026-08-20: …)". Das ist (a) außerhalb der Code Map (nur §5.15 + §7 + **Revision 3.4** + §3.2-Anker sind deklariert) und (b) eine Umformung eines historischen Revisionslog-Eintrags einer anderen Story. `sprint-status.yaml` Z. 61 trägt tatsächlich `done`, aber ob die Historik-Eintrag-Revision zulässig ist, ist eine Intent-Entscheidung. **Optionen:** 1) Revision-3.3-Änderung behalten (3.8 ist faktisch done) + Code Map um „§8-Revision-3.3-Nachführung" ergänzen; 2) Revision 3.3 auf den ursprünglichen Wortlaut zurücksetzen (Status-Nachführung gehört ausschließlich nach Revision 3.4 / sprint-status). +- [x] [Review][Decision] **D-3.9-2 — Status-Kontraktion über die Diff-Artefakte (welcher Status ist für den 3-9-Key maßgeblich?).** [`spec frontmatter:5`, `sprint-status.yaml:62`, `compiler.md:501`] Vier Artefakte sagen vier Dinge über den 3-9-Key: Spec-Frontmatter `status: 'done'`; Suggested-Review-Order + Revision 3.4 „Key … **`in-progress`** (finaler `review`/`done`-Flip im Step-04/05)"; `sprint-status.yaml` Z. 62 **`review`**. Konsistente Lektüre: der Implementierungs-Commit hat `review` gesetzt (Review-Start), der finale `done`-Flip gehört erst nach diesem Review (Step-05). **Optionen:** 1) Frontmatter auf `in-progress` zurücksetzen, Sprint-Status `review` bleibt bis zum Step-05-`done`-Flip (konsistent mit Story 3.7/3.8-Präzedenz „finaler Flip im Step-05"); 2) alles auf `done` ziehen (Review als abschließend betrachten) — braucht aber die D-3.9-1-/Patch-Auflösung vorher. +- [x] [Review][Decision] **D-3.9-3 — Sandbox-Re-Build-Tiefe: die Loop-2-Auflagen haben 11 defekte/tautologische Verifikationen hinterlassen.** [`run-sandbox.sh` R-1..R-9] Die Loop-2-Mandate (BS-L2-1a, P-L2-1..P-L2-6, D-L2-1/2) wurden formal umgesetzt, aber die resultierenden Szenarien üben die pinnte Instruktion **nicht real** aus — sie vergleichen handgeschriebene Werte mit sich selbst. Verifizierte Kern-Mängel: **R-1** Negativ-Manifest = String-Ungleichheit, der Run-FAIL-vor-Mutation-Pfad wird nie gefeuert; **R-1b/R-4** kumulatives `ST_*`-Diff gg. B1 enthält selbst R/M/D, assertet aber „Guard passiert" (Widerspruch zu §5.15 Pkt. 4, R = Guard-FAIL); **R-3** Fall-2 = 2-Wort-Präfix-Check auf eigens überlappendem Body, Stufe-b `STUFE_B_CAND="verwaister-concept"` wird mit sich selbst verglichen; **R-5** `ROUTE_ZETA="CREATE"` dann `[ "$ROUTE_ZETA" = "CREATE" ]` (Tautologie, kein Routing-Code; leere Candidate-Liste → NO_OP-Regression bliebe unsichtbar) + nur Primär-Terme (keine D-8-Vereinigung); **R-6/R-7** hart codiertes `printf 'index-doc'` + nicht-instruktionelle `sed 's/-doc$//'`; **R-8** Receipt wird von Hand gebaut (A/B-Identität garantiert), tote-CAND-Assert gegen nie geschriebene Datei; **R-9** at-Exzeption prüft zwei vom Run selbst geschriebene Konstanten auf Ungleichheit. Das ist kein einzelnes Pflaster, sondern die **Tiefe des Re-Builds** (minimale harte Feuern je Szenario vs. vollständige Re-Implementierung der Routing-/Guard-/Traversal-Mechanik in der Sandbox). **Optionen:** 1) **Voll-Re-Build** — jedes Szenario führt die echte Mechanik aus (Escaper, Guard-Sub-Run mit realer `git mv`/`rm`, Routing-Code statt Literal, Traversal-Stufe), negative Kontrollen feuern real; 2) **Minimal-Härtung** — nur die hartcodierten Tautologien (R-3/R-5/R-8) in echte Vergleichs-Feuern umbauen, Rest als benannte Sandbox-Defer; 3) **Scope-Split** — Re-Build als eigene Folge-Story (3.9.x / 3.10-Vorbereitung), Story 3.9 mit Defer-Liste abschließen. *(Die konkreten, eigenständig fixierbaren Pflaster — Escaper, README, R-9-Bundle-Grep, R-8-Grep, I/O-Matrix, u. a. — sind als `patch`-Findings separat gelistet und unabhängig von dieser Entscheidung anwendbar.)* + +**Patch** + +- [x] [Review][Patch] **P-3.9-1 — Escaper defekt: `sed 's|[][\.*^$+?(){}|]|\\&|g'` nutzt `|` als Trenner UND als Klassen-Mitglied → Metacharaktäre werden nicht escapet.** [`run-sandbox.sh:74`, `run-sandbox.sh:790`] **VERWORFEN (Fehlalarm) — nach Re-Verifikation am 2026-08-21.** Ursprüngliche Begründung („durch Ausführung verifiziert: `v1.0` → `v1&0`"): die „Ausführung" tippte das sed-Programm direkt in Bash-Befehle; die Tool-Transport-Schicht halbiert dabei Backslash-Ebenen (eingegebenes `\\&` ankam als `\&` = Literal-`&`-Ersetzung, **ohne** Backslash) — der getestete Code war damit **nicht** der Datei-Code. **Definitive Gegenprüfung (2026-08-21):** (a) `od -c` auf `run-sandbox.sh` Z. 74/790 belegt: die Datei enthält `\\&` (Zwei-Backslash-Sequenz) — korrektes GNU-sed-Programm; das `|` innerhalb der Klammer-Klasse ist ein literaler Klassen-Mitglied und beendet die Klasse nicht (die Klasse schließt sich am zweiten `]`); (b) Negativ-/Positiv-Kontrolle ausgeführt **aus den Datei-Bytes selbst** (Funktionen via `source` extrahierter Zeilen, keine Neubeeingabe): `match_stufe_a("v1.0", Body-`foo v10 bar`)` → **leer** (kein falsches Match trotz fehlendem Punkt — die `\.`-Escapage wirkt), `match_stufe_a("v1.0", Body-`foo v1.0 bar`)` → **Treffer** (Escape bricht echte Treffer nicht). **Fazit: Escaper ist literal-sicher, Z. 74/790 bleiben unverändert; keine Sandbox-Term-Änderung nötig.** Konsequenz: die im Loop-3-Change-Log genannte „Escaper-Fix" wurde NICHT angewandt (siehe Korrektur-Postscript des Eintrags); die Patch-Anwendungszahl sinkt von 15 auf 14. +- [x] [Review][Patch] **P-3.9-2 — Reserviertheits-Check `case "$TARGET_SLUG" in index|log|source|README)` case-sensitiv, aber `TARGET_SLUG` ist immer lowercased → `README` ist unerreichbar.** [`run-sandbox.sh:591`, `run-sandbox.sh:635`] Ein CREATE mit Ziel `wiki/readme.md` (abgeleitet, lowercased) kollidiert mit dem reservierten Bundleroot-`README.md` (AC-5), wird aber nicht erwischt. Fix: Vergleich case-insensitiv machen (beide Seiten lowercased) bzw. `readme` in die Liste. +- [x] [Review][Patch] **P-3.9-3 — §5.15 Pkt. 1: „die Ausgabe erfolgt deterministisch über `/dev/null`-Stable-Sortierung" ist inkoherent (ungenannter Mechanismus; die Sandbox nutzt `LC_ALL=C sort`).** [`compiler.md:383`] Fix: auf den konkreten, deterministischen Sort-Mechanismus verweisen (z. B. „stable Sortierung nach bytetreuer Kollation (C-Locale)"), `/dev/null`-Begriff entfernen. +- [x] [Review][Patch] **P-3.9-4 — §5.15 Pkt. 5: „Run „teilweise erfolgreich" gemäß §3.2-Endergebnis" ist ein hängender Verweis (einzige „Endergebnis"-Stelle; das Run-Status-Konzept lebt in §3-Kollision-Hold Rev 1.4 / §5.8).** [`compiler.md:397`] Fix: Verweis auf die tatsächliche Run-Status-Definition (§5.8-Instruktions-Hold / §3-Kollision-Hold) korrigieren. +- [x] [Review][Patch] **P-3.9-5 — §5.15 Pkt. 2: Tippfehler „kein Regex-Muster über den *Actor*-Body hinaus" → „Concept-Body" (normativ, nicht nur dekorativ).** [`compiler.md:384`] +- [x] [Review][Patch] **P-3.9-6 — §5.15 Pkt. 1 Status-Code-Wortlaut selbstwidersprüchlich: „Status-Codes `A`/`M`/`D` — `--diff-filter=ACMR` liefert die akzeptierten Zuordnungen (… **R** = umbenannt; die Zuordnung fallspezifisch in Pkt. 4)" vermischt R in „akzeptiert", während Pkt. 4 M/D/R = Run-FAIL definiert.** [`compiler.md:383` vs `compiler.md:396`] Fix: Pkt. 1 so präzisieren, dass die `--diff-filter=ACMR`-Liste den *Befund* beschreibt und die A/C-vs-M/D/R-Zuordnung eindeutig auf Pkt. 4 verweist (R/C nicht als „akzeptiert" im Pkt.-1-Satz). +- [x] [Review][Patch] **P-3.9-7 — R-9-Bundle-Vergleich: `grep -vE '/?(index|log)\.md$'` unanker → schließt neben index.md/log.md (bewusst, aber unbenannt) zusätzlich **jeden Basenamen, der *auf* `index.md`/`log.md` ENDET** (z. B. `myindex.md`, `prelog.md`) still aus dem „byte-identische mutierte Bundle-Bestandteile"-Vergleich aus.** [`run-sandbox.sh:856`] *(Re-Scope 2026-08-21: die Original-Finding-Formulierung „schließt JEDEN `wiki//…`-Pfad still aus" ist falsch — der Muster `/?(index|log)\.md$` matcht ausschließlich Pfade, deren letztes Segment exakt oder -präfixhaft `index.md`/`log.md` endet; normale Area-Concepts wie `wiki//begriff-x.md` bleiben im Vergleich. Der reale Defekt ist die Über-Ausschluss-Menge (Enden-Präfixe) + das unbenannte Bewusstsein.)* Eine Divergenz in einer falsch-geschnittenen Datei wäre unsichtbar (AC-6/A0-19). Fix: anchorn auf `(^|/)(index|log)\.md$` + Kommentar, der die index/log-Ausnahme explizit benannt. +- [x] [Review][Patch] **P-3.9-8 — R-8: Hold-Befund-`grep` nur gegen `wt-hold-a`, nicht gegen `wt-hold-b` (Zwei-Worktree-Hold soll Identität über BEIDE belegen).** [`run-sandbox.sh:719-720`] Fix: Hold-Bullet-Assertion auf beide Worktrees ausdehnen. +- [x] [Review][Patch] **P-3.9-9 — I/O-Matrix-Zeile `RAW_GUARD_MODIFIZIERT` nennt nur „modifiziert/gelöscht" und lässt Rename (R) aus, das §5.15 Pkt. 4 als Guard-FAIL definiert.** [`spec I/O-Matrix`] Fix: „modifiziert/gelöscht/umbenannt (M/D/R)" in die Zeile aufnehmen. +- [x] [Review][Patch] **P-3.9-10 — ``-Block (öffnet Z. 12) ist nie geschlossen — alle 7 folgenden Sektionen fallen de facto in den Frozen-Block.** [`spec:12`] Konvention der Geschwister-Specs 3-7 (Z. 76) und 3-8 (Z. 166): Schließtag **nach dem Spec Change Log**. Fix: `` nach dem Change-Log-Bullet (vor „## Design Notes") ergänzen. +- [x] [Review][Patch] **P-3.9-11 — `sprint-status.yaml` `last_updated: 08-21-2026` trägt das Code-Map-Format `MM-DD-YYYY HH:MM` nicht (HH:MM fehlt).** [`sprint-status.yaml:32`] +- [x] [Review][Patch] **P-3.9-12 — `deferred-work.md` D-9-„aufgegriffen"-Text überzeichnet: „scannt den VOLLEN Kandidaten-Baum (`wiki/*.md`-Glob)" bzw. „Stufen a/b/c", aber R-5 scannt nur Stufe-a über den Root-`wiki/*.md`-Glob (keine Stufen b/c, keine Area-Rekursion).** [`deferred-work.md:554`] Fix: Aufgegriffen-Text auf den tatsächlich ausgeübten Umfang (Stufe-a, Root-Glob) präzisieren; Rest (Stufen b/c) als verbleibenden Defer benannen. +- [x] [Review][Patch] **P-3.9-13 — `wiki/log.md` Story-3.9-Eintrag (Z. 4) trägt das von Spec-Task-Z. 68 / Verification-Cmd-4 verlangte explizite „Validator-Verdikt: alle `wiki/`-Dateien SUCCESS" nicht (nur Verankerung + Sandbox-Nachweis + Erhaltungs-Invariante).** [`wiki/log.md:4`] +- [x] [Review][Patch] **P-3.9-14 — Worktrees `wt-hold-a/b` (R-8) und `wt-a/b/n/at` (R-9) werden AUSSERHALB der `isolate`-Verzeichnisse unter `$ROOT` angelegt → Re-Run über ein veraltetes `$ROOT` schlägt fehl (idempotente Re-Executierbarkeit fehlt).** [`run-sandbox.sh:664-665`, `run-sandbox.sh:763-766`] +- [x] [Review][Patch] **P-3.9-15 — `local rc=$?` nach barem Subshell unter `set -e` (R-8 `hold_run`, R-9 `run9_worktree`) → die benannte HARD-FAIL-Diagnostik ist unerreichbar (tote Diagnostics; Hauptskript bricht trotzdem über `set -e` ab, also kein false-PASS, aber die geführte Fehlermeldung druckt nie).** [`run-sandbox.sh:694`, `run-sandbox.sh:867`] Fix: `rc` direkt nach dem Subshell-Ausdruck erfassen bzw. den Abbruch-Pfad ohne tote Variable formulieren. + +**Defer** + +- [x] [Review][Defer] **W-3.9-1 — `norm()`/`tr` ohne `LC_ALL`-Pinning: unter C/POSIX-Locale zerfällt die Byte-Klasse `[–—]` und `tr 'A-Z' 'a-z'` ist locale-abhängig → Kanon-Form hängt am Host-Locale.** [`run-sandbox.sh:53-61`] — deferred, bekannt (Story-3.8-Defer D-7), Scope jetzt auf den kompletten 952-Zeilen-Sandbox-3.9 ausgeweitet. +- [x] [Review][Defer] **W-3.9-2 — Positive Zwei-Run-Identität (R-9 clean A==B) ist strukturell trivial: Plan-/Form-Literale sind Skript-Konstanten, die Nicht-Vakuum-Last tragen die echten Content-Hashes + Negativ-Kontrolle.** [`run-sandbox.sh:771-869`] — deferred, Präzedenz DES Story-3.8-Defer „DET-1/DET-2-Plan-Literale" (Home: Story-3.13-Abnahme, echte Gate-Runs). diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 58f15a2..d751a03 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-21-2026 +last_updated: 08-21-2026 10:16 project: wow20 project_key: NOKEY tracking_system: file-system @@ -59,7 +59,7 @@ development_status: 3-6-lease-staleness-recovery-basis-absichern: done 3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: done 3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: done # Review-Loop-3-Abschluss 2026-08-20 (bmad-code-review; D-2-Entscheidung). Hinweis: die Abschluss-Reihenfolge „3.9 → 3.10 / 3.11 → 3.12 → 3.8 Abschluss → 3.13 Abnahme" des genehmigten Sprint-Change-Proposal 2026-08-20 betrifft das Epic-3-Abnahmegate (3.13) — die Story-3.8-Instruktionsverankerung ist abgeschlossen (done); die 3.9–3.12-Verankerungen und die 3.13-Abnahme bleiben offen (epic-3 remains in-progress). - 3-9-deterministische-relevanz-und-reconcile-routing-schliessen: review + 3-9-deterministische-relevanz-und-reconcile-routing-schliessen: done # Review-Loop-3-Abschluss 2026-08-21 (bmad-code-review, 4 Layer; D-3.9-1/2/3 = 1/1/2 — empfohlene Optionen; kein Loopback). Hinweis: der finale `done`-Flip ist der Step-05-Status-Sync nach konvergiertem Review-Loop (Präzedenz Story 3.7/3.8); die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress). 3-10-inkrementelle-update-und-synthese-erhaltung-absichern: backlog 3-11-root-scope-leasing-atomar-akquirieren: backlog 3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen: backlog diff --git a/schema/compiler.md b/schema/compiler.md index 2b200bb..15dcb37 100644 --- a/schema/compiler.md +++ b/schema/compiler.md @@ -380,8 +380,8 @@ Diese Sektion ist der **einzige Instruktions-Ort der geschlossenen Determinismus Die Relevanz-Findungs-Mechanik (§3.2, Story 3.2) und die Determinismus-/Bestätigungs-Mechanik (§5.14, Story 3.8) bleiben textuell unverändert; diese Sektion **schließt die Routing-Ebene des Reconcile als eine geschlossene, exklusive Tabelle** (AD-17h/A0-19, Story 3.9; §5.14-Pkt.-5-/§7-„Story-3.9-ACs noch nicht verankert"-Vorbehalt hiermit aufgelöst — siehe §5.14-Pkt.-5-Scope-Präzisierung und §7-Bullet). Sie führt **kein** neues Prädikat, **keine** neue §7-Invaliditätsklasse und **keinen** neuen Frontmatter-/Format-Key ein; jede Zelle referenziert die bestehende Mechanik **Wortlaut-unverändert** (Referenz statt Re-Negotiation). Die AD-3-read-only-Invariante bleibt: `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` und `schema/canonical-terms.md` (append-only) werden nicht mutiert. -1. **Termgewinnung (geschlossener, geordneter Algorithmus — AC-1):** Die Candidate-Terme eines Runs werden **nicht** vom Producer frei gewählt, sondern deterministisch aus der **Zuwachs-Sicht der committeten Evidenz** gewonnen (§5.9 Pkt. 6 R-1: `git diff --name-only -- raw/`). Pro Zuwachs-Datei unter `raw/` gilt: **Status-Codes `A`/`M`/`D`** — `--diff-filter=ACMR` liefert die akzeptierten Zuordnungen (die Status-Bedeutung: **A** = neu/kopiert (C zählt als A), **M**/*D* = modificiert/gelöscht, **R** = umbenannt; die Zuordnung fallspezifisch in Pkt. 4); die Ausgabe erfolgt deterministisch über `/dev/null`-Stable-Sortierung. Der **Dateiname→Term-Mapping**: vom Dateinamen die `.md`-Endung und ggf. ein Datums-Suffix (`YYYY-MM-DD`) strippen, verbleibende Trennzeichen (`-`, `_`, `–`, `—`, Leerzeichen) **auf `-` kollabieren** — beide Seiten identisch normalisiert: **lowercasing + Kollaps-Klasse `[-–— _]`→`-`** (genau die §3.2-Pkt.-1b-Normalisierung, Kollaps-Reichweite/Trim, §5.14 Pkt. 5; AC-2-Symmetrie) → dieser kollabierte, lowercasene Dateiname ist der **primäre Term** je Zuwachs-Datei; ein Dateiname mit mehreren `-`-getrennten, aus ≥2 Zeichen bestehenden Segmenten wird als **Vereinigung** mehrerer Terme geführt (Mehrfach-Term-Vereinigung, Defer **D-8** — hier aufgegriffen): der kollabierte Gesamt-Name (primärer Term) **plus** jedes ≥2-Zeichen-Segment als weiterer Term; **Einzelzeichen-Segmente** (Varianten-/Abkürzungs-Marker wie `-a`/`-b`) tragen **keinen** eigenen Term (deterministische Ableitung: `-`-getrennte Segmente mit bedeutungstragendem Inhalt, Länge ≥ 2). **`source.md`-Sidecars bleiben exkludiert** (Dokumentations-Seiteneffekt der Source-Bereitstellung, kein Zuwachs-Inhalt; die Sidecar-Datei trägt selbst keinen eigenen Term bei). **Alternativ zulässig:** ein **committetes, persistiertes Term-Manifest** als Run-Input (Teil des kanonischen Eingabemanifests, §5.14 Pkt. 2); es ist die **einzige** explizite Alternative — nie freie Producer-Auswahl. Die geordnete Term-Liste wird Bestandteil des **Run-Receipts** (AC-1/AC-6, §5.14 Pkt. 2). **Diskrepanz-/Fallback-Kopplung (§5.9 Pkt. 6):** widersprechen `git diff`-Befund und SHA-256-Record derselben Datei, gewinnt `git diff` (Commit-Boundary-Prinzip); ohne vorherige Mutations-Boundary gelten alle `raw/`-Dateien als Zuwachs. **Diff↔Manifest-Äquivalenz:** ein als Run-Input übergebenes Term-Manifest muss mit der aus der Zuwachs-Sicht ableitbaren Menge übereinstimmen (deterministischer Abgleich; abweichendes Manifest = Run-FAIL vor der Mutation). -2. **Symmetrische Normalisierung + literal-sichere Suche (AC-2):** Suchterm und Concept-Body werden **identisch** normalisiert (lowercasing + `[-–— _]`→`-`-Kollaps, §3.2-Pkt.-1b — die Em-Dash-Variante bleibt in der Kollaps-Klasse, §5.14 Pkt. 5). Der Stufe-a-Vergleich ist **literal-sicher**: es wird auf Token-Basis verglichen (normalisierter Suchterm als literales Wort, begrenzt durch Nicht-Buchstaben-/Nicht-Ziffern-Grenzen — kein Teilzeichen-Trunkierungs-Treffer, kein Regex-Muster über den Actor-Body hinaus). **`index.md`-Treffer bleiben für die Traversal-Stufe** (§3.2-Pkt.-2b, TRAVERSAL_REACH_ONLY) erhalten — ein Stufe-a-Treffer auf `wiki/index.md` (oder eine Area-`index.md`) löst Stufe b aus, `index.md` selbst ist **kein Concept-Kandidat** (T2-Kommentar-Konvention; §3.2-Pkt.-2b-Satz). Kein neuer Normalisierungs-Operand (Umlaut-vs-Transkription-Divergenz bleibt benannter Defer, Ask-First). +1. **Termgewinnung (geschlossener, geordneter Algorithmus — AC-1):** Die Candidate-Terme eines Runs werden **nicht** vom Producer frei gewählt, sondern deterministisch aus der **Zuwachs-Sicht der committeten Evidenz** gewonnen (§5.9 Pkt. 6 R-1: `git diff --name-only -- raw/`). Pro Zuwachs-Datei unter `raw/` gilt: **Status-Codes des Zuwachs-Befunds** — `git diff --name-status -- raw/` liefert **A** (neu), **M** (modifiziert), **D** (gelöscht), **R** (umbenannt), **C** (kopiert); `--diff-filter=ACMR` begrenzt die Befund-Extraktion auf die relevanten Zuordnungen. Die **Akkzeptanz-/Ablehnungs-Regel** der Status folgt **Pkt. 4**: A/C (C zählt als A — neu/separat versioniert) akzeptiert, M/D/R → Run-FAIL vor jeder Mutation (dort fallspezifisch definiert). Die Ausgabe erfolgt deterministisch über eine **bytetreue Stable-Sortierung (C-Locale-Kollation, `LC_ALL=C` — keine Host-Locale-Abhängigkeit)**. Der **Dateiname→Term-Mapping**: vom Dateinamen die `.md`-Endung und ggf. ein Datums-Suffix (`YYYY-MM-DD`) strippen, verbleibende Trennzeichen (`-`, `_`, `–`, `—`, Leerzeichen) **auf `-` kollabieren** — beide Seiten identisch normalisiert: **lowercasing + Kollaps-Klasse `[-–— _]`→`-`** (genau die §3.2-Pkt.-1b-Normalisierung, Kollaps-Reichweite/Trim, §5.14 Pkt. 5; AC-2-Symmetrie) → dieser kollabierte, lowercasene Dateiname ist der **primäre Term** je Zuwachs-Datei; ein Dateiname mit mehreren `-`-getrennten, aus ≥2 Zeichen bestehenden Segmenten wird als **Vereinigung** mehrerer Terme geführt (Mehrfach-Term-Vereinigung, Defer **D-8** — hier aufgegriffen): der kollabierte Gesamt-Name (primärer Term) **plus** jedes ≥2-Zeichen-Segment als weiterer Term; **Einzelzeichen-Segmente** (Varianten-/Abkürzungs-Marker wie `-a`/`-b`) tragen **keinen** eigenen Term (deterministische Ableitung: `-`-getrennte Segmente mit bedeutungstragendem Inhalt, Länge ≥ 2). **`source.md`-Sidecars bleiben exkludiert** (Dokumentations-Seiteneffekt der Source-Bereitstellung, kein Zuwachs-Inhalt; die Sidecar-Datei trägt selbst keinen eigenen Term bei). **Alternativ zulässig:** ein **committetes, persistiertes Term-Manifest** als Run-Input (Teil des kanonischen Eingabemanifests, §5.14 Pkt. 2); es ist die **einzige** explizite Alternative — nie freie Producer-Auswahl. Die geordnete Term-Liste wird Bestandteil des **Run-Receipts** (AC-1/AC-6, §5.14 Pkt. 2). **Diskrepanz-/Fallback-Kopplung (§5.9 Pkt. 6):** widersprechen `git diff`-Befund und SHA-256-Record derselben Datei, gewinnt `git diff` (Commit-Boundary-Prinzip); ohne vorherige Mutations-Boundary gelten alle `raw/`-Dateien als Zuwachs. **Diff↔Manifest-Äquivalenz:** ein als Run-Input übergebenes Term-Manifest muss mit der aus der Zuwachs-Sicht ableitbaren Menge übereinstimmen (deterministischer Abgleich; abweichendes Manifest = Run-FAIL vor der Mutation). +2. **Symmetrische Normalisierung + literal-sichere Suche (AC-2):** Suchterm und Concept-Body werden **identisch** normalisiert (lowercasing + `[-–— _]`→`-`-Kollaps, §3.2-Pkt.-1b — die Em-Dash-Variante bleibt in der Kollaps-Klasse, §5.14 Pkt. 5). Der Stufe-a-Vergleich ist **literal-sicher**: es wird auf Token-Basis verglichen (normalisierter Suchterm als literales Wort, begrenzt durch Nicht-Buchstaben-/Nicht-Ziffern-Grenzen — kein Teilzeichen-Trunkierungs-Treffer, kein Regex-Muster über den Concept-Body hinaus). **`index.md`-Treffer bleiben für die Traversal-Stufe** (§3.2-Pkt.-2b, TRAVERSAL_REACH_ONLY) erhalten — ein Stufe-a-Treffer auf `wiki/index.md` (oder eine Area-`index.md`) löst Stufe b aus, `index.md` selbst ist **kein Concept-Kandidat** (T2-Kommentar-Konvention; §3.2-Pkt.-2b-Satz). Kein neuer Normalisierungs-Operand (Umlaut-vs-Transkription-Divergenz bleibt benannter Defer, Ask-First). 3. **Die eine exklusive Routing-Tabelle (AC-3):** Nach Interpretation und Reconcile-Erhebung (§3/§3.2) gilt **genau eine** der folgenden Zellen, in **dieser** Prüf-Reihenfolge (analog §5.9-Abgrenzungs-Reihenfolge, Design Notes — NO_OP ist eine Unter-Entscheidung des Update-Pfads, keine eigenständige Zelle für leere Candidate-Listen): | # | Prüfung (in dieser Reihenfolge) | Zelle | referenzierte Mechanik (Wortlaut-unverändert) | @@ -394,7 +394,7 @@ Die Relevanz-Findungs-Mechanik (§3.2, Story 3.2) und die Determinismus-/Bestät **Leere Candidate-Liste (Zelle-2/Zelle-3-Bewertungsraum):** Trifft kein Term ein bestehendes Concept (leere Candidate-Liste), ist die Einheit im **CREATE/ORPHAN-Bewertungsraum** zu klassifizieren (Zellen 2/3): ist sie als eigenständige, inhaltlich begründete Wissenseinheit interpreterbar (§2-Interpretation) und ist ein deterministischer Ziel-Pfad ableitbar (Pkt. 5-Reserviertheits-Check passiert) → **CREATE** (Zelle 2); andernfalls (nicht klassifizierbar, kein Ziel-Pfad) → **ORPHAN/HOLD** (Zelle 3, fail-closed). **Niemals** NO_OP bei leerer Candidate-Liste. Der §3.2-Pkt.-3d-NO_MATCH-Anker (`UNTOUCHED_CONCEPT`/Story-3.1-Pfad) gilt unverändert für den Fall, dass gar keine neue Evidenz/Zuwachs-Einheit zu reconciliieren ist (Ghost-Diff-negativ, §5.9 Pkt. 5); eine **neue Evidenz** mit leerem Treffer-Match ist dagegen im Zelle-2/Zelle-3-Raum zu bewerten — nicht NO_OP. **Stufe-b-Zelle (Traversal via `index.md`, §3.2-Pkt.-2b):** Candidate-Pfade aus dem Stufe-b-Traversal erhalten **keine eigene Zelle**, sondern werden derselben Tabelle zugeführt — ein Stufe-b-Kandidat, der einen bestehenden Concept-Pfad erreicht (gewurzelte Erreichbarkeit, §5.8), ist **UPDATE** (Zelle 1); ein Stufe-b-Kandidat, der über `index.md` auf eine **nicht existierende** Concept-Datei verweist (Dangling-Link im Traversal, §5.7-Pkt.-4-Auflösung), ist **ORPHAN/HOLD** (Zelle 3, kein CREATE — die `index.md`-Verlinkung ist kein eigenständiger Neu-Anlage-Stimulus; konsistent mit der Sandbox-R-5-Stufe-a-Regel). Jede Abweichung von genau-einer-Zelle ist ein AD-16-Klassifikationsdefekt (§5.14 Pkt. 4), keine stille Mehrfachklassifikation. 4. **Raw-Immutability-Guard (AC-4):** Der Guard prüft den **Zuwachs-Befund** auf committete `raw/`-Dateien mit Status **`M`/`D`/`R`** (modifiziert/gelöscht/umbenannt — `git diff --name-status -- raw/`; die `raw/`-Treffer-Extraktion folgt §5.9 Pkt. 6 R-1). **Befund positiv (committete `raw/`-Datei im Zuwachs modifiziert/gelöscht/umbenannt)** → **Run-FAIL vor jeder Mutation** (AD-3: keine modifizierte/gelöschte/umbenannte committete Source als Input — unabhängig vom Commit-Zustand der Working-Copy, die der §5.9-P2-Block-Element-(1)-`INPUT_UNCOMMITTED`-Abbruch adressiert; der Guard ergänzt diesen, er ersetzt ihn nicht). Akzeptiert werden **nur neu hinzugefügte (`A`) oder separat versionierte Sources** (neue Datei (A), eine **Copy/Version** (C zählt als A) oder eine eigene Versions-Datei — AD-3-„neue Version einer Source als neue/versionierte Source"); ein **Rename (`R`)** einer committeten `raw/`-Datei wird behandelt wie M/D — Run-FAIL (ein Rename kollidiert mit der `raw/`-Immutabilität; AD-3, Ask-First). Der Befund wird **textuell benannt** (NFR-4) und ist Bestandteil des Run-Receipts. -5. **Reservierte Zielpfade (AC-5):** Ein neu zu erzeugendes Concept-Ziel (CREATE-Zelle) mit Slug **`index`**, **`log`** oder einem **sonstigen reservierten Bundle-Namen** wird **nicht geschrieben**; der **deterministische Hold** verlangt eine **disambiguierte Identität** (z. B. Pfad-Erweiterung um einen Bereichs- bzw. spezifizierenden Bestandteil, der die Kollision mit der `index.md`-Hierarchie bzw. dem `log.md`-Typ auflöst, Vertrag §5/§6) — es wird **keine** Datei erzeugt und der Hold-Befund textuell benannt (NFR-4; Run „teilweise erfolgreich" gemäß §3.2-Endergebnis, analog §5.8-Instruktions-Hold). **Reservierte-Namen-Menge (erschöpfend, Ask-First-Nomenklatur):** `index` (Bundleroot-`index.md` + Area-`index.md`, Punkt-10/§5.7 Pkt. 5), `log` (Bundleroot-`wiki/log.md`, Vertrag §5), **`source`** (Sidecar-`source.md`-Kollision, §1.4-Artefakt-Ausnahme), **`README`** (Bundleroot-`wiki/README.md`, First-Line-Kickstart, §1.1). **Ist-Dateimenge deterministisch:** die Prüfung gegen die Ist-Dateimenge unter `wiki/` ist statisch über den committeten Baum definiert (keine Run-Zeit-Erhebung): zu jedem Run-Zeitpunkt ist die Kandidaten-Menge der Reserviertheits-Kollision die **Schnittmenge** aus (a) der erschöpfenden benannten Liste und (b) den im committeten Baum unter `wiki/` existierenden Datei-Basen (`git ls-tree` — datei-basename ohne `.md`); weitere reservierte Namen jenseits dieser Menge = Ask-First (Boundaries & Constraints). Die zugrunde liegende Fixture-Anlage ist als negative Matrix-Zeile („keine gültige Fixture erzeugt ein kollidierendes sonstiges Struktur-Ziel") benannt — kein stiller Ausschluss. +5. **Reservierte Zielpfade (AC-5):** Ein neu zu erzeugendes Concept-Ziel (CREATE-Zelle) mit Slug **`index`**, **`log`** oder einem **sonstigen reservierten Bundle-Namen** wird **nicht geschrieben**; der **deterministische Hold** verlangt eine **disambiguierte Identität** (z. B. Pfad-Erweiterung um einen Bereichs- bzw. spezifizierenden Bestandteil, der die Kollision mit der `index.md`-Hierarchie bzw. dem `log.md`-Typ auflöst, Vertrag §5/§6) — es wird **keine** Datei erzeugt und der Hold-Befund textuell benannt (NFR-4; Run-Status „teilweise erfolgreich" gemäß §5.8-Instruktions-Hold, der den Status definiert — §5.8 Pkt. 2/3). **Reservierte-Namen-Menge (erschöpfend, Ask-First-Nomenklatur):** `index` (Bundleroot-`index.md` + Area-`index.md`, Punkt-10/§5.7 Pkt. 5), `log` (Bundleroot-`wiki/log.md`, Vertrag §5), **`source`** (Sidecar-`source.md`-Kollision, §1.4-Artefakt-Ausnahme), **`README`** (Bundleroot-`wiki/README.md`, First-Line-Kickstart, §1.1). **Ist-Dateimenge deterministisch:** die Prüfung gegen die Ist-Dateimenge unter `wiki/` ist statisch über den committeten Baum definiert (keine Run-Zeit-Erhebung): zu jedem Run-Zeitpunkt ist die Kandidaten-Menge der Reserviertheits-Kollision die **Schnittmenge** aus (a) der erschöpfenden benannten Liste und (b) den im committeten Baum unter `wiki/` existierenden Datei-Basen (`git ls-tree` — datei-basename ohne `.md`); weitere reservierte Namen jenseits dieser Menge = Ask-First (Boundaries & Constraints). Die zugrunde liegende Fixture-Anlage ist als negative Matrix-Zeile („keine gültige Fixture erzeugt ein kollidierendes sonstiges Struktur-Ziel") benannt — kein stiller Ausschluss. 6. **Zwei-Run-Identität (AC-6, AD-17h/A0-19):** Gleicher Git-State + gleiches Eingabemanifest ⇒ identische Candidate-Liste, Reihenfolge und Routing-Entscheidung. **Re-executierbarer Nachweis:** Die Sandbox (`_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh`) führt **zwei getrennte Läufe über getrennte, saubere Worktrees** desselben committeten Baums aus und vergleicht die Run-Ausgänge (Candidate-Listen, Term-Listen, Routing-Entscheidungen; inkl. der Hold-Entscheidung über einen zweiten Worktree) **gegeneinander** — nicht gegen vorab fixierte Erwartungswerte (keine hart codierten erwarteten Pläne/Concept-Bodies; keine pauschale `verified`-Maskierung). **Benannte Differenz-Menge:** der Vergleich lässt allein den `generated.at`-Wanduhr-Gap als Differenz zu (§5.14 Pkt. 3 — kein `tree=`-/Pfad-Prefix-Vergleichs-Ausschluss); die Sandbox erzeugt in den Receipts ein `generated.at`-Feld und übt die at-Exzeption praktisch (legitime at-Abweichung wird erkannt und **nicht** als AD-16 gemeldet; eine andere als-at-Differenz wird **hart als FAIL** assertiert); jede weitere Abweichung ist ein AD-16-Klassifikationsdefekt (§5.14 Pkt. 4) und wird textuell benannt (NFR-4), bevor der Vertrag als bestätigt gilt. Der Nachweis umfasst **positive UND negative Fixtures** (positive: Identität über zwei Läufe; negativ: eine konstruierte AD-16-Abweichung wird **hart als FAIL assertiert** — kein Vakuum). **§7-Scope-Präzisierung (Story-3.9-Rest):** Der §7-Relevanzbestimmung-Bullet ist auf die §5.15-Verankerung nachgeführt (siehe §7); die verbleibenden Story-3.9-AC-Inhalte sind damit nicht mehr „noch nicht verankert", sondern in §5.15 Pkt. 1–6 geschlossen. Der Hold-Ausbau (post-Reconcile-Orphan, Mehrziel) verbleibt Story 3.10; die AD-16-Klassifikation/semantische Kollisionsauflösung bleibt Epic 4. **Abschlussklausel (Story 3.9):** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`adapters/`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **keine fünfte Routing-/Update-Klasse**; die **vier** Zellen bleiben UPDATE/CREATE/ORPHAN-HOLD/NO_OP (NO_OP als Update-Unter-Entscheidung, leere Candidate-Liste → Zellen 2/3, §3.2-Pkt.-3d-Anker entsprechend nachgeführt); **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; `schema/canonical-terms.md` unverändert (kein neuer Normalisierungs-Operand — Umlaut-vs-Transkription bleibt benannter Defer); Commit-Boundary-Regel unverändert. @@ -498,4 +498,4 @@ Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in A - **Revision 3.1 (2026-08-19, Story 3.6):** Neue Sektion **§5.12 „Lease-Staleness & Recovery-Basis (Story 3.6)"** eingefügt (nach §5.11, vor §6) — die **verbindliche Verankerung der Staleness-/Recovery-Dimension** (AD-17d, A0-15; §5.11-Seam-Kriterium S-1 und Pkt.-7-Text sowie §5.11 Pkt. 1 „verbleibt bis Story 3.6" bleiben textuell unverändert): (1) **Lease-Registrierung & generationenbasiertes TTL** — Registrierung im **Clone-Root-State**, monotoner Run-Generator (`generation`-Zähler je Clone-Root, deterministisch aus dem committeten Git-State ableitbar: `lease/`-Baumableitung oder Mono-Commit-`lease-granite-root`-Marker), **TTL-Ablauf-Kriterium generationen-basiert** (ältere/niedrigere Erzeugungs-Generation = stale, blockiert keinen nachfolgenden Run), **kein Wanduhr-Timestamp** im Lockfile-/Registrierungs-Format (A0-20; streng durchsetzendes Zeit-TTL = Ask-First); (2) **holder_id-Ableitung** (deterministischer Default `holder_id := -`, Defer aufgegriffen); (3) **Verwaist-Klassifikation** (Übernehmen gg. erneute Merge-Base-Prüfung oder Stale-Markieren via Registry-Marker, je `log.md`-Eintrag; verwaiste Leases **nie still gelöscht**, AD-17e; Konflikt → AD-16-Pfad/Eskalation AD-17g); (4) **baseline_commit-Merge-Base-Diskrepanz-Regel** (git-merge-base gewinnt, notierter SHA = Sekundär-Fingerprint, Fallback ohne Boundary; Defer aufgegriffen); (5) **`raw/`-Recovery-Basis & native `git stash`-Variante** (`raw/` immutable AD-3 als Zugriffs-/Consistency-Basis, Real-Baum-Beweis auf Sandbox-Evidenzwege beschränkt, EC-1-Grenze; `git stash push -- ` als zweitezulässige Schutzvariante, Defer aufgegriffen); (6) **Registrierungs-Invariante & kumulativer Registry-Aufbau über Runs** (Gen > erzeugend oder gleiche Gen, hält den sichtbar höchsten Reg-Generator; kein eigener `# Log`-Stand — `wiki/log.md` alleiniger Aufzeichnungs-Ort, Vertrag §5; Defer aufgegriffen); (7) **`log.md`-Eintragspflicht & Determinismus-Vertrag** (Registrierung/TTL, Lease-Übernahme, Stale-Markierung, Recovery-Basis-Nutzung als Datumsgruppen-Einträge; Registrierung/TTL/Verwaist-Klassifikation/log.md-Texte deterministisch aus dem committeten Git-State). **§7:** Staleness/Recovery-Vorbehalt **aufgelöst** („in §5.12 verankert (Story 3.6)"). **§8:** Normreferenzen AD-17d/A0-15 von reiner Story-Zuordnung auf **§5.12-Anker** angehoben (`AD-17d (Lease-Staleness, §5.12)`, `A0-15 (Lease-Staleness, §5.12)`). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Frontmatter-Key für Lease-Metadaten** (Lease lebt in Git/Datei-Ebene, Vertrag §3.1–§3.7 unverändert); kein Prädikat-/Format-Key; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-6-lease-staleness-recovery-basis-absichern` → **`in-progress`**. Sandbox-Nachweis (STALE-1..STALE-6 + Erhaltungs-Invariante, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.6, Revision 3.1). - **Revision 3.2 (2026-08-20, Story 3.7):** Neue Sektion **§5.13 „Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)"** eingefügt (nach §5.12, vor §6) — die **verbindliche Verankerung der logischen Phasen-Disziplin** (AD-6, A0-7; §7-Vorbehalt der Reason/Mutate-Trennung aufgelöst): (1) **Änderungsplanung** — der §5.9-Pkt.-6-P2-Block wird Wortlaut-unverändert als Änderungsplanungs-Phase der AD-6-Kette institutionalisiert (textuell festgehaltener konsistenter Plan: Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheits-Liste, Struktur-Erhaltung; Plan-Defizit textuell benannt NFR-4 und verhindert die Mutation — I/O-Matrix `PLAN_BEABSICHTIGT`); (2) **Plan-Freeze** (Veränderungs-Sperre nach Phasenabschluss, Kopplung an die §5.9-Pkt.-5-Ghost-Diff-Probe; erlaubte Pfad-Menge = Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ `index.md` — `PLAN_FREEZE`); (3) **Zustands-Restaurations-Invariante** (Post-Rollback-Diff gg. Baseline leer / Bundle == valide committet; §5.3-Pkt.-3-/§6-Pkt.-3-Rollback und Ghost-Diff-Rollback Wortlaut-unverändert, Endzustands-Konsistenz als git-prüfbare Eigenschaft — `MUTATION_ABBRUCH`, A0-7-AC-2); (4) **kein Teilerfolg als fertige Mutation** (`MUTATION_TEILFOLGE`, AD-17f unverändert); (5) **Validierungsphase `VALIDATION_FAIL`** (§6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent); (6) **Validierungsphase `VALIDATION_SUCCESS`** (Mutationen als Ganzes committet erst nach Diff-Selbsttest ohne Ghost-Diff, §6-Pkt.-4-Nachweis, kein Wanduhr-Trigger A0-20 — A0-7-AC-3); (7) **keine eigene Workflow-Engine** (`KEINE_EIGENE_ENGINE`: logische Trennung in einer Session, kein Prozess/Server/MCP, D-3/AD-11 — A0-7-AC-4). Die **AD-6-Phasen-Zuordnungstabelle (§0 ↔ vier AD-6-Phasen)** ist eine Klassifikation ohne neue Norm: §0 bleibt die deterministische Takt-Folge (Wortlaut unverändert, keine Um-Nummerierung, keine neue Phase); §5.9-Pkt.-6-P2-Block, §5.3-Pkt.-3/§6-Pkt.-3-Rollback und §5.9-Pkt.-5-Diff-Probe bleiben **textuell unverändert** (Rückverweise, keine Doppel-Instruktion); §5.12-Seam-Satz unverändert (Lease-Staleness/Recovery bleibt Story 3.6). **§7:** Reason/Mutate-Vorbehalt **aufgelöst** („in §5.13 verankert (Story 3.7)"-Bullet, AD-6/A0-7, AC-1/2/3/4; bestehende Story-Bullets unverändert). **§8:** Normreferenz AD-6 von reiner Story-Zuordnung auf **§5.13-Anker** angehoben (`AD-6 (Reason/Mutate-Trennung, §5.13)`); A0-7-Reihenfolge-Referenzen in der Epic-Zeile um §5.13-Bezug nicht neu aufgelistet (A0-7 bleibt in der bestehenden Epic-Enum, die §5.13-Ankerung erfolgt über die Spine-AD-6-Zeile und den neuen §7-Bullet). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; keine neue Workflow-Engine (AD-6); §0-Phasen-Listentext unverändert; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell` → **`review`** (Implementierungs-Flip `in-progress` + Review-Start-Flip `review` im selben Commit; der finale `done`-Flip erfolgt im Step-05-Status-Sync). Sandbox-Nachweis (CONSIST-1..CONSIST-7 + Erhaltungs-/Restaurations-Invariante, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.7, Revision 3.2). - **Revision 3.3 (2026-08-20, Story 3.8):** Neue Sektion **§5.14 „Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)"** eingefügt (nach §5.13, vor §6) — die **geschlossene, aus dem committeten Git-State ableitbare Bestätigungs-Mechanik des Determinismus-Vertrags** (AD-17h/FT-10/A0-19; §3.2/§5.9/§5.10/§5.11/§5.12/§5.13-Rückverweise verankern den Vertrag, §5.14 die Bestätigung): (1) **Bundle-State-Definition** (deterministische Projektion des committeten Git-States: committeter Baum unter `wiki/`/`raw/` gg. Baseline + Plan-/Kandidaten-/Reihenfolge-Outputs + Ausführungs-Entscheidungen; `generated.at`/`verified[].at` als benannte Ausnahme, A0-20 — einzig zulässige Differenz zwischen zwei Runs), (2) **Zwei-Run-Bestätigungs-Mechanik** (agent-Instruktions-basiert, D-3/Q-6: **zwei frische Agent-Kontexte in getrennt aufgebauten (sauberen) Worktrees** führen die Instruktion jeweils einmal über demselben committeten Git-State aus und die Bundle-States werden verglichen — **eine zweite Ausführung in derselben Session genügt nicht** (Q-6, A0-19); Vergleichs-Operandum = committeter Baum, **kanonisches Eingabemanifest** (Baseline-Commit, geordnete Sources, output-sichtbare Run-/Zeit-/Identitätswerte) + **Run-Receipt außerhalb des Bundles** (Candidates, Reihenfolge, Plan, Entscheidungen, Output-Hashes), re-executierbare diff-/hashbasierte Formeln, keine Wanduhr-Steuerung, keine hart codierten erwarteten Pläne/Concept-Bodies, keine pauschale `verified`-Maskierung; identische Plan-/Kandidaten-/Reihenfolge-Outputs, byte-identische mutierte Bestandteile; keine eigene Runtime/kein neues Werkzeug, AD-6/AD-11), (3) **Ausnahme-Menge** (allein der `generated.at`-Wanduhr-Gap; dokumentiert, kein stiller Ausschluss; alle übrigen Bestandteile byte-identisch), (4) **Abweichungs-Klassifikation** (jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsfehler, kein Rauschen; textuell benannt NFR-4, Run korrigiert/rollt zurück, Zustands-Restaurations-Invariante §5.13 Pkt. 3), (5) **Normalisierungs-/Match-/Orphan-Schließung** (§3.2-Pkt.-1b-Em-Dash-Kollaps-Klasse `[-–— _]`, Kollaps-Reichweite, Match-Scope Stufe a; §5.10-Pkt.-8-Reconcile-Orphan-Regel; Erhebungen vollständig pinbar). **§3.2:** die bekannten Determinismus-Lücken als append-only-Regel-Ergänzungen geschlossen (Em-Dash in der Kollaps-Klasse, Kollaps-Reichweite, Match-Scope der Stufe a) — bestehender §3.2-Wortlaut unverändert. **§5.10 Pkt. 8:** Orphan-Politik zur deterministischen **Reconcile-Orphan-Regel** präzisiert (unzugeordnet, datumsgruppierter `log.md`-Verwaist-Eintrag mit ``, kein Banner/keine stille Vorbearbeitung/keine eigenständige Anlage; AD-16-Default). **§5.13 Pkt. 7:** Home-Verweis des `generated.at`-Gaps auf die §5.14-Definition verlagert (Wortlaut des §5.13-Satzbaus semantisch unverändert; die Behandlung des Gaps ist jetzt in §5.14-Pkt.-3 definiert). **§7:** Determinismus-Vorbehalt der Relevanzbestimmung **aufgelöst** (Em-Dash-Lücke → „in §5.14 verankert (Story 3.8)"-Ergänzung; §3.2-/§5.10-/`at`-Gap-Schließung benannt; bestehende Story-Bullets unverändert — kein neuer Bullet ersetzt einen bestehenden). **§8:** Normreferenz **AD-17h** von reiner Story-Zuordnung auf **§5.14-Anker** angehoben (`AD-17h (Determinismus, §5.14 — Zwei-Run-Bestätigung mit benannter `generated.at`-Wanduhr-Gap-Ausnahme, A0-20/§5.9-Pkt.-2/§5.10-Pkt.-8)`). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`adapters/`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; das `generated.at`-Verhalten selbst **unverändert** (nur seine Behandlung im Bundle-State-Vergleich; Verhaltenswechsel = Ask-First); keine Workflow-Engine/kein neuer Prozess/Server/MCP (AD-6, AD-11); §0-Phasen-Listentext, §5.13-Phasen-Disziplin und §6/§6.5 textuell **unverändert** (Fugen-Identität); Commit-Boundary-Regel unverändert; `schema/canonical-terms.md` unverändert (Em-Dash-/Kollaps-Schließung lebt als Regel in §3.2, nicht als Registry-Edits). **`sprint-status.yaml`:** Key `3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato` **`done`** (2026-08-20: Story-3.8-Instruktionsverankerung abgeschlossen — die Re-Open-/Abschluss-Reihenfolge „3.9 → 3.10/3.11 → 3.12 → 3.8-Abschluss → 3.13" des Sprint-Change-Proposals 2026-08-20 betrifft das Epic-3-**Abnahmegate**, nicht die Story-3.8-Inhaltsverankerung; die 3.9–3.12-Verankerungen bleiben offen, epic-3 bleibt in-progress). **Re-Open-Delta (2026-08-20, genehmigtes Sprint-Change-Proposal):** Zwei-Run-Mechanik auf getrennte Worktrees/frische Agent-Kontexte umgestellt (Q-6/A0-19), kanonisches Eingabemanifest + Run-Receipt außerhalb des Bundles, keine hart codierten erwarteten Pläne/Concept-Bodies, keine pauschale `verified`-Maskierung (nur `at`-Ausnahme). Sandbox-Nachweis (**DET-1..DET-8 + Erhaltungs-Invariante, Exit 0**; Zwei-Run-Identität nicht-vakuum, DET-2 in zwei getrennten Worktrees) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.8, Revision 3.3). -- **Revision 3.4 (2026-08-20, Story 3.9):** Neue Sektion **§5.15 „Deterministische Relevanz- & Reconcile-Routing (Story 3.9)"** eingefügt (nach §5.14, vor §6) — die **geschlossene Routing-Ebene des Reconcile** (AD-17h/A0-19, Story 3.9; §5.14-Pkt.-5-/§7-„Story-3.9-ACs noch nicht verankert"-Vorbehalt aufgelöst): (1) **Termgewinnung** — geschlossener, geordneter Algorithmus aus der Zuwachs-Sicht (Dateiname→Term-Mapping, `source.md`-Sidecar-Exklusion, Status-Codes `A`/`M`/`D` via `--diff-filter=ACMR` mit Status-Bedeutung A/C/R, §5.9-Pkt.-6-Diskrepanz-/Fallback-Kopplung, Diff↔Manifest-Äquivalenz, Mehrfach-Term-Vereinigung D-8 aufgegriffen; **eine** Kollaps-Definition §3.2-Pkt.-1b), (2) **symmetrische Normalisierung + literal-sichere Suche** (AC-2; `index.md`-Treffer → Traversal-Stufe, kein Konzept-Kandidat), (3) **eine exklusive Routing-Tabelle** (AC-3; UPDATE → CREATE → ORPHAN/HOLD → NO_OP; **NO_OP als Update-Unter-Entscheidung** — leere Candidate-Liste bei neuer Evidenz → Zellen 2/3, nie NO_OP; CREATE-vs-ORPHAN-Prädikat; Stufe-b-Zelle konsistent), (4) **Raw-Immutability-Guard** (AC-4; M/D/**R** → Run-FAIL vor jeder Mutation, A/C akzeptiert), (5) **reservierte Zielpfade** (AC-5; erschöpfende Liste `index`/`log`/`source`/`README`, Ist-Dateimenge via `git ls-tree`-Schnittmenge, deterministischer Hold), (6) **Zwei-Run-Identität** (AC-6; positive UND negative Fixtures, Zwei-Worktree-Vergleich, praktisch ausgeübte `at`-Exzeption — die einzige benannte Differenz). **§3.2:** der NO_MATCH-Anker (Pkt. 3d) als §5.15-Verweis-Anker nachgeführt (leere Candidate-Liste bei neuer Evidenz → Zellen 2/3, nicht NO_OP; `UNTOUCHED_CONCEPT` gilt für den Ghost-Diff-negativen Fall ohne Zuwachs). **§5.14 Pkt. 5:** Scope-Präzisierung nachgeführt (Story-3.9-ACs sind verankert). **§7:** Relevanzbestimmung-Bullet um die §5.15-Verankerung erweitert (Story-3.9-Vorbehalt aufgelöst; bestehende Story-Bullets unverändert). **§8:** AD-17h-/A0-19-/A0-18-Reihe bereits in §5.14 verankert — §5.15 referenziert sie (Fugen-Identität). **Abschlussklausel (Story 3.9):** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`adapters/`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **keine fünfte Routing-/Update-Klasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; `schema/canonical-terms.md` unverändert (kein neuer Normalisierungs-Operand — Umlaut-vs-Transkription bleibt benannter Defer); Commit-Boundary-Regel unverändert; Hold-Ausbau (post-Reconcile-Orphan, Mehrziel) bleibt Story 3.10; AD-16-Klassifikation/semantische Auflösung bleibt Epic 4. **`sprint-status.yaml`:** Key `3-9-deterministische-relevanz-und-reconcile-routing-schliessen` **`in-progress`** (finaler `review`/`done`-Flip im Step-04/05). **Review-Loop-2-Auflösung (Re-Ableitung):** Kern-Defekt BS-L2-1 (NO_MATCH/leere Candidate-Liste nicht NO_OP, sondern Zellen 2/3) und BS-L2-2 (Status-Codes inkl. R/C) behoben; Sandbox um R-1-Negativ-Manifest, R-3-Fall-2-Evidenzvergleich, R-5-CREATE-Bewertungsraum, R-6/R-7-Slug-Ableitung + `git ls-tree`-Schnittmenge, R-8-Zwei-Worktree-Hold, R-9-`at`-Exzeption, harmonisierte `norm()` (eine Kollaps-Definition) ertüchtigt (Details siehe Spec Change Log Loop-2 und `wiki/log.md`-Eintrag). +- **Revision 3.4 (2026-08-20, Story 3.9):** Neue Sektion **§5.15 „Deterministische Relevanz- & Reconcile-Routing (Story 3.9)"** eingefügt (nach §5.14, vor §6) — die **geschlossene Routing-Ebene des Reconcile** (AD-17h/A0-19, Story 3.9; §5.14-Pkt.-5-/§7-„Story-3.9-ACs noch nicht verankert"-Vorbehalt aufgelöst): (1) **Termgewinnung** — geschlossener, geordneter Algorithmus aus der Zuwachs-Sicht (Dateiname→Term-Mapping, `source.md`-Sidecar-Exklusion, Status-Codes `A`/`M`/`D`/`R`/`C` via `--diff-filter=ACMR` mit der Akzeptanz-Regel A/C (akzeptiert) vs. M/D/R (Run-FAIL) aus Pkt. 4, §5.9-Pkt.-6-Diskrepanz-/Fallback-Kopplung, Diff↔Manifest-Äquivalenz, Mehrfach-Term-Vereinigung D-8 aufgegriffen; **eine** Kollaps-Definition §3.2-Pkt.-1b), (2) **symmetrische Normalisierung + literal-sichere Suche** (AC-2; `index.md`-Treffer → Traversal-Stufe, kein Konzept-Kandidat), (3) **eine exklusive Routing-Tabelle** (AC-3; UPDATE → CREATE → ORPHAN/HOLD → NO_OP; **NO_OP als Update-Unter-Entscheidung** — leere Candidate-Liste bei neuer Evidenz → Zellen 2/3, nie NO_OP; CREATE-vs-ORPHAN-Prädikat; Stufe-b-Zelle konsistent), (4) **Raw-Immutability-Guard** (AC-4; M/D/**R** → Run-FAIL vor jeder Mutation, A/C akzeptiert), (5) **reservierte Zielpfade** (AC-5; erschöpfende Liste `index`/`log`/`source`/`README`, Ist-Dateimenge via `git ls-tree`-Schnittmenge, deterministischer Hold), (6) **Zwei-Run-Identität** (AC-6; positive UND negative Fixtures, Zwei-Worktree-Vergleich, praktisch ausgeübte `at`-Exzeption — die einzige benannte Differenz). **§3.2:** der NO_MATCH-Anker (Pkt. 3d) als §5.15-Verweis-Anker nachgeführt (leere Candidate-Liste bei neuer Evidenz → Zellen 2/3, nicht NO_OP; `UNTOUCHED_CONCEPT` gilt für den Ghost-Diff-negativen Fall ohne Zuwachs). **§5.14 Pkt. 5:** Scope-Präzisierung nachgeführt (Story-3.9-ACs sind verankert). **§7:** Relevanzbestimmung-Bullet um die §5.15-Verankerung erweitert (Story-3.9-Vorbehalt aufgelöst; bestehende Story-Bullets unverändert). **§8:** AD-17h-/A0-19-/A0-18-Reihe bereits in §5.14 verankert — §5.15 referenziert sie (Fugen-Identität). **Abschlussklausel (Story 3.9):** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`adapters/`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **keine fünfte Routing-/Update-Klasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; `schema/canonical-terms.md` unverändert (kein neuer Normalisierungs-Operand — Umlaut-vs-Transkription bleibt benannter Defer); Commit-Boundary-Regel unverändert; Hold-Ausbau (post-Reconcile-Orphan, Mehrziel) bleibt Story 3.10; AD-16-Klassifikation/semantische Auflösung bleibt Epic 4. **`sprint-status.yaml`:** Key `3-9-deterministische-relevanz-und-reconcile-routing-schliessen` auf **`review`** geflippt (Implementierungs-Commit; finaler `done`-Flip im Step-05-Status-Sync nach abgeschlossenem Review-Loop — Präzedenz Revision 3.2/3.3). **Review-Loop-2-Auflösung (Re-Ableitung):** Kern-Defekt BS-L2-1 (NO_MATCH/leere Candidate-Liste nicht NO_OP, sondern Zellen 2/3) und BS-L2-2 (Status-Codes inkl. R/C) behoben; Sandbox um R-1-Negativ-Manifest, R-3-Fall-2-Evidenzvergleich, R-5-CREATE-Bewertungsraum, R-6/R-7-Slug-Ableitung + `git ls-tree`-Schnittmenge, R-8-Zwei-Worktree-Hold, R-9-`at`-Exzeption, harmonisierte `norm()` (eine Kollaps-Definition) ertüchtigt (Details siehe Spec Change Log Loop-2 und `wiki/log.md`-Eintrag). diff --git a/wiki/log.md b/wiki/log.md index acaaf29..bfb0dd8 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -1,5 +1,7 @@ # Log +## 2026-08-21 +- **Story 3.9 → Review-Loop-3-Abschluss + `done` (Deterministische Relevanz- & Reconcile-Routing, bmad-code-review Re-Run 4 Layer — blind-hunter ×2 / edge-case-hunter / acceptance-auditor, verification-gap über Retry, 2026-08-21):** Re-Review der Loop-2-zustands Implementation (Diffform `2f079ee..73f2c9e`, 7 Dateien); **Triage:** 3 decision-needed / 15 patch (1 nach Re-Verifikation verworfen → **14 anzuwenden**) / 2 defer / 9 verworfen; **keine intent_gap/bad_spec (kein Loopback).** **Nutzer-Entscheidungen (empfohlene Optionen):** D-3.9-1 §8-Revision-3.3-Nachführung **behalten** (3.8 ist faktisch done; Dokumentation über den append-only Spec-Change-Log, kein Rückbau); D-3.9-2 Status-Kontraktion aufgelöst auf die 3.7/3.8-Präzedenz: maßgeblich `review` (Impl-Commit-Flip), Spec-Frontmatter-`done` zurückgesetzt, **finaler `done`-Flip in diesem Eintrag (Step-05-Status-Sync)**; D-3.9-3 **Minimal-Härtung** der tautologischen Sandbox-Szenarien (echte Mechanik statt Selbstvergleich; strukturelle Restlimits — volle Stufen-b/c-Traversal, Zwei-Run über realen Contents — bleiben benannte Defers, Home Story 3.13, Präzedenz DET-1/2). **Patches (14):** compiler.md §5.15 Pkt. 1 (`/dev/null`-Sort → **LC_ALL=C-bytetreue Stable-Sortierung**; Status-Code-Entflechtung Befund A/M/D/R/C ↔ Akzeptanz A/C vs. Run-FAIL M/D/R über Pkt. 4), Pkt. 2 (Actor-Body → **Concept-Body**), Pkt. 5 (hängender „§3.2-Endergebnis"-Verweis → **§5.8-Instruktions-Hold** als Run-Status-Definition), Revision 3.4 (Status-Wortlaut `review` + Status-Code-Korrektur); Sandbox: **README-Reserviertheits-Fall case-insensitiv** (P-3.9-2), R-9-Bundle-Grep-**Anchorung** `(^|/)(index|log)\.md$` + **Benennung** der index/log-Ausnahme (P-3.9-7, re-scoped: Enden-Präfixe wie `myindex.md` nicht mehr still ausgeschlossen), R-8-Hold-**Assert auf beide Worktrees** (P-3.9-8), **Worktree-Re-Run-Idempotenz** unter `$ROOT` (P-3.9-14), `local rc=$?`-**Dead-Diagnostics** → lebendige `rc`-Erfassung (P-3.9-15), Term-Quoting + `sort -u`; **D-3.9-3-Härtung:** R-1 Negativ-Manifest → **echter Run-FAIL-Sub-Run** vor jeder Mutation (Mutation-Sentinel bleibt abseits), R-1b/R-4 **saubere je-Status-Sub-Runs** (M/D/R-Run-FAIL ohne kumulative-Baseline-Kontraktion; A-Sub-Run Guard-Pass mit Sentinel), R-3-Fall-2 **echter Full-Containment-Check** (Token-weise, mit Negativ-Kontrolle) + **Stufe-b via echtem index.md-Link-Parsing** und Zielpfad-Existenz, R-5 **echte `route()`-Funktion** (leere Candidate-Liste → CREATE-Bewertungsraum, **nie NO_OP** — NO_OP-Regression negativ geprüft) + D-8-Vereinigung ausgeübt, R-6/R-7 **Slug rein aus dem committeten Dateinamen** (Datums-Suffix-Strip + Norm, kein nicht-instruktioneller `-doc$`-Strip). **Verworfen (1):** P-3.9-1 „Escaper defekt" = **Fehlalarm** — die „Ausführung"-Evidenz war durch die Bash-Tool-Transport-Schicht korrumpiert (Backslash-Ebene halbiert; getesteter Code ≠ Datei-Code); `od`-Beweis (Z. 74/790: `\\&`) + Negativ-/Positiv-Kontrolle aus Datei-Bytes (`v1.0` matcht `v10`-Body **nicht**, `v1.0`-Body **ja**) → Escaper **literal-sicher**, Z. 74/790 unverändert (Korrektur im Spec-Change-Log). **Defers (2, → deferred-work.md append-only):** W-3.9-1 `norm()`/`tr` ohne `LC_ALL`-Pinning (Home: Sandbox-Härtung, 952-Zeilen-Scope); W-3.9-2 positive Zwei-Run-Identität strukturell trivial (Home: Story-3.13-Abnahme, Präzedenz DET-1/2). **Frozen-Änderungen (im Spec-Change-Log autorisiert/dokumentiert):** I/O-Matrix-`RAW_GUARD_MODIFIZIERT`-Zelle um Rename (M/D/**R**) + ``-Schließtag-Nachtrag (Präzedenz Spec-3-7/3-8); **keine AC-/Intent-Wortlaut-Änderung, keine Neu-Verhandlung nötig.** **Sandbox re-executiert:** `bash _bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh` → **R-1..R-9 harte PASS, Exit 0** (inkl. R-1b/R-4 je-Status-Guard-Sub-Runs, R-3 Full-Containment + Stufe-b-Link-Parsing, R-5 route(), R-6/R-7 echte Slug-Ableitung, R-8 beide Worktrees, R-9 ankerter Bundle-Vergleich). **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-9-…` → **`done`** (finaler Step-05-Flip nach konvergiertem Loop-3), `last_updated` → 08-21-2026. **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; Umlaut-vs-Transkription bleibt benannter Defer; Hold-Ausbau (post-Reconcile-Orphan, Mehrziel) + Stufen-b/c-Traversal-Coverage bleiben Story 3.10/3.13. ## 2026-08-20 - **Story 3.9 → Re-Ableitung Loop-2 + `in-progress` (Deterministische Relevanz- & Reconcile-Routing, 2026-08-20):** Loop-2-bad_spec-Loopback (bmad-code-review Re-Run — 2 bad_spec, 4 patch, 2 defer; kein intent_gap, kein Eingriff ins ``). **Kern-Defekte behoben:** **BS-L2-1** — NO_MATCH/leere Candidate-Liste ist bei **neuer Einheit** NICHT NO_OP, sondern **CREATE (Zelle 2) / ORPHAN-HOLD (Zelle 3)**; NO_OP (Zelle 4) bleibt ausschließlich **Unter-Entscheidung des Update-Pfads** (bestehende Evidenz identisch repräsentiert) — §5.15-Pkt.-3-Tabelle, §3.2-Pkt.-3d-Verweis-Anker und Sandbox R-3/R-5 in Einklang; **BS-L2-2** — Status-Codes geschlossen: **R (Rename)** einer committeten `raw/`-Datei = AD-3-Verletzung → **Guard-FAIL**, **C** zählt als **A** (Copy/neu), `--diff-filter=ACMR` dokumentiert die Zuordnung. **Weitere Loop-2-Auflagen:** P-L2-1 Sandbox-`norm()` auf die **eine** Kollaps-Definition (§3.2-Pkt.-1b) vereinheitlicht (keine vierte Pipeline); P-L2-2 R-2-`index.md`-Traversal **real** gefeuert (Fixture enthält den Term, Traversal-Exklusion negativ assertiert); P-L2-3 R-1-Diff↔Manifest-Äquivalenz um **Negativ-Zweig** ergänzt (divergentes Manifest → Run-FAIL vor Mutation); P-L2-4 R-8-**Zwei-Run-Hold über zwei getrennte Worktrees** (Behebung der Doppel-Read-Tautologie); P-L2-5 R-6/R-7-**Slug aus dem abgeleiteten Kandidaten** + `git ls-tree`-Schnittmengen-Fixture + entdoppelte tote `CAND`-Assertion (kein `|| true`); P-L2-6 R-9-Receipts um **`generated.at`-Feld + praktisch ausgeübte at-Exzeption** (legitime at-Abweichung ≠ AD-16; andere als-at-Differenz = HARD-FAIL). **Defer-Aufgriffe (Home Story 3.9):** D-8 (Mehrfach-Term-Vereinigung) und D-9 (Term-Ableitung aus dem `raw/`-Zuwachs + voller Kandidaten-Scan statt fester 2-Datei-Liste) in §5.15 Pkt. 1 / Sandbox R-1/R-5 realisiert. **Verankerung (`schema/compiler.md` Revision 3.4):** neue **§5.15** „Deterministische Relevanz- & Reconcile-Routing (Story 3.9)" (Pkt. 1–6) — Pkt. 1 **Termgewinnung** (geschlossen, geordnet, AC-1: Dateiname→Term-Mapping, Sidecar-Exklusion, Status-Codes A/M/D/R/C via `--diff-filter=ACMR`, §5.9-Pkt.-6-Diskrepanz-/Fallback-Kopplung, Diff↔Manifest-Äquivalenz, D-8-Mehrfach-Term-Vereinigung mit ≥2-Zeichen-Segmenten, Einzelzeichen-Segmente kein eigener Term, optional committetes Term-Manifest), Pkt. 2 **symmetrische Normalisierung + literal-sichere Suche** (AC-2, §3.2-Pkt.-1b-Kollaps-Klasse `[-–— _]`→`-`; `index.md`-Treffer → Traversal-Stufe, kein Concept-Kandidat), Pkt. 3 die **eine exklusive Routing-Tabelle** (AC-3: UPDATE → CREATE → ORPHAN/HOLD → NO_OP; NO_OP als Update-Unter-Entscheidung; Leere-Candidate-Absatz; Stufe-b-Zelle), Pkt. 4 **Raw-Immutability-Guard** (AC-4: M/D/**R** → Run-FAIL vor Mutation, A/C akzeptiert), Pkt. 5 **reservierte Zielpfade** (AC-5: `index`/`log`/`source`/`README` erschöpfend, deterministischer Hold, Ist-Dateimenge via `git ls-tree`-Schnittmenge), Pkt. 6 **Zwei-Run-Identität** (AC-6: positive UND negative Fixtures, Zwei-Worktree-Vergleich, praktisch ausgeübte `at`-Exzeption, kein hart codierter Erwartungswert). **§3.2**-Pkt.-3d-§5.15-**Verweis-Anker** nachgeführt; **§5.14**-Pkt.-5-Scope-Präzisierung (Story-3.9-ACs verankert); **§7**-Relevanzbestimmung-Bullet erweitert (Vorbehalt aufgelöst); Revisionslog **Revision 3.4**. **Re-Derivation:** `deferred-work.md` D-8/D-9 → **`aufgegriffen`**; `epic-3-context.md` Z. 39 → **Ist (§5.15 verankert)**; `sprint-status.yaml` Key `3-9-…` → **`in-progress`** (finaler `review`/`done`-Flip im Step-04/05). **Sandbox-Nachweis:** `bash _bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh` → **R-1..R-9 harte PASS, Exit 0** (inkl. R-1b R/C-Guard, R-5-CREATE-Bewertungsraum, R-8-Zwei-Worktree-Hold, R-9-at-Exzeption + Negativkontrolle). **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; Umlaut-vs-Transkription bleibt benannter Defer; Hold-Ausbau (post-Reconcile-Orphan, Mehrziel) bleibt Story 3.10. - **Story 3.8 → Review-Loop-3-Abschluss + `done` (Determinismus-Vertrag, bmad-code-review Re-Run 4 Layer — blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor, 2026-08-20):** Re-Review der Loop-2-zustands Implementation; **Triage:** 4 decision-needed / 13 patch / 6 defer / 7 dismissed; **keine intent_gap/bad_spec (kein Loopback).** **Nutzer-Entscheidungen D-1..D-4 = 1/1/1/1** (empfohlene Optionen): D-1 §5.14-Schließungs-Bullet macht den Z. 53 „Bekannte Determinismus-Lücke"-Bullet **superseded** (append-only-Superseded-Hinweis, kein Umbruch); D-2 Story-Status-Flip auf **`done`** nach Loop-3-Abschluss (Change-Proposal-Sequenzierung „3.9 → 3.10 / 3.11 → 3.12 → 3.8 Abschluss → 3.13 Abnahme" betrifft das **Epic-3-Abnahmegate (3.13)** — Story-3.8-Instruktionsverankerung abgeschlossen, 3.9–3.12-Verankerungen + 3.13-Abnahme bleiben offen, epic-3 in-progress); D-3 **autorisierte Neu-Verhandlung** des Frozen-Intents in geringfügigem Umfang: Typo „documenthuman" → „document-human" (spec Z. 24, dokumentiert); D-4 Hold-Home = **Story 3.10 (Epic 3)**, Korrektur-/Erweiterungs-**Klassifikation** = Epic-4-Interface/Story 4.1 (§5.10 Pkt. 8 präzisiert). **Patches (13):** P-1 SRO-/Code-Map-Anker auf IST-Zeilen (compiler.md 375/376/377/477; Code Map §5.14 365–377, §7 424ff, §8 445, Rev 3.3 477; Sandbox 245/265/442/275/447/531); P-2/P-4 symmetrische Term-/Body-Normalisierung (identisch normalisiert vor Vergleich, kein host-abhängiges `-i`, Wortgrenze = Tool-Wortdefinition rg `-w`/`\b`) — §3.2 Pkt. 2a + `match_stufe_a` (Term + Body identisch normalisiert, `-i` entfernt, ASCII-Boundary `[^A-Za-z0-9]`); P-3 re-executierbare Zwei-Run-Formel als Receipt-Hashvergleich (git-diff-Formel = Austauschbar-Form derselben Eigenschaft) — §5.14 Pkt. 2; P-5 Sandbox-Fresh-Kontext-Restlücke textuell benannt (mechanisch simuliert; Nachweis echter frischer Kontexte = Story-3.13-Abnahmegate) — §5.14 Pkt. 2; P-6 Manifest-`generated_by` wird **konsumiert** (Guard + Konzepts-Frontmatter `by:` aus Manifest statt hart-codiert); P-7 **at-Ausnahme hart assertiert** (Wiederholungs-Schleife bei Wanduhr-Sekundenkollision erzwingt real unterschiedliche `at_cell`-Werte; `HARD-FAIL` statt stiller PASS) — re-executiert: `BEFUND: at-Ausnahme aktiv (assertiert)` mit real differierenden Wanduhrwerten; P-8 „vollständig pinbar"/„keine offene Frage" **ge scopet** (nur für die mit §5.14 geschlossenen Lücken; Umlaut-Defer + Story-3.9-ACs bewusst nicht damit geschlossen, sichtbar als benannte Defers/Geplante) — §5.14 Pkt. 5 + §7-Bullet; P-9 epics.md AC-2: „Determinismusfehler" = **AD-16-Klassifikationsdefekt** (AD-16-Kopplung nachgeführt, AC-Wortlaut erhalten); P-10 spec `review_loop_iteration: 0 → 2` (Counter ab Loop-3 gepflegt); P-11 epic-3-context.md Technical Decisions Z. 39/41/43 als **Zielzustand** markiert (Story 3.9/3.11/3.12 — noch nicht in compiler.md verankert, §3.2/§5.11 maßgeblich bis dahin); P-12 verifiziert + dokumentiert (epics.md enthält kein Literal „AD-17a..h"; die AD-17-Enums sind mit dem Change-Proposal konsistent — AD-17g liegt in Epic 4 Z. 101; kein Edit nötig); P-13 Spec Change Log Review-Loop-3 (append-only, nach Loop-2-Eintrag, vor ``). **Defers (6, → deferred-work.md append-only, Block „Deferred from: code review of spec-3-8… (2026-08-20)"):** D-5 Sandbox-Härtungs-Guards (8 Befunde), D-6 DET-1/2-Plan-Literale (Home: 3.13-Abnahme), D-7 `norm()`-Locale-Pinning, D-8 Mehrfach-Term-Union, D-9 Orphan-Term-Hand-Pin/feste 2-Datei-Scan (Home: 3.9), D-10 Umlaut-vs-Transkription im Match-Pfad (bereits „Aufgegriffen (teilweise)" offen). **Dismissed (7):** u. a. Blind-Hunter-Claim „alle Sandbox-SRO-Anker stale" — **widerlegt** (alle 6 exakt vor den eigenen Edits); übrige: Loop-1/2-Präzedenz-Doppelzählungen + by-design-Lesarten (Details im Spec Change Log). **Sandbox re-executiert:** `bash _bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh` → **DET-1..DET-8 harte PASS, Exit 0**; at-Ausnahme aktiv (assertiert, real unterschiedliche Wanduhr-`at`-Werte A vs. B); Manifest `generated_by` konsumiert; Witness nicht-vakuum (Baseline-SHA ≠ Run-SHA). **Spec-Frontmatter:** `status: 'done'`, `review_loop_iteration: 2`; Frozen-Intent nur im D-3-Umfang geändert (Typo, autorisiert). `sprint-status.yaml`-Key `3-8-…` → **`done`** (D-2; Sequenzierungs-Hinweis im Key-Kommentar), `last_updated` → 08-20-2026 17:04. **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/` unverändert).