feat: Story 3.9 (Loop-2) — Deterministische Relevanz- & Reconcile-Routing schließen, review reif

§5.15 „Deterministische Relevanz- & Reconcile-Routing (Story 3.9)" in schema/compiler.md
(Revision 3.4): geschlossene Termgewinnung (AC-1), symmetrische Normalisierung +
literal-sichere Suche (AC-2), eine exklusive Routing-Tabelle UPDATE→CREATE→ORPHAN/HOLD→NO_OP
(AC-3, NO_OP als Update-Unter-Entscheidung), Raw-Immutability-Guard M/D/R→Run-FAIL (AC-4),
reservierte Zielpfade index/log/source/README→deterministischer Hold (AC-5), Zwei-Run-Identität
nicht-vakuum inkl. praktisch ausgeübter at-Exzeption (AC-6). §3.2-Pkt.-3d-Anker, §5.14-Pkt.-5-Scope
und §7-Bullet nachgeführt.

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

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Michael Tamse
2026-08-21 06:21:08 +02:00
co-authored by Claude
parent 2f079ee3c8
commit 73f2c9e71d
7 changed files with 1176 additions and 9 deletions
@@ -550,6 +550,6 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
- **Sandbox-Härtungs-Guards (8 Befunde):** `mktemp` (Z. 37), `git init` (Z. 41), Baseline-Commit (Z. 94-96), `isolate()`-Sequenz (Z. 104-107), fehlende Match-Datei (Z. 136-141), verwaiste unregistrierte Worktree-Verzeichnisse (Z. 243-244) und Porcelain-Pfade mit Leerzeichen (`awk '{print $2}'`, Z. 217/682) tragen keine Fehler-Guards; ein Fehlgeschlagener init-Schritt lässt einen späteren „PASS" auf kaputtem Zustand drucken. — Defer-Grund: Testharness-Robustheit, keine normative Instruktion-Semantik; Re-Run-Risiko dokumentiert. Home: nächste Sandbox-Revision. — status: offen
- **DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert":** `plan=$(printf 'cand=alpha;form=update;baseline=%s')` (run-sandbox.sh:189) und `echo "form=update"` in `bs_a()` (Z. 221) sind wörtliche Skript-Konstanten; B1==B2-Identität damit trivial. — Defer-Grund: Loop-1-/Loop-2-Präzedenz (Loop-1 hat „DET-2-Plan-Literal (dev-demo, echte Content-Hashes decken)" rejected; DET-1 trägt den Demo-Kommentar Z. 211-214); die Nicht-Vakuum-Assertion tragen die echten Content-Hashes. Home: Story-3.13-Abnahme (echte Gate-Runs über die Instruktion). — status: offen
- **`norm()`-Locale-Pinning (`LC_ALL`) fehlt:** unter C/POSIX-Locale zerfällt die Byte-Klasse `[–—]` in {E2,80,93,94} (übermatcht z. B. `…`/`„`), `tr 'A-Z' 'a-z'` ist locale-abhängig → die Kanon-Form hängt am Host-Locale statt am committeten Git-State. — Defer-Grund: Testharness-Host-Locale-Abhängigkeit (Host = Ubuntu-Sandbox, dort UTF-8-Default; auf C-Locale-Hosts falsch), keine normative Semantik der Instruktion. Home: Sandbox-Härtung (gemeinsam mit den Hards-Guards). — status: offen
- **Mehrfach-Term-Vereinigung unübend:** §3.2 Pkt. 1c/§5.14 Pkt. 5 pinnen die Vereinigung der Treffer über alle Terme (besuchte Menge, Zuwachs-Sicht-Ordnung) als „vollständig pinbar" — die Sandbox übt nur den Einzel-Term-Pfad; eine Regressions in Union-/Dedup-/visited-Set-Logik würde unbemerkt durchgehen. — Defer-Grund: Story-3.8-Scope (Bestätigungs-Mechanik + Lückenschließung) ist erfüllt; ausführbare Union-Coverage gehört zur deterministischen Term-Gewinnung/Routing. Home: Story 3.9. — status: offen
- **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
- **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)
- **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
@@ -36,7 +36,7 @@ Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bu
- **Inkrementeller Datenfluss (AD-5, A0-6):** Interpret → Reconcile → Synthesize → Update affected Concepts. Startpunkt ist immer das bestehende Bundle — niemals "Regenerate Everything" (SM-1, FT-6).
- **Reason/Mutate-Trennung (AD-6, A0-7):** Logische Phasen Analyse → Reconcile → Plan Changes → Mutate → Validate. Keine eigene Workflow Engine; ein Agent kann die Phasen in einer Session durchführen, der beobachtbare Endzustand des Bundles muss auch bei Abbruch konsistent sein.
- **Deterministische Relevanz & Routing (A0-18):** Geschlossene, geordnete Term-Gewinnung bzw. explizites persistiertes Term-Manifest; Suchterm und Concept-Body werden identisch normalisiert und literal-sicher verglichen. Eine exklusive Routing-Tabelle unterscheidet `UPDATE`, `CREATE`, `ORPHAN/HOLD` und echten `NO_OP`; gleicher Git-State plus gleiches Eingabemanifest erzeugt dieselbe Candidate-Liste und Reihenfolge. *Zielzustand (Review-Loop-3, P-11): geplant für Story 3.9 (Sprint-Change-Proposal 2026-08-20) — noch nicht in `schema/compiler.md` verankert; §3.2 ist bis dahin der maßgebliche Regeltext.*
- **Deterministische Relevanz & Routing (A0-18):** Geschlossene, geordnete Term-Gewinnung bzw. explizites persistiertes Term-Manifest; Suchterm und Concept-Body werden identisch normalisiert und literal-sicher verglichen. Eine exklusive Routing-Tabelle unterscheidet `UPDATE`, `CREATE`, `ORPHAN/HOLD` und echten `NO_OP`; gleicher Git-State plus gleiches Eingabemanifest erzeugt dieselbe Candidate-Liste und Reihenfolge. *Ist (Story 3.9, 2026-08-20): in `schema/compiler.md` **§5.15** verankert (Termgewinnung geschlossen, eine exklusive Routing-Tabelle, Raw-Immutability-Guard, reservierte Zielpfade, Zwei-Run-Identität; §3.2 bleibt Erhebungs-Anker, §5.15 Pkt. 3 die Routing-Zuordnung).*
- **Keine eigene LLM-Runtime:** Der ausführende agentische Host orchestriert den AD-17-Ablauf (Lease holen, innerhalb des geleasten Bereichs mutieren, committen, freigeben); keine separaten Prozesse oder ein Server (AD-11).
- **Atomare Root-Scope-Lease (AD-17a/b, A0-12/13):** Producer behalten die Branch-Konvention `lease/<area>/<id>`, akquirieren aber genau einen atomaren, scope-bezogenen Lock im clone-geteilten Zustand. Die Run-ID ist Lock-Inhalt, nicht Exklusivitätsschlüssel; konkurrierende Producer verschiedener IDs und Worktrees teilen denselben Root-Scope (`wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien). *Zielzustand (Review-Loop-3, P-11): geplant für Story 3.11 (Sprint-Change-Proposal 2026-08-20) — die atomare Lock-Präzisierung ist noch nicht in `schema/compiler.md` §5.11 verankert; die bestehende §5.11-Verankerung (Branch-Konvention, Root-Scope-Lease) bleibt unverändert maßgeblich.*
- **Fail-closed Kollisionsgrenze (AD-17c, A0-14):** Zwei Branches mit ungleichen Änderungen am selben Concept-Pfad werden nie textuell automatisch gemerged. Epic 3 erhält beide Commit-Hashes und den Scope in einem strukturierten Hold; AD-16-Klassifikation und semantische Auflösung sind Epic 4 / Story 4.x.
@@ -0,0 +1,952 @@
#!/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)
#
# 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).
# 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)
# 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)
# R-8 HOLD_ZWEI_WORKTREE (BS-L2-4: Hold über zwei getrennte Worktrees
# statt Doppel-Read; tote CAND einzeln hart assertiert, kein || true)
# 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)
# =============================================================================
set -u
# P-1: set -e im Hauptkörper (sandbox-3-8-Härtung) — kein false-PASS.
# Achtung: R-9 nutzt Subshell-Läufe; Fehler dort werden explizit geroutet.
set -e
# --- Ort & Halluzinations-Root -------------------------------------------------
SB_NAME="sb39"
if [ -n "${1:-}" ]; then
ROOT="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
else
ROOT="$(mktemp -d "${TMPDIR:-/tmp}/${SB_NAME}.XXXXXX")"
fi
BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "Sandbox-Root: $ROOT"
echo "SMOKE: $0"
# Pfad des Skripts (für relokierte Aufrufe)
SELF="$0"
mkdir -p "$ROOT/raw" "$ROOT/wiki" "$ROOT/scratch" "$ROOT/receipts"
runlabel() { echo; echo "### $1"; }
# Eine deterministische Kollaps-Funktion — GENAU die §3.2-Pkt.-1b-Kollaps-Klasse.
# (Loop-2/P-L2-1: eine Definition, keine vierte abweichende Pipeline.)
norm() {
printf '%s' "$1" \
| sed \
-e 's/[-–—_ ]/-/g' \
-e 's/--*/-/g' \
-e 's/^-*//' \
-e 's/-*$//' \
| tr 'A-Z' 'a-z'
}
# C-clean-Kollaps (Unterschied zwischen En-/Em-Dash-Hyphen vs. underscore+space):
norm_pipe() { norm "$1"; }
# Lokale lowercase-Hilfe (deterministisch auf ASCII-Bytes, wie §3.2-Pkt.-1b).
tolower() { tr 'A-Z' 'a-z'; }
# Stufe-a-Match: Symmetrie, Ganzes-Wort, Body-only, index/log-Exklusion.
# Liefert getroffene wiki-Pfade ohne Präfix/Suffix.
match_stufe_a() { # $1=Begriff $2=Datei-Liste
local term="$1" file term_lc esc body
term_lc=$(norm "$term")
esc=$(printf '%s' "$term_lc" | sed 's|[][\.*^$+?(){}|]|\\&|g')
local out=""
for file in "${@:2}"; do
case "$(basename "$file")" in
index.md|log.md) continue;; # keine Concept-Kandidaten
esac
body=$(tr -d '\r' < "$file" | awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}')
body=$(norm "$body")
if printf '%s' "$body" | grep -Eq "(^|[^a-z0-9])${esc}([^a-z0-9]|$)"; then
[ -n "$out" ] && out="$out $(basename "$file" .md)" || out="$(basename "$file" .md)"
fi
done
printf '%s' "$out"
}
# --- Isolation: frische Kopie der Fixture-Dateien je Szenario -------------------
# Fixtures sind im Skript eingebettete Datei-Inhalte (kein realer Baum).
# isolate <name> legt ein frisches Verzeichnis an und git-init.
isolate() {
local name="$1"
local dir="$ROOT/$name"
rm -rf "$dir"
mkdir -p "$dir/raw" "$dir/wiki" "$dir/scratch"
: > "$dir/.gitkeep"
git -C "$dir" init -q
git -C "$dir" config user.email "sandbox@test"
git -C "$dir" config user.name "Sandbox"
echo "$dir"
}
# --- log_bullet: Befund-/Hold-Text (kapselt in $ROOT/log-bullets.txt; optional
# in wklog, siehe R-8-Exploit: real only unter isolierten Worktrees). --------
log_bullet() { # $1 = Zeile
printf '%s\n' "$1" >> "$ROOT/log-bullets.txt"
}
# =============================================================================
# R-1 TERMGEWINNUNG_GESCHLOSSEN (AC-1)
# =============================================================================
runlabel "R-1: TERMGEWINNUNG_GESCHLOSSEN — Zuwachs-Sicht (git diff gg. Baseline) -> deterministisch geordnete Term-Liste via Dateiname->Term-Mapping (Datums-Suffix/Endung gestripped, Trennzeichen kollabiert §3.2-Pkt.-1b); source.md-Sidecars exkludiert; Diff<->Manifest-Äquivalenz (Positiv + Negativ = FAIL)"
r1dir=$(isolate r1)
cd "$r1dir"
# Basis-Concepts (Baseline-Commit).
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: Init-Sequenz und lokale Netze (raw/alpha-v1.md#S-1/#S-2).
EOF
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
EOF
cat > wiki/log.md <<'EOF'
# Änderungen
EOF
git add -A && git commit -qm "R-1 Baseline"
BASE=$(git rev-parse HEAD)
# Zuwachs: neue Source mit Datums-Suffix + Sidecar.
cat > raw/beta-kommunikation-2026-08-20.md <<'EOF'
### S-1
Evidenz: Beta-Kommunikation als Zuwachs-Thema.
EOF
cat > raw/beta-kommunikation-2026-08-20.source.md <<'EOF'
# Source
Herkunft: Test-Evidenz (Sidecar-Konvention §1.4).
EOF
git add -A && git commit -qm "Zuwachs: beta-kommunikation + Sidecar"
ZUWACHS=$(git diff --name-only "$BASE" -- raw/ | LC_ALL=C sort)
STATUSES=$(git diff --name-status "$BASE" -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//')
echo " Zuwachs-Dateien: $ZUWACHS"
echo " Status-Codes (A/M/D/R/C): $STATUSES"
case "$STATUSES" in
*A*) ;;
*) echo "HARD-FAIL (R-1): Status-Code 'A' fehlt im Zuwachs (A erwartet)" >&2; exit 1;;
esac
case "$STATUSES" in
*M*|*D*|*R*) echo "HARD-FAIL (R-1): unerwarteter M/D/R im Zuwachs-Befund (nur A erwartet)" >&2; exit 1;;
esac
# Term-Ableitung (Dateiname->Term, Mehrfach-Term-Vereinigung D-8, Sidecar exkludiert).
TERMS=""
for f in $ZUWACHS; do
case "$f" in
*.source.md) echo " Sidecar exkludiert (kein eigener Term): $f"; continue;;
esac
base=$(basename "$f" .md)
stem=$(printf '%s' "$base" | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')
t=$(norm "$stem")
# mehrere Segmente (>=2 Zeichen) als weitere Terme (Mehrfach-Term-Vereinigung D-8)
for seg in $(printf '%s' "$stem" | tr '-' '\n'); do
case "$seg" in
''|'-'|'?'*) continue;;
*) if [ "${#seg}" -ge 2 ]; then [ -n "$seg" ] && [ "$seg" != "$t" ] && TERMS="$TERMS $(norm "$seg")"; fi;;
esac
done
[ -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/ $//')
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).
EXPECT=""
for f_ in $ZUWACHS; do
case "$f_" in *.source.md) continue;; esac
s_=$(printf '%s' "$(basename "$f_" .md)" | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')
t_=$(norm "$s_")
EXPECT="$EXPECT $t_"
for seg_ in $(printf '%s' "$s_" | tr '-' '\n'); do
case "$seg_" in
''|'-'|'?'*) continue;;
*) if [ "${#seg_}" -ge 2 ] && [ "$seg_" != "$t_" ]; then EXPECT="$EXPECT $(norm "$seg_")"; fi;;
esac
done
done
EXPECT=$(printf '%s\n' $EXPECT | LC_ALL=C sort | 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"
# Diff<->Manifest-Äquivalenz (positiv): Manifest-Terme == Zuwachs-Terme.
cat > "$ROOT/manifest-r1.yaml" <<EOF
baseline: $BASE
sources:
- raw/beta-kommunikation-2026-08-20.md
terms:
- beta
- beta-kommunikation
- kommunikation
EOF
MANIFEST_TERMS=$(awk '/^terms:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); print}' "$ROOT/manifest-r1.yaml" | tr '\n' ' ' | sed 's/ $//')
[ "$MANIFEST_TERMS" = "$TERMS" ] || { echo "HARD-FAIL (R-1): Manifest-Terme ($MANIFEST_TERMS) != Zuwachs-Terme ($TERMS) — Diff↔Manifest-Äquivalenz verletzt" >&2; exit 1; }
echo " Manifest-Abgleich: Manifest-Terme == Zuwachs-Terme ($TERMS)"
# Diff<->Manifest-Äquivalenz (NEGATIV, P-L2-3): divergentes Manifest => RUN-FAIL
# vor jeder Mutation. Wir bauen ein zweites Manifest mit abweichendem Term.
cat > "$ROOT/manifest-r1-bad.yaml" <<EOF
baseline: $BASE
sources:
- raw/beta-kommunikation-2026-08-20.md
terms:
- beta-KOMMUNIKATION
EOF
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)"
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"
# =============================================================================
# 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)"
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
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"
# =============================================================================
# R-2 NORMALISIERUNG_SYMMETRISCH (AC-2) — index.md real gefeuert
# =============================================================================
runlabel "R-2: NORMALISIERUNG_SYMMETRISCH — Symmetrie (lowercase + [-–— _] -> -), literal-sicher; index.md-Treffer => Traversal-Stufe, real gefeuert (P-L2-2)"
r2dir=$(isolate r2)
cd "$r2dir"
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
---
Wissen Relevanz (En-Dash), Wissen — Relevanz (Em-Dash), Wissen_Relevanz (Unterstrich) und
Wissen Relevanz (Leerzeichen) bezeichnen dasselbe Konzept hier im Body.
EOF
cat > wiki/index.md <<'EOF'
# Index
Wissen-Relevanz ist ein Traversal-Stufen-Begriff (body word).
EOF
cat > wiki/gamma.md <<'EOF'
---
type: concept
sources:
- resource: raw/gamma-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Gamma: gänzlich anderes Thema ohne Relevanz-Begriff.
EOF
git add -A && git commit -qm "R-2 Baum"
[ "$(norm "Wissen Relevanz")" = "wissen-relevanz" ] || { echo "HARD-FAIL (R-2): norm(En-Dash)" >&2; exit 1; }
[ "$(norm "Wissen — Relevanz")" = "wissen-relevanz" ] || { echo "HARD-FAIL (R-2): norm(Em-Dash)" >&2; exit 1; }
[ "$(norm "Wissen_relevanz")" = "wissen-relevanz" ] || { echo "HARD-FAIL (R-2): norm(Unterstrich)" >&2; exit 1; }
[ "$(norm "Wissen Relevanz")" = "wissen-relevanz" ] || { echo "HARD-FAIL (R-2): norm(Leerzeichen)" >&2; exit 1; }
# Symmetrie: Der Ganzes-Wort-Match wirkt auf beide Seiten identisch normalisiert.
# Der Stufe-a-Sweep über wiki/ muss alpha UND index.md „sehen" — index.md ist aber
# strukturell exkludiert (nur Traversal-Stufe). Fixture enthält den Term real.
# Wir prüfen: alpha ist getroffen, gamma nicht; index.md-Treffer lösen Traversal b aus.
HIT_A=$(match_stufe_a "wissen-relevanz" wiki/alpha.md)
[ "$HIT_A" = "alpha" ] || { echo "HARD-FAIL (R-2): Stufe-a alpha '$HIT_A' (Soll alpha)" >&2; exit 1; }
# index.md-Treffer real feuern: Fixture-Body enthält das Wort. Der Sweep (Stufe a)
# würde index.md als Treffer melden — aber die Exklusion darf es NICHT als Kandidat liefern.
# (match_stufe_a exkludiert index.md strukturell; wir belegen zusätzlich, dass der
# exkludierte Treffer dennoch die Traversal-Relevanz besitzt.)
HIT_I=$(grep -l "Wissen-Relevanz\|wissen-relevanz" wiki/index.md >/dev/null 2>&1 && echo index || true)
echo " index.md-Body enthält den Begriff (Fixture real): ${HIT_I:-nein}"
CAND_INDEX=$(match_stufe_a "wissen-relevanz" wiki/index.md)
[ -z "$CAND_INDEX" ] || { echo "HARD-FAIL (R-2): index.md ist als Candidate gelistet ('$CAND_INDEX') — §5.15 Pkt. 2 Traversal-Exklusion" >&2; exit 1; }
# Traversal-B: Ein Stufe-a-Treffer auf index.md (hier: body word) löst Stufe b aus
# (TRAVERSAL_REACH_ONLY) — die darunter gewurzelten Concepts sind betroffen, nicht index.md selbst.
[ "$HIT_I" = "index" ] || { echo "HARD-FAIL (R-2): Traversal-Trigger-Bedingung nicht real (Fixture ohne Wort)" >&2; exit 1; }
echo " BEFUND: index.md-Treffer lösen Traversal-Stufe (TRAVERSAL_REACH_ONLY); index.md selbst bleibt Nicht-Kandidat — Traversal-Exklusion real ausgeübt"
echo "RESULT: PASS — R-2: NORMALISIERUNG_SYMMETRISCH — Symmetrie + Kollaps-Klasse §3.2-Pkt.-1b; literal-sicherer Ganzes-Wort-Vergleich; index.md-Traversal-Stufe real gefeuert (AC-2)"
# =============================================================================
# R-3 ROUTING_TABELLE_EXKLUSIV (AC-3)
# =============================================================================
runlabel "R-3: ROUTING_TABELLE_EXKLUSIV — genau eine Zelle je Fall (UPDATE→CREATE→ORPHAN/HOLD→NO_OP); NO_OP als Update-Unter-Entscheidung mit ECHTEM Evidenzvergleich; NO_MATCH neuer Einheit => CREATE, nicht NO_OP; Stufe-b-Zelle"
r3dir=$(isolate r3)
cd "$r3dir"
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
EOF
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
EOF
git add -A && git commit -qm "R-3 Basis"
# --- Fall 1: UPDATE (bestehender Match) ---
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Zuwachs alpha-v2 (kopplung)"
# Termgewinnung aus dem Zuwachs-Dateinamen (alpha-v2.md -> alpha); UPDATE erfordert
# einen Treffer des Zuwachs-Terms gegen den bestehenden Concept-Body.
HIT=$(match_stufe_a "alpha" wiki/alpha.md)
[ "$HIT" = "alpha" ] || { echo "HARD-FAIL (R-3): UPDATE-Kandidat 'alpha' -> '$HIT' (Soll alpha)" >&2; exit 1; }
echo " Fall 1: Zuwachs-Term 'alpha' trifft alpha.md -> UPDATE (Zelle 1)"
# --- Fall 2: NO_OP via ECHTEM Evidenzvergleich (P-L2-5) ---
# Neue Evidenz claimt denselben Beleg wie der bestehende Body -> der Vergleich
# der tatsächlichen Inhalte (Datei vs. Body) ergibt: kein neuer Beleg => NO_OP.
cat > raw/alpha-identisch.md <<'EOF'
### S-1
deterministische init-sequenz
EOF
git add -A && git commit -qm "Zuwachs alpha-identisch (deckungsgleich)"
# Deterministisches Kriterium: neue Evidenz angewandt auf den bestehenden Body
# ergibt keinen inhaltlichen Neu-Beleg (Byte-Vergleich der inhaltlichen Aussage).
new_content=$(tr -d '\r' < raw/alpha-identisch.md | awk 'BEGIN{n=0}/^###/{n++;next} n>=1{print}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr '\n' ' ')
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)"
else
echo "HARD-FAIL (R-3): NO_OP-Kriterium nicht auf echte Evidenz gegründet (Kein-New-Beleg-Vergleich inkonsistent)" >&2; exit 1
fi
# --- Fall 3: CREATE (neue eigenständige Einheit, leerer Match) ---
cat > raw/delta-protokoll.md <<'EOF'
### S-1
Evidenz: Delta-Protokoll als gänzlich neues, eigenständiges Thema ohne Concept-Treffer.
EOF
git add -A && git commit -qm "Zuwachs delta-protokoll"
HIT3=$(match_stufe_a "delta" wiki/*.md)
[ -z "$HIT3" ] || { echo "HARD-FAIL (R-3): CREATE-Kandidat 'delta' trifft unerwartet '$HIT3'" >&2; exit 1; }
echo " Fall 3: keine Treffer, eigenständige neue Einheit -> CREATE (Zelle 2)"
# --- Fall 4: ORPHAN/HOLD (nicht klassifizierbar) ---
cat > raw/epsilon-ratlos.md <<'EOF'
### S-1
Evidenz: Epsilon-Ratlos — Fragment ohne klare Wissenseinheit.
EOF
git add -A && git commit -qm "Zuwachs epsilon-ratlos"
HIT4=$(match_stufe_a "epsilon" wiki/*.md)
[ -z "$HIT4" ] || { echo "HARD-FAIL (R-3): ORPHAN-Kandidat 'epsilon' trifft '$HIT4'" >&2; exit 1; }
[ -f wiki/epsilon-ratlos.md ] && { echo "HARD-FAIL (R-3): ORPHAN-HOLD hat eigenständige Anlage erzeugt (verboten, fail-closed)" >&2; exit 1; }
echo " Fall 4: kein Ziel-Pfad-Treffer, nicht klassifizierbar -> ORPHAN/HOLD (Zelle 3, fail-closed)"
# --- Stufe-b-Zelle ---
mkdir -p wiki/area
cat > wiki/area/index.md <<'EOF'
# Area Index
- [Verwaister-Concept](verwaister-concept.md)
EOF
git add -A && git commit -qm "Area-Index mit Dangling-Link"
[ -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" ] || { 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 "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"
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz Alpha.
EOF
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 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
(
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;;
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
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"
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)"
# =============================================================================
# R-5 NEUE_EINHEIT_KEIN_NOOP (Loop-2 / BS-L2-1)
# =============================================================================
runlabel "R-5: NEUE_EINHEIT_KEIN_NOOP — neue Einheit mit leerer Candidate-Liste => CREATE-Bewertungsraum (Zellen 2/3), NICHT NO_OP; kein Anlage-Zwang für Einzelzeichen-Segmente"
r5dir=$(isolate r5)
cd "$r5dir"
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
git add -A && git commit -qm "R-5 Basis"
# Zuwachs: neue, eigenständige Einheit aus einem 2-Datei-Zuwachs (zeta-a + zeta-b).
cat > raw/zeta-a-2026-08-20.md <<'EOF'
### S-1
Evidenz: Zeta-A-Subthema (eigenständige init-Sequenz).
EOF
cat > raw/zeta-b-2026-08-20.md <<'EOF'
### S-1
Evidenz: Zeta-B-Subthema (eigenständige init-Sequenz).
EOF
git add -A && git commit -qm "Zuwachs: zeta-a + zeta-b"
# D-9-Term-Ableitung AUS dem committeten Zuwachs (nicht hart notiert): der volle
# Kandidaten-Scan läuft über ALLE wiki/*.md (Glob, kein fester 2-Datei-Scan).
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; }
R5_TERMS=""
for f in $R5_ZUWACHS; do
stem=$(basename "$f" .md | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')
t=$(norm "$stem")
R5_TERMS="$R5_TERMS $t"
done
R5_TERMS=$(printf '%s\n' $R5_TERMS | LC_ALL=C sort | 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).
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/^ *//')
[ -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"
[ "$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"
# =============================================================================
# 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)"
r6dir=$(isolate r6)
cd "$r6dir"
cat > raw/index-doc.md <<'EOF'
### S-1
Evidenz: Index-Struktur des Bundles selbst.
EOF
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$//')
TARGET_SLUG=$(norm "$STEM")
echo " Abgeleiteter Ziel-Slug: '$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)";;
*) echo "HARD-FAIL (R-6): Slug '$TARGET_SLUG' nicht in Reserviert-Menge" >&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; }
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).
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.
HIT_INTERSECTION=""
for b in $LS_BASES; do
case "$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}"
case " $HIT_INTERSECTION " in
*" index "*) echo " BEFUND: Ist-Dateimenge enthält 'index' als committete Basis (Schnittmenge deterministisch)" ;;
*) echo "HARD-FAIL (R-6): Ist-Dateimenge-Schnittmenge liefert 'index' nicht (git ls-tree-Check defekt)" >&2; exit 1;;
esac
echo "RESULT: PASS — R-6: RESERVED_ZIEL_INDEX — abgeleiteter Slug 'index' => Hold, keine Anlage, keine wiki/-Mutation; Ist-Dateimenge via git ls-tree-Schnittmenge deterministisch (AC-5, §5.15 Pkt. 5)"
# =============================================================================
# R-7 RESERVED_ZIEL_LOG (AC-5)
# =============================================================================
runlabel "R-7: RESERVED_ZIEL_LOG — Slug 'log' (abgeleitet); 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
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$//')
TARGET_SLUG7=$(norm "$STEM7")
[ "$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)";;
*) 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; }
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)"
# =============================================================================
# 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)"
r8dir=$(isolate r8)
cd "$r8dir"
cat > raw/log-doc.md <<'EOF'
### S-1
Evidenz: ein Thema mit Log-Bezug (Kandidat: log-thema).
EOF
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)"
R8BASE=$(git rev-parse HEAD)
# Zwei getrennte, saubere Worktrees über demselben committeten Baum.
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; }
hold_run() { # $1 = worktree-Pfad ; $2 = Run-Name
local wt="$1" name="$2"
(
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)
# Hold: keine Datei schreiben, textuell benannt (NFR-4).
befund="RESERVED_ZIEL_$slug"
;;
*) befund="OK";;
esac
mkdir -p "$ROOT/receipts"
{
echo "run: $name"
echo "slug: $slug"
echo "befund: $befund"
# tote CAND: Candidate wird bestimmt, aber nicht geschrieben.
echo "cand: log-thema"
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; }
}
# 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
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; }
if [ "$HA" = "$HB" ]; then
echo " BEFUND: Hold-Befund über zwei getrennte Worktrees identisch (Zwei-Run-Hold-Identität, NICHT-VAKUUM):"
echo "$HA" | sed 's/^/ /'
else
echo "HARD-FAIL (R-8): Hold-Befund über zwei getrennte Worktrees verschieden (Zwei-Run-Hold-Identität verletzt):" >&2
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; }
# 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"
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)"
# =============================================================================
# R-9 ZWEI_RUN_NEGATIV (AC-6) — + at-Exzeption, Norm-Pin
# =============================================================================
runlabel "R-9: ZWEI_RUN_IDENTISCH_NEGATIV — positive Identität NICHT-VAKUUM (2 getrennte Worktrees, echte Content-Hashes) + negative AD-16-Abweichung hart FAIL + at-Exzeption praktisch ausgeübt + Norm-Pin (AC-6, AD-17h/A0-19)"
r9dir=$(isolate r9)
cd "$r9dir"
cat > wiki/alpha.md <<'EOF'
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
Alpha beschreibt die synchrone Kopplung seiner Dienste (raw/alpha-v1.md#S-2).
EOF
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
EOF
cat > raw/alpha-v1.md <<'EOF'
### S-1
Init-Sequenz.
### S-2
Synchrone Kopplung.
EOF
git add -A && git commit -qm "R-9 Basis (alpha traegt Kopplung)"
R9BASE=$(git rev-parse HEAD)
cat > "$ROOT/manifest.yaml" <<EOF
baseline: $R9BASE
generated_by: wow-compiler/0.1.0
sources:
- raw/alpha-v1.md
terms:
- kopplung
EOF
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; }
# Ein konstanter at-Wert für die Sandbox (Wanduhr-frei) — die at-Exzeption wird
# bewusst als legitime Abweichung modelliert, nicht gemashat.
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
(
set -e
cd "$wt" || exit 9
git checkout -qf "$R9BASE"
git clean -qfd wiki raw
git config user.email "sandbox@test" && git config user.name "Sandbox"
# Eingabe-Set aus dem Manifest: neue Source, deren Term den Alpha-Body trifft.
cat > raw/alpha-v2.md <<'EOF'
### S-3
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
EOF
git add -A && git commit -qm "Evidenz v2 (R-9)"
# Deterministische Routing-Projektion (gleiche Regeln wie Haupt-Skript).
m_term=$(awk '/^terms:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); print}' "$ROOT/manifest.yaml")
[ -n "$m_term" ] || { echo "HARD-FAIL (R-9): Manifest ohne terms" >&2; exit 9; }
TERM_NORM=$(norm "$m_term")
cand=""
esc=$(printf '%s' "$TERM_NORM" | sed 's|[][\.*^$+?(){}|]|\\&|g')
for f in wiki/*.md; do
case "$(basename "$f")" in index.md|log.md) continue;; esac
body=$(tr -d '\r' < "$f" | awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}')
body=$(norm "$body")
if printf '%s' "$body" | grep -Eq "(^|[^a-z0-9])${esc}([^a-z0-9]|$)"; then
[ -n "$cand" ] && cand="$cand $(basename "$f" .md)" || cand="$(basename "$f" .md)"
fi
done
[ -n "$cand" ] || { echo "HARD-FAIL (R-9): Candidate-Liste leer — run-Feststellung unbrauchbar" >&2; exit 9; }
routing="UPDATE"
# at: je Run ein eigenständiger (deterministischer) Wert — für die Exzeption nutzen
# wir den konstanten Sandbox-at, im atgap-Modus eine bewusste Abweichung.
at_val="$SANDBOX_AT"
if [ "$mode" = "atgap" ]; then
at_val="2026-08-20T13:00:00Z"
fi
# Normalform-Pin: der at-Wert ist ein volles ISO-8601-Datetime (§5.15 Pkt. 6, §6.5-Pkt.-2).
case "$at_val" in
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T*Z) ;;
*) echo "HARD-FAIL (R-9): at-Normalform verletzt ($at_val)" >&2; exit 9;;
esac
mkdir -p "$ROOT/receipts"
{
echo "baseline: $R9BASE"
echo "generated.at: $at_val"
echo "terms: $m_term"
echo "candidates: $cand"
echo "order: $cand"
echo "routing: $routing"
echo "at.normalform: iso8601"
if [ "$mode" = "divergent" ]; then
echo "candidates: other-candidate"
fi
} > "$ROOT/receipts/$name.receipt"
# BUNDLE-MUTATION + BYTE-NACHWEIS (F-27, §5.15 Pkt. 6/A0-19): der
# Zwei-Run-Abgleich verlangt byte-identische MUTIERTE Bundle-Bestandteile,
# nicht nur identische Receipt-Felder. Wir mutieren wiki/alpha.md zu einem
# deterministischen Stand — einziges laufabhängiges Feld ist generated.at
# (Sandbox-Konstante bzw. atgap-Abweichung) — und sichern je Datei den
# SHA-256 (voll) sowie den SHA-256 mit maskierter at-Zeile. Der divergente
# Modus (run9n) weicht BUNDLE-sichtbar ab (synchrone-Zeile), sodass die
# Negativ-Kontrolle auch auf Bundle-Ebene hart FAIL ist; der atgap-Modus
# weicht nur im generated.at-Feld ab (Exzeption).
cat > wiki/alpha.md <<EOF
---
type: concept
sources:
- resource: raw/alpha-v1.md
id: s1
- resource: raw/alpha-v2.md
id: s2
generated:
by: wow-compiler/0.1.0
at: $at_val
---
Kopplung des Alpha-Protokolls: synchrone Kopplung als Evidenz v2 (raw/alpha-v2.md#S-3).
EOF
if [ "$mode" = "divergent" ]; then
echo "Divergente Bundle-Zeile (AD-16-negativ): abweichende Routing-Entscheidung." >> wiki/alpha.md
fi
git add -A
git commit -qm "R-9 Mutation ($name)" 2>/dev/null || true
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
full=$(sha256sum "$f" | awk '{print $1}')
masked=$(sed 's|^ at:.*| at: <at>|' "$f" | sha256sum | awk '{print $1}')
full_list="$full_list $f=$full"
masked_list="$masked_list $f=$masked"
done
: > "$ROOT/receipts/$name.bundle"
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; }
}
echo "--- Lauf A (clean) ---"
run9_worktree "$ROOT/wt-a" run9a "clean"
echo "--- Lauf B (clean) ---"
run9_worktree "$ROOT/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; }
if [ "$R9A" != "$R9B" ]; then
echo "HARD-FAIL (R-9): Zwei-Run-Identität verletzt (candidates/order/routing/terms/at.normalform):" >&2
diff -u <(printf '%s\n' "$R9A") <(printf '%s\n' "$R9B") >&2 || true
exit 1
fi
echo " BEFUND: positive Identität byte-gleich (candidates/order/routing/terms/at.normalform) — NICHT-VAKUUM:"
echo "$R9A" | sed 's/^/ /'
# Nicht-Vakuum: alpha Kandidat.
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"
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
echo "HARD-FAIL (R-9): konstruierte Abweichung unerkannt — Negativ-Kontrolle muss FAIL" >&2; exit 1
else
echo " BEFUND: konstruierte AD-16-Abweichung (candidates-Block) erkannt — hart FAIL, kein Vakuum (BS-7-Nachführung)"
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"
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
echo "HARD-FAIL (R-9): atgap-Run weicht außerhalb at ab (nur at darf differieren):" >&2
diff -u <(printf '%s\n' "$R9A") <(printf '%s\n' "$R9AT_REST") >&2 || true
exit 1
fi
if [ "$R9AT_AT" = "$(grep '^generated.at:' "$ROOT/receipts/run9a.receipt")" ]; then
echo "HARD-FAIL (R-9): atgap-Run hat keine at-Abweichung erzeugt (Exzeption nicht ausgeübt)" >&2; exit 1
fi
echo " BEFUND: at-Exzeption ausgeübt — nur generated.at differiert, übrige Felder byte-identisch; legitime at-Abweichung NICHT als AD-16 gemeldet (§5.14 Pkt. 3, §5.15 Pkt. 6)"
# BUNDLE-BYTE-VERGLEICH (patch F-27, §5.15 Pkt. 6/A0-19): der Zwei-Run-Abgleich
# verlangt byte-identische MUTIERTE Bundle-Bestandteile, nicht nur identische
# Receipt-Felder. Wir vergleichen die SHA-256 der mutierten wiki/-Dateien der
# zwei clean-Runs (aI b) sowie des atgap-Runs — letzterer bis auf die benannte
# at-Ausnahme (Pkt. 6, Pkt. 3: allein generated.at darf differieren).
# Bundle-VERGLEICH (patch F-27, §5.15 Pkt. 6/A0-19): byte-identische mutierte
# Bundle-Bestandteile der zwei clean-Runs (voll, inkl. at) sowie des atgap-Runs
# gegenüber dem clean-Run (maskiert, at ausgenommen). Divergenter Run => hart FAIL.
B9AF=$(awk '/^full:/{sub(/^full: /,""); print; exit}' "$ROOT/receipts/run9a.bundle")
B9BF=$(awk '/^full:/{sub(/^full: /,""); print; exit}' "$ROOT/receipts/run9b.bundle")
B9AM=$(awk '/^masked:/{sub(/^masked: /,""); print; exit}' "$ROOT/receipts/run9a.bundle")
B9TM=$(awk '/^masked:/{sub(/^masked: /,""); print; exit}' "$ROOT/receipts/run9at.bundle")
B9NM=$(awk '/^masked:/{sub(/^masked: /,""); print; exit}' "$ROOT/receipts/run9n.bundle")
if [ -z "$B9AF" ] || [ -z "$B9BF" ] || [ -z "$B9AM" ] || [ -z "$B9TM" ] || [ -z "$B9NM" ]; then
echo "HARD-FAIL (R-9-bundle): Bundle-Hash-Artefakt unvollständig (Vakuum)" >&2; exit 1
fi
if [ "$B9AF" != "$B9BF" ]; then
echo "HARD-FAIL (R-9-bundle): Bundle-State-Hashes der zwei clean-Runs differieren — byte-identische MUTIERTE Bestandteile verletzt:" >&2
diff -u <(printf '%s\n' "$B9AF") <(printf '%s\n' "$B9BF") >&2 || true
exit 1
fi
echo " BEFUND: byte-identische mutierte Bundle-Bestandteile der zwei clean-Runs (SHA-256 voll je wiki/-Datei, ohne index/log) — nicht nur Receipt-Felder (A0-19)"
if [ "$B9AM" != "$B9TM" ]; then
echo "HARD-FAIL (R-9-bundle): atgap-Run-Bundle weicht bei maskiertem at ab (byte-identisch bis auf generated.at erwartet):" >&2
diff -u <(printf '%s\n' "$B9AM") <(printf '%s\n' "$B9TM") >&2 || true
exit 1
fi
echo " BEFUND: atgap-Run-Bundle byte-identisch zum clean-Run außer der benannten at-Ausnahme (maskiert), §5.15 Pkt. 6/§5.14 Pkt. 3"
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)"
# =============================================================================
# Abschluss — Gesamt-Exit 0 (nur wenn alle R-Szenarien harte PASS)
# =============================================================================
echo
echo "===== Sandbox abgeschlossen (R-1, R-1b, R-2..R-9) ====="
echo "Alle Szenarien harte PASS, 0 HARD-FAIL."
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
exit 0
File diff suppressed because one or more lines are too long
@@ -29,7 +29,7 @@
# - Dev moves story to 'review', then runs code-review (fresh context, different LLM recommended)
# - Retrospective appends its action items to action_items; the status view surfaces open ones
generated: 08-14-2026 00:00
last_updated: 08-20-2026 17:04
last_updated: 08-21-2026
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.93.12-Verankerungen und die 3.13-Abnahme bleiben offen (epic-3 remains in-progress).
3-9-deterministische-relevanz-und-reconcile-routing-schliessen: backlog
3-9-deterministische-relevanz-und-reconcile-routing-schliessen: review
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