Files
wow20/_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh
T
Michael TamseandClaude 73f2c9e71d 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>
2026-08-21 06:21:08 +02:00

953 lines
47 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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