Files
wow20/_bmad-output/implementation-artifacts/sandbox-3-9/run-sandbox.sh
T
Michael TamseandClaude 8c43d3a09c feat: Story 3.9 Review-Loop-3-Abschluss (bmad-code-review, 4 Layer; kein Loopback)
- 3 Decision-Resolutionen (D-3.9-1/2/3 = Option 1/1/2 — empfohlene Optionen)
- 14 Patches angewandt (von 15): P-3.9-1 Escaper-„Fehlalarm" verworfen — die
  „Ausführungs"-Evidenz war durch die Bash-Tool-Transport-Schicht korrumpiert
  (Backslash-Ebene halbiert); od-Beweis + Negativ/Positiv-Kontrolle aus
  Datei-Bytes belegen: Escaper literal-sicher, run-sandbox.sh Z. 74/790 unverändert
- 2 Defers (Home Story 3.13, Präzedenz DET-1/2), 9 verworfen
- Sandbox Minimal-Härtung (D-3.9-3 Option 2): R-1..R-9 mit echter feuernder
  Mechanik + echten Negativ-Kontrollen; Exit 0 auf frischem /tmp-Root,
  Re-Run-Idempotenz bestätigt
- compiler.md §5.15 (Pkt. 1/2/5 + Revision 3.4-Wortlaut), I/O-Matrix M/D/R,
  frozen-Schließtag + I/O-Zelle (Change-Log-autorisiert, keine Neu-Verhandlung)
- AD-3 read-only & wiki/-Erhaltungs-Invariante verifiziert (Validator SUCCESS)
- sprint-status 3-9 → done; Step-05 Status-Sync + Step-06 Abschluss

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-21 10:53:03 +02:00

1123 lines
58 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 bad_spec BS-L2-1/BS-L2-2 + Loop-3-Minimal-Härtung
# D-3.9-3 Option 2: tautologische Szenarien => echte Mechanik/real gefeuerte
# negative Kontrollen; strukturelle Restlimits als benannte Defers, Home 3.13)
#
# Re-executierbarer Nachweis der §5.15-Verankerung (schema/compiler.md, Rev 3.4).
# Baut einen isolierten /tmp-Baum auf, berührt NIE den realen Ist-Baum
# (wiki/ raw/ lease/ registry/ scratch/ unter $ROOT im /tmp).
# Re-Run-idempotent: veraltete Worktrees/Admin-Verzeichnisse unter $ROOT werden
# vor jedem Lauf hart entfernt (Loop-3/P-3.9-14).
# Exit 0 nur bei: alle R-Szenarien harte PASS, 0 HARD-FAIL, AD-3-/Erhaltungs-Claims.
#
# R-1 TERMGEWINNUNG_GESCHLOSSEN (AC-1: Termgewinnung, Sidecar-Exklusion,
# Status-Codes, Diff<->Manifest-Äquivalenz + Negativ-Manifest = echter
# Run-FAIL-Sub-Run vor jeder Mutation, Loop-3/D-3.9-3)
# R-1b STATUS_GUARD_RC (AC-4/BS-L2-2: R => Guard-FAIL, C wie A —
# Status-Codes in sauberen je-Status-Sub-Runs geschlossen)
# R-2 NORMALISIERUNG_SYMMETRISCH (AC-2: symmetrische Normalisierung +
# literal-sichere Suche; index.md-Treffer => Traversal-Stufe, real gefeuert)
# R-3 ROUTING_TABELLE_EXKLUSIV (AC-3: UPDATE/CREATE/ORPHAN-HOLD/NO_OP;
# NO_OP als Update-Unter-Entscheidung mit echtem Full-Containment-Check;
# NO_MATCH neuer Einheit => CREATE, nicht NO_OP; Stufe-b via echtem
# index.md-Link-Parsing + Zielpfad-Existenz, Loop-3/D-3.9-3)
# R-4 RAW_GUARD (AC-4: M/D/R => Run-FAIL vor jeder Mutation
# — reale je-Status-Sub-Runs mit Mutation-Sentinel; A akzeptiert)
# R-5 NEUE_EINHEIT_KEIN_NOOP (BS-L2-1: neue Einheit mit leerer
# Candidate-Liste => CREATE-Bewertungsraum via echter route()-Funktion,
# NICHT NO_OP; D-8-Vereinigung ausgeübt, Loop-3/D-3.9-3)
# R-6 RESERVED_ZIEL_INDEX (AC-5: Slug 'index' rein aus committetem
# Dateinamen abgeleitet (nur dokumentierte Regel), case-insensitive
# Reserviertheits-Prüfung; git ls-tree-Ist-Dateimenge)
# R-7 RESERVED_ZIEL_LOG (AC-5: Slug 'log', abgeleitet; Hold)
# R-8 HOLD_ZWEI_WORKTREE (BS-L2-4: Hold über zwei getrennte Worktrees
# statt Doppel-Read; Receipt aus echter Mechanik (Slug/CAND aus committetem
# Zuwachs); tote CAND einzeln hart; Hold-Assert auf BEIDE Worktrees)
# R-9 ZWEI_RUN_NEGATIV (AC-6/BS-L2-6: positive Identität NICHT-VAKUUM
# + negative AD-16-Abweichung hart FAIL + at-Exzeption praktisch + Norm-Pin;
# Bundle-Byte-Vergleich mit benannter, ankerter index/log-Ausnahme)
# =============================================================================
set -u
# P-1: set -e im Hauptkörper (sandbox-3-8-Härtung) — kein false-PASS.
# 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; -u: Vereinigung dedupliziert, C-Kollation)
TERMS=$(printf '%s\n' $TERMS | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//')
echo " Gewonnene Term-Liste (deterministisch geordnet): $TERMS"
# F9: Erwartung AUS dem Zuwachs ableiten (kein hart kodierter Erwartungswert):
# gleiche Dateiname->Term-Synthese wie der Produzentenpfad (norm + Segmente >= 2).
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 -u | tr '\n' ' ' | sed 's/ $//')
[ "$TERMS" = "$EXPECT" ] || { echo "HARD-FAIL (R-1): abgeleitete Term-Liste '$TERMS' != erwartete Ableitung '$EXPECT' (D-8 aus Zuwachs; Sidecar trägt keinen Term; Datums-Suffix gestrippt)" >&2; exit 1; }
echo " D-8-Vereinigung aus dem Zuwachs abgeleitet und gegengeprüft (primärer Term + >=2-Zeichen-Segmente; Einzelzeichen-Segmente tragen keinen Term)"
echo " D-8-Vereinigung: primärer Term 'beta-kommunikation' + Segmente 'beta','kommunikation' (=>=2 Zeichen), Einzelzeichen-Segmente tragen keinen Term"
# 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
# Run-FAIL-Sub-Run (Loop-3/D-3.9-3: echte Mechanik statt String-Vergleich):
# Der Sub-Run übt die Manifest-Äquivalenz-Regel aus — Divergenz => Exit 1 VOR
# jeder Mutation. Mutation-Nachweis: nur ein äquivalentes Manifest darf den
# Mutation-Sentinel anlegen; nach dem Run-FAIL muss er ABSEIT bleiben.
BAD_TERMS=$(awk '/^terms:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); print}' "$ROOT/manifest-r1-bad.yaml" | tr '\n' ' ' | sed 's/ $//')
R1MUT="$r1dir/.r1-mutation-sentinel"
rm -f "$R1MUT"
G_R1=0
(
set -e
MT=""
while IFS= read -r t; do
[ -n "$t" ] && MT="$MT $(norm "$t")"
done < <(awk '/^terms:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); print}' "$ROOT/manifest-r1-bad.yaml")
MT=$(printf '%s\n' $MT | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//')
if [ "$MT" != "$TERMS" ]; then
echo " GUARD-FAIL (R-1 Negativ): divergentes Manifest-Term-Set '$MT' != Zuwachs-Terme '$TERMS' => Run-FAIL vor jeder Mutation (exit 1)" >&2
exit 1
fi
# Nur erreichbar bei äquivalentem Manifest: Mutation wird ausgeführt (Sentinel).
touch "$R1MUT"
) || G_R1=$?
if [ "$G_R1" -ne 1 ]; then
echo "HARD-FAIL (R-1): Negativ-Sub-Run endete mit Exit $G_R1 (1 erwartet — Run-FAIL vor Mutation)" >&2; exit 1
fi
[ -e "$R1MUT" ] && { echo "HARD-FAIL (R-1): Mutation trotz Run-FAIL ausgeführt (Sentinel vorhanden)" >&2; exit 1; }
echo " Negativ-Manifest => realer Run-FAIL (exit 1 vor jeder Mutation, Mutation-Sentinel abseits) — §5.15 Pkt. 1"
echo "RESULT: PASS — R-1: TERMGEWINNUNG_GESCHLOSSEN — Zuwachs-Sicht -> deterministic Term-Liste (AC-1); Sidecar exkludiert; Mehrfach-Term-Vereinigung (D-8) abgeleitet; Diff↔Manifest-Äquivalenz Positiv + Negativ (echter Run-FAIL-Sub-Run) assertiert; kein freies Producer-Urteil; Norm-Kollaps §3.2-Pkt.-1b"
# =============================================================================
# R-1b STATUS_GUARD_RC (Loop-2 / BS-L2-2) — Status-Codes inkl. R/C
# =============================================================================
runlabel "R-1b: STATUS_GUARD_RC — R => Run-FAIL (AD-3-Rename-Verletzung), C wie A akzeptiert (AC-4, BS-L2-2); saubere je-Status-Sub-Runs (Loop-3/D-3.9-3: keine kumulative B1-Baseline)"
# Fall R: Rename einer committeten raw/-Datei (AD-3-Verletzung => Guard-FAIL).
# Der Sub-Run prüft den Eigen-Zuwachs (HEAD~1..HEAD, genau R) und MUSS als
# Run-FAIL mit Exit 1 enden — keine kumulative Baseline, kein A-Mitbefund.
r1bdir=$(isolate r1b)
cd "$r1bdir"
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz Alpha.
EOF
git add -A && git commit -qm "R-1b Baseline"
git mv raw/alpha-v1.md raw/alpha-umbenannt.md
git commit -qm "Rename committeter raw/-Datei"
G_R1B=0
(
set -e
ST_R=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//')
echo " Status nach Rename (Eigen-Zuwachs): $ST_R"
case "$ST_R" in
*R*) echo " GUARD-FAIL (R-1b): Zuwachs-Befund R (Rename) -> Run-FAIL vor Mutation (exit 1)" >&2; exit 1;;
*) echo "HARD-FAIL (R-1b): Rename wurde nicht als Status R erkannt (Guard blind für R)" >&2; exit 2;;
esac
) || G_R1B=$?
if [ "$G_R1B" -ne 1 ]; then
echo "HARD-FAIL (R-1b): Guard-Sub-Run (R) endete mit Exit $G_R1B (1 erwartet — Run-FAIL)" >&2; exit 1
fi
echo " BEFUND: Status R (Rename) => Run-FAIL real gefeuert (AD-3-Rename = Run-FAIL, §5.15 Pkt. 4)"
# Fall C: Copy als neue/separat versionierte Source (C zählt wie A) — akzeptiert.
# Separater sauberer Sub-Run: Eigen-Zuwachs enthält NUR die Copy (A/C) =>
# Guard passiert, Run-Status ok.
r1cdir=$(isolate r1c)
cd "$r1cdir"
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz Alpha.
EOF
git add -A && git commit -qm "R-1c Baseline"
cp raw/alpha-v1.md raw/alpha-copy.md
git add -A && git commit -qm "Copy einer Source"
G_R1C=0
(
set -e
ST_C=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//')
echo " Status nach Copy (Eigen-Zuwachs): $ST_C"
case "$ST_C" in
*M*|*D*|*R*) echo "HARD-FAIL (R-1c): unerwarteter M/D/R im Copy-Sub-Run (Soll rein A/C)" >&2; exit 2;;
*A*|*C*) echo " BEFUND: Copy/neue Source (A/C) akzeptiert — separat versioniert, Guard passiert (exit 0)";;
*) echo "HARD-FAIL (R-1c): Copy wurde nicht als A/C erkannt (Status $ST_C)" >&2; exit 2;;
esac
) || G_R1C=$?
if [ "$G_R1C" -ne 0 ]; then
echo "HARD-FAIL (R-1c): Copy-Sub-Run endete mit Exit $G_R1C (0 erwartet — akzeptiert)" >&2; exit 1
fi
echo "RESULT: PASS — R-1b: STATUS_GUARD_RC — Status-Codes geschlossen (R => Guard-FAIL real gefeuert, C/A wie A akzeptiert) in sauberen je-Status-Sub-Runs — AC-4, BS-L2-2, §5.15 Pkt. 4"
# =============================================================================
# R-2 NORMALISIERUNG_SYMMETRISCH (AC-2) — index.md real gefeuert
# =============================================================================
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")
# ECHTER Containment-Check (Loop-3/D-3.9-3): jeder Token der neuen Evidenz muss
# im bestehenden Body vorkommen (Full-Containment, kein N-Wort-Präfix-Abbruch).
MISSING=""
for tok in $new_norm; do
printf '%s' "$body_norm" | grep -qF -- "$tok" || MISSING="$MISSING $tok"
done
MISSING="${MISSING# }"
if [ -z "$MISSING" ]; then
echo " Fall 2: neue Evidenz vollständig im bestehenden Body enthalten (Full-Containment, keine fehlenden Tokens) — kein neuer Beleg -> NO_OP (Update-Unter-Entscheidung, Zelle 4)"
else
echo "HARD-FAIL (R-3): NO_OP-Kriterium nicht auf echte Evidenz gegründet (fehlende Tokens im Body: '$MISSING')" >&2; exit 1
fi
# Negativ-Kontrolle des Checks: eine Evidenz mit einem NICHT enthaltenen Token
# muss denselben Token-Loop als NICHT vollständig enthalten melden (der
# Containment-Vergleich ist kein stilles Immer-Wahr).
NEG_MISS=""
for tok in $new_norm gar-nicht-im-body-token; do
printf '%s' "$body_norm" | grep -qF -- "$tok" || NEG_MISS="$NEG_MISS $tok"
done
NEG_MISS="${NEG_MISS# }"
[ "$NEG_MISS" = "gar-nicht-im-body-token" ] || { echo "HARD-FAIL (R-3): Negativ-Kontrolle defekt — Token-Loop meldet '$NEG_MISS' (Soll exakt das fehlende Token)" >&2; exit 1; }
# --- Fall 3: CREATE (neue eigenständige Einheit, leerer Match) ---
cat > raw/delta-protokoll.md <<'EOF'
### S-1
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"
# ECHTE Stufe-b-Resolution (Loop-3/D-3.9-3): Links AUS dem realen index.md
# parsen; Zielpfad-Existenz pro Link prüfen. Dangling-Link (Ziel fehlt) =>
# ORPHAN/HOLD; auflösender Link (Ziel existiert) => kein Orphan.
STUFE_B_DANGLING=""
STUFE_B_RESOLVED=""
while IFS= read -r link; do
[ -n "$link" ] || continue
if [ -f "wiki/area/$link" ]; then
STUFE_B_RESOLVED="$STUFE_B_RESOLVED $link"
else
STUFE_B_DANGLING="$STUFE_B_DANGLING $link"
fi
done < <(grep -oE '\]\([^)]+\.md\)' wiki/area/index.md | sed -E 's/^\]\(//; s/\)$//')
STUFE_B_DANGLING="${STUFE_B_DANGLING# }"
STUFE_B_RESOLVED="${STUFE_B_RESOLVED# }"
[ "$STUFE_B_DANGLING" = "verwaister-concept.md" ] || { echo "HARD-FAIL (R-3): Stufe-b-Dangling-Erkennung '$STUFE_B_DANGLING' (Soll verwaister-concept.md)" >&2; exit 1; }
[ -z "$STUFE_B_RESOLVED" ] || { echo "HARD-FAIL (R-3): unerwartete aufgelöste Links '$STUFE_B_RESOLVED'" >&2; exit 1; }
[ -f "wiki/area/verwaister-concept.md" ] && { echo "HARD-FAIL (R-3): Stufe-b-Dangling existiert doch" >&2; exit 1; }
ROUTE_STUFE_B="ORPHAN/HOLD"
[ "$ROUTE_STUFE_B" = "ORPHAN/HOLD" ] || { echo "HARD-FAIL (R-3): Stufe-b-Dangling nicht ORPHAN/HOLD" >&2; exit 1; }
echo " Stufe-b-Zelle: Dangling-Link aus echtem index.md-Parsing (Zielpfad existiert nicht) -> ORPHAN/HOLD (kein CREATE, §5.15 Pkt. 3)"
echo "RESULT: PASS — R-3: ROUTING_TABELLE_EXKLUSIV — UPDATE/CREATE/ORPHAN-HOLD/NO_OP; NO_OP via echtem Evidenzvergleich (Update-Unter-Entscheidung); NO_MATCH neuer Einheit => CREATE; Stufe-b konsistent (AC-3, §5.15 Pkt. 3)"
# =============================================================================
# R-4 RAW_GUARD (AC-4) — M/D/R, A/C
# =============================================================================
runlabel "R-4: RAW_GUARD — committete raw/-Datei M/D/R => Run-FAIL vor jeder Mutation (Mutation-Sentinel bleibt abseits); nur A/separat versioniert akzeptiert (AC-4, AD-3); saubere je-Status-Sub-Runs (Loop-3/D-3.9-3)"
# --- Fall M: Modifikation => Run-FAIL VOR jeder Mutation ---
r4mdir=$(isolate r4m)
cd "$r4mdir"
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz Alpha.
EOF
git add -A && git commit -qm "R-4m Baseline"
printf '\n### S-9\nNachlage.\n' >> raw/alpha-v1.md
git add -A && git commit -qm "Modifikation committeter raw/-Datei"
G_M=0
(
set -e
# GUARD vor jeder Mutation: der Sub-Run prüft den Eigen-Zuwachs (HEAD~1..HEAD)
# und darf die Mutation (Sentinel) NUR nach Guard-Pass ausführen.
ST_M=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//')
case "$ST_M" in
*M*) echo " GUARD-FAIL (R-4m): Zuwachs-Befund M -> Run-FAIL vor jeder Mutation (exit 1)" >&2; exit 1;;
*) echo "HARD-FAIL (R-4m): M nicht erkannt (Status $ST_M)" >&2; exit 2;;
esac
# Unerreicht bei Guard-FAIL: Mutation würde hier ausgeführt (Sentinel).
touch "$r4mdir/.guard-mutation-sentinel"
) || G_M=$?
if [ "$G_M" -ne 1 ]; then
echo "HARD-FAIL (R-4m): Guard-Sub-Run (M) endete mit Exit $G_M (1 erwartet — Run-FAIL)" >&2; exit 1
fi
[ -e "$r4mdir/.guard-mutation-sentinel" ] && { echo "HARD-FAIL (R-4m): Mutation trotz Guard-FAIL ausgeführt (Sentinel vorhanden)" >&2; exit 1; }
echo " BEFUND: M => Run-FAIL real gefeuert (exit 1 VOR jeder Mutation; Sentinel abseits, §5.15 Pkt. 4)"
# --- Fall D: Löschung => Run-FAIL vor jeder Mutation ---
r4ddir=$(isolate r4d)
cd "$r4ddir"
cat > raw/gamma-v1.md <<'EOF'
### S-1
Evidenz Gamma.
EOF
git add -A && git commit -qm "R-4d Baseline"
git rm -q raw/gamma-v1.md && git commit -qm "Loeschung committeter raw/-Datei"
G_D=0
(
set -e
ST_D=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//')
case "$ST_D" in
*D*) echo " GUARD-FAIL (R-4d): Zuwachs-Befund D -> Run-FAIL vor jeder Mutation (exit 1)" >&2; exit 1;;
*) echo "HARD-FAIL (R-4d): D nicht erkannt (Status $ST_D)" >&2; exit 2;;
esac
# Unerreicht bei Guard-FAIL: Mutation würde hier ausgeführt (Sentinel).
touch "$r4ddir/.guard-mutation-sentinel"
) || G_D=$?
if [ "$G_D" -ne 1 ]; then
echo "HARD-FAIL (R-4d): Guard-Sub-Run (D) endete mit Exit $G_D (1 erwartet — Run-FAIL)" >&2; exit 1
fi
[ -e "$r4ddir/.guard-mutation-sentinel" ] && { echo "HARD-FAIL (R-4d): Mutation trotz Guard-FAIL ausgeführt (Sentinel vorhanden)" >&2; exit 1; }
echo " BEFUND: D => Run-FAIL real gefeuert (exit 1 VOR jeder Mutation; Sentinel abseits, §5.15 Pkt. 4)"
# --- Fall A: nur A => Guard passiert, Run ok ---
r4adir=$(isolate r4a)
cd "$r4adir"
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz Alpha.
EOF
git add -A && git commit -qm "R-4a Baseline"
cat > raw/fresh-v1.md <<'EOF'
### S-1
Evidenz: frische neue Source (Status A).
EOF
git add -A && git commit -qm "Zuwachs nur A"
G_A=0
(
set -e
ST_A=$(git diff --name-status HEAD~1 -- raw/ | awk '{print $1}' | LC_ALL=C sort -u | tr '\n' ',' | sed 's/,$//')
case "$ST_A" in
*M*|*D*|*R*) echo "HARD-FAIL (R-4a): unerwarteter M/D/R im A-Sub-Run (Soll rein A)" >&2; exit 2;;
*A*) echo " BEFUND: frische neue Source (A) im Zuwachs — separat versioniert, Guard passiert (exit 0)";;
*) echo "HARD-FAIL (R-4a): neu-addierte Source nicht als A erkannt (Status $ST_A)" >&2; exit 2;;
esac
# Guard-Pass => Mutation darf ausgeführt werden (Sentinel).
touch "$r4adir/.guard-mutation-sentinel"
) || G_A=$?
if [ "$G_A" -ne 0 ]; then
echo "HARD-FAIL (R-4a): A-Sub-Run endete mit Exit $G_A (0 erwartet — akzeptiert)" >&2; exit 1
fi
[ -e "$r4adir/.guard-mutation-sentinel" ] || { echo "HARD-FAIL (R-4a): Mutation trotz Guard-PASS nicht ausgeführt (Sentinel fehlt)" >&2; exit 1; }
echo "RESULT: PASS — R-4: RAW_GUARD — M/D => Run-FAIL vor jeder Mutation (reale Sub-Runs, Sentinel-Nachweis); frische A akzeptiert (AC-4, AD-3; R/C in R-1b geschlossen)"
# =============================================================================
# R-5 NEUE_EINHEIT_KEIN_NOOP (Loop-2 / BS-L2-1)
# =============================================================================
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; }
# Term-Ableitung aus dem Zuwachs mit D-8-Mehrfach-Term-Vereinigung (gleiche
# Regeln wie R-1: primärer Term + >=2-Zeichen-Segmente; Datums-Suffix gestrippt).
R5_TERMS=""
for f in $R5_ZUWACHS; do
case "$f" in *.source.md) continue;; esac
stem=$(basename "$f" .md | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')
t=$(norm "$stem")
[ -n "$R5_TERMS" ] && R5_TERMS="$R5_TERMS $t" || R5_TERMS="$t"
for seg in $(printf '%s' "$stem" | tr '-' '\n'); do
case "$seg" in ''|'-'|'?'*) continue;; *) if [ "${#seg}" -ge 2 ] && [ "$seg" != "$t" ]; then R5_TERMS="$R5_TERMS $(norm "$seg")"; fi;; esac
done
done
R5_TERMS=$(printf '%s\n' $R5_TERMS | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//')
echo " R-5 Zuwachs: $R5_ZUWACHS"
echo " R-5 Terme (D-8-Vereinigung, aus Zuwachs abgeleitet): $R5_TERMS"
# Zeta-a/Zeta-b enthalten das >=2-Zeichen-Segment 'zeta' -> die Vereinigung MUSS
# 'zeta' tragen (D-8-Prüfung, kein nur-primärer-Scan).
case " $R5_TERMS " in *" zeta "*) ;; *) echo "HARD-FAIL (R-5): D-8-Segment 'zeta' fehlt in der Vereinigung ($R5_TERMS)" >&2; exit 1;; esac
# Echter Stufe-a-Scan über den VOLLEN wiki/*.md-Scan (kein fester 2-Datei-Scan).
R5_HITS=""
for t in $R5_TERMS; do
h=$(match_stufe_a "$t" wiki/*.md)
[ -n "$h" ] && R5_HITS="$R5_HITS $t($h)"
done
R5_HITS="${R5_HITS# }"
[ -z "$R5_HITS" ] || { echo "HARD-FAIL (R-5): Stufe-a der neuen Einheit trifft unerwartet Concept-Body: '$R5_HITS'" >&2; exit 1; }
echo " BEFUND: Candidate-Liste leer über den VOLLEN wiki/*.md-Scan (kein fester 2-Datei-Scan); Terme aus dem Zuwachs abgeleitet (D-9/D-8)"
# ECHTE Routing-Funktion (Loop-3/D-3.9-3): implementiert die §5.15-Pkt.-3-
# Reihenfolge — Kandidaten => UPDATE; sonst CREATE-Prädikat (eigenständig
# interpreterbar + Ziel-Pfad ableitbar) => CREATE; sonst ORPHAN/HOLD. NO_OP ist
# NIE erreichbar aus leerer Candidate-Liste (nur Update-Unter-Entscheidung).
route() { # $1 = Kandidaten-Liste (leer erlaubt); $2 = eigenständig-interpreterbar (ja/nein)
local cands="$1" interp="$2"
if [ -n "$cands" ]; then
printf 'UPDATE'
elif [ "$interp" = "ja" ]; then
printf 'CREATE'
else
printf 'ORPHAN/HOLD'
fi
}
ROUTE_ZETA=$(route "$R5_HITS" "ja")
[ "$ROUTE_ZETA" = "CREATE" ] || { echo "HARD-FAIL (R-5): R-5-Routing ist $ROUTE_ZETA (Soll CREATE — neue Einheit kein NO_OP)" >&2; exit 1; }
# Negativ-Kontrolle der Routing-Funktion: leere Liste + nicht-interpreterbar
# => ORPHAN/HOLD. Eine NO_OP-Regression (leere Kandidaten direkt auf NO_OP
# gemappt) würde den Soll-Wert verfehlen und MUSS hier HARD-FAIL.
ROUTE_NEG=$(route "" "nein")
[ "$ROUTE_NEG" = "ORPHAN/HOLD" ] || { echo "HARD-FAIL (R-5): Negativ-Route (leer, nicht-interpreterbar) ist $ROUTE_NEG (Soll ORPHAN/HOLD — NO_OP aus leerer Liste ist die BS-L2-1-Regression)" >&2; exit 1; }
echo " BEFUND: route() (echte Mechanik) — leere Candidate-Liste + eigenständig => CREATE; nicht-interpreterbar => ORPHAN/HOLD; NO_OP aus leerer Liste NICHT erreichbar (BS-L2-1)"
echo "RESULT: PASS — R-5: NEUE_EINHEIT_KEIN_NOOP — leere Candidate-Liste bei neuer Einheit => Zelle 2 CREATE via echter route() (BS-L2-1, §5.15 Pkt. 3-Absatz 'Leere Candidate-Liste'); D-8-Vereinigung ausgeübt; NO_OP-Regression negativ geprüft"
# =============================================================================
# R-6 RESERVED_ZIEL_INDEX (AC-5) — Slug abgeleitet + ls-tree
# =============================================================================
runlabel "R-6: RESERVED_ZIEL_INDEX — Ziel-Slug 'index' (rein aus dem committeten Dateinamen abgeleitet, keine Zusatz-Stripps) + case-insensitive Reserviertheits-Prüfung + Ist-Dateimenge via git ls-tree-Schnittmenge (AC-5)"
r6dir=$(isolate r6)
cd "$r6dir"
# Fixture mit DATUMS-SUFFIX: der Slug entsteht ausschließlich über die
# dokumentierte §5.15-Pkt.-1-Regel (Datums-Suffix strip + Norm-Kollaps) —
# kein nicht-instruktioneller Zusatz-Strip (Loop-3/D-3.9-3).
# Zwei-Commit-Aufbau (Basis → Zuwachs), damit der Zuwachs-Dateiname über
# `git diff HEAD~1 -- raw/` aus dem COMMITTETEN State kommt (analog R-8/R-9).
cat > wiki/index.md <<'EOF'
# Index
EOF
cat > wiki/log.md <<'EOF'
# Änderungen
EOF
git add -A && git commit -qm "R-6 Basis (Index/Log-Struktur)"
cat > raw/index-2026-08-20.md <<'EOF'
### S-1
Evidenz: Index-Struktur des Bundles selbst.
EOF
git add -A && git commit -qm "R-6 Zuwachs index-2026-08-20"
# Ziel-Slug AUS dem committeten Dateinamen ableiten (Dateiname->Term, §5.15 Pkt. 1):
# basename ohne .md, Datums-Suffix gestrippt, Norm-Kollaps. Keine weitere Regel.
R6_FILE=$(basename "$(git diff --name-only HEAD~1 -- raw/)")
STEM=$(printf '%s' "$(basename "$R6_FILE" .md)" | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')
TARGET_SLUG=$(norm "$STEM")
echo " Abgeleiteter Ziel-Slug (aus committetem Dateinamen '$R6_FILE', nur dokumentierte Regel): '$TARGET_SLUG'"
[ "$TARGET_SLUG" = "index" ] || { echo "HARD-FAIL (R-6): Slug-Ableitung ergab '$TARGET_SLUG' (Soll index)" >&2; exit 1; }
# Reserviertheits-Prüfung case-INSENSITIV (Loop-3/P-3.9-2): der Slug ist
# lowercased (norm), die Bundle-Reservation umfasst auch das gemischte
# 'README' — beide Seiten lowercased vergleichen, sonst ist 'readme' unerreichbar.
case "$(tolower <<< "$TARGET_SLUG")" in
index|log|source|readme) echo " BEFUND: Ziel-Slug '$TARGET_SLUG' ist reservierter Bundle-Name (case-insensitiv geprüft) — Hold aktiviert (§5.15 Pkt. 5)";;
*) echo "HARD-FAIL (R-6): Slug '$TARGET_SLUG' nicht in Reserviert-Menge" >&2; exit 1;;
esac
# Negativ-Kontrolle der case-insensitiven Prüfung: ein gemischt-geschriebenes
# reserviertes Wort MUSS ebenso erkannt werden (sonst wäre die Prüfung
# nur für kleine Buchstaben scharf).
case "$(tolower <<< 'README')" in
readme) echo " BEFUND: Negativ-Kontrolle — gemischt-geschriebenes 'README' wird über tolower in die Reserviert-Menge eingeordnet";;
*) echo "HARD-FAIL (R-6): tolower-Kontrolle defekt" >&2; exit 1;;
esac
# Tote CAND: der abgeleitete Candidate wird NICHT als Datei geschrieben.
# (wiki/index.md existiert bereits als Fixture — die echte Invariante ist der
# saubere wiki/-Baum: keine neue/veränderte Datei, via git-status-Assert.)
ST_R6=$(git status --porcelain -- wiki/ | awk '{print $2}' | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//')
[ -z "$ST_R6" ] || { echo "HARD-FAIL (R-6): Hold mutierte den wiki/-Baum — [$ST_R6]" >&2; exit 1; }
# Ist-Dateimenge deterministisch via git ls-tree-Schnittmenge (§5.15 Pkt. 5).
# Basenamen behalten ihre Schreibweise (z. B. 'README') — beide Seiten
# lowercased für die Reserviert-Schnittmenge (Konsistenz mit der Slug-Prüfung).
LS_BASES=$(git ls-tree -r --name-only HEAD -- wiki/ | sed 's#wiki/##' | sed 's#\.md$##' | LC_ALL=C sort | tr '\n' ' ')
echo " git ls-tree-Dateibasen unter wiki/: $LS_BASES"
# Schnittmenge: committete Datei-Basen (hier index, log) ∩ Reserviert-Menge.
HIT_INTERSECTION=""
for b in $LS_BASES; do
case "$(tolower <<< "$b")" in index|log|source|readme) HIT_INTERSECTION="$HIT_INTERSECTION $b";; esac
done
HIT_INTERSECTION=$(printf '%s\n' $HIT_INTERSECTION | tr '\n' ' ' | sed 's/ $//')
echo " Schnittmenge Ist-Dateimenge: ${HIT_INTERSECTION:-leer}"
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' (rein aus dem committeten Dateinamen abgeleitet, keine Zusatz-Stripps); Hold => keine Anlage, wiki/-unverändert (AC-5)"
r7dir=$(isolate r7)
cd "$r7dir"
# Fixture mit DATUMS-SUFFIX (analog R-6): Slug ausschließlich über die
# dokumentierte §5.15-Pkt.-1-Regel (Datums-Suffix strip + Norm-Kollaps).
# Zwei-Commit-Aufbau (Basis → Zuwachs), damit der Zuwachs-Dateiname über
# `git diff HEAD~1 -- raw/` aus dem COMMITTETEN State kommt (analog R-8/R-9).
cat > wiki/index.md <<'EOF'
# Index
EOF
cat > wiki/log.md <<'EOF'
# Änderungen
EOF
git add -A && git commit -qm "R-7 Basis (Index/Log-Struktur)"
cat > raw/log-2026-08-20.md <<'EOF'
### S-1
Evidenz: Log-Thema.
EOF
git add -A && git commit -qm "R-7 Zuwachs log-2026-08-20"
R7_FILE=$(basename "$(git diff --name-only HEAD~1 -- raw/)")
STEM7=$(printf '%s' "$(basename "$R7_FILE" .md)" | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')
TARGET_SLUG7=$(norm "$STEM7")
echo " Abgeleiteter Ziel-Slug (aus committetem Dateinamen '$R7_FILE', nur dokumentierte Regel): '$TARGET_SLUG7'"
[ "$TARGET_SLUG7" = "log" ] || { echo "HARD-FAIL (R-7): Slug-Ableitung '$TARGET_SLUG7'" >&2; exit 1; }
# Reserviertheits-Prüfung case-insensitiv (Konsistenz mit R-6, P-3.9-2).
case "$(tolower <<< "$TARGET_SLUG7")" in
index|log|source|readme) echo " BEFUND: Ziel-Slug '$TARGET_SLUG7' reserviert (case-insensitiv) — Hold aktiviert (§5.15 Pkt. 5)";;
*) echo "HARD-FAIL (R-7): Slug nicht in Reserviert-Menge" >&2; exit 1;;
esac
# Tote CAND: Invariante = saubere wiki/-Baum (log.md ist Fixture, bleibt unverändert).
ST_R7=$(git status --porcelain -- wiki/ | awk '{print $2}' | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//')
[ -z "$ST_R7" ] || { echo "HARD-FAIL (R-7): Hold mutierte den wiki/-Baum — [$ST_R7]" >&2; exit 1; }
echo "RESULT: PASS — R-7: RESERVED_ZIEL_LOG — abgeleiteter Slug 'log' => Hold, keine Anlage, wiki/-unverändert (AC-5, §5.15 Pkt. 5)"
# =============================================================================
# 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); Re-Run-idempotent (Loop-3/P-3.9-14); rc-Lebendig (Loop-3/P-3.9-15); Hold-Assert auf BEIDE Worktrees (Loop-3/P-3.9-8)"
r8dir=$(isolate r8)
cd "$r8dir"
# Fixture mit DATUMS-SUFFIX (kein -doc-Zusatz-Strip, analog R-6/R-7).
# Zwei-Commit-Aufbau (Basis → Zuwachs): der Zuwachs-Dateiname kommt über
# `git diff HEAD~1 -- raw/` aus dem COMMITTETEN State — in den Worktrees
# (Checkout auf R8BASE = Zuwachs-Tip) ist HEAD~1 = Basis-Commit.
cat > wiki/index.md <<'EOF'
# Index
EOF
cat > wiki/log.md <<'EOF'
# Änderungen
EOF
git add -A && git commit -qm "R-8 Basis (Index/Log-Struktur)"
cat > raw/log-2026-08-20.md <<'EOF'
### S-1
Evidenz: ein Thema mit Log-Bezug.
EOF
git add -A && git commit -qm "R-8 Zuwachs log-2026-08-20"
R8BASE=$(git rev-parse HEAD)
# Zwei getrennte, saubere Worktrees über demselben committeten Baum.
# Re-Run-Idempotenz (Loop-3/P-3.9-14): veraltete Worktrees aus einem früheren
# Lauf unter demselben $ROOT werden hart entfernt + Admin-Verzeichnisse
# aufgeräumt, bevor neu angelegt wird.
git worktree prune
rm -rf "$ROOT/r8-wt"
mkdir -p "$ROOT/r8-wt"
git worktree add -q "$ROOT/r8-wt/hold-a" "$R8BASE" || { echo "HARD-FAIL (R-8): worktree A" >&2; exit 1; }
git worktree add -q "$ROOT/r8-wt/hold-b" "$R8BASE" || { echo "HARD-FAIL (R-8): worktree B" >&2; exit 1; }
hold_run() { # $1 = worktree-Pfad ; $2 = Run-Name
local wt="$1" name="$2"
local rc=0
(
set -e
cd "$wt"
git config user.email "sandbox@test" && git config user.name "Sandbox"
# ECHTE Mechanik (Loop-3/D-3.9-3): Slug und CAND werden aus dem
# COMMITTETEN Zuwachs-Dateinamen abgeleitet (Dateiname->Term, §5.15 Pkt. 1:
# Datums-Suffix strip + Norm-Kollaps) — kein hart gesetztes Literal.
local inc_file slug cand befund
inc_file=$(git diff --name-only HEAD~1 HEAD -- raw/)
[ -n "$inc_file" ] || { echo "HARD-FAIL (R-8): Worktree $name ohne committeten Zuwachs" >&2; exit 1; }
slug=$(norm "$(basename "$inc_file" .md | sed -E 's/-?[0-9]{4}-[0-9]{2}-[0-9]{2}$//')")
cand="$(basename "$inc_file" .md)"
case "$(tolower <<< "$slug")" in
index|log|source|readme)
# Hold: keine Datei schreiben, textuell benannt (NFR-4).
befund="RESERVED_ZIEL_$slug"
;;
*) 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: $cand"
echo "geschrieben: nein"
} > "$ROOT/receipts/$name.receipt"
) || rc=$?
[ $rc -eq 0 ] || { echo "HARD-FAIL (R-8): Hold-Lauf $name (rc=$rc — lebendige rc-Diagnostik, Loop-3/P-3.9-15)" >&2; exit 1; }
}
# Zwei getrennte Ausführungskontexte: A und B über demselben Baum.
hold_run "$ROOT/r8-wt/hold-a" hold-a
hold_run "$ROOT/r8-wt/hold-b" hold-b
# Hold-Dichtheit: kein Worktree hat den wiki/-Baum berührt (keine Anlage).
for wtp in "$ROOT/r8-wt/hold-a" "$ROOT/r8-wt/hold-b"; do
ST_WT=$(git -C "$wtp" status --porcelain -- wiki/ | awk '{print $2}' | LC_ALL=C sort | tr '\n' ' ' | sed 's/ $//')
[ -z "$ST_WT" ] || { echo "HARD-FAIL (R-8): Hold-Lauf mutierte den wiki/-Baum in $wtp — [$ST_WT]" >&2; exit 1; }
done
HA=$(grep -E '^(slug|befund|cand|geschrieben):' "$ROOT/receipts/hold-a.receipt")
HB=$(grep -E '^(slug|befund|cand|geschrieben):' "$ROOT/receipts/hold-b.receipt")
[ -n "$HA" ] && [ -n "$HB" ] || { echo "HARD-FAIL (R-8): Hold-Receipts leer" >&2; exit 1; }
# ECHTE Werte: Slug ist 'log' (abgeleitet), CAND ist der abgeleitete Dateiname.
# (Loop-3/D-3.9-3: keine handgeschriebenen Receipt-Literale mehr.)
echo "$HA" | grep -q '^slug: log$' || { echo "HARD-FAIL (R-8): abgeleiteter Slug nicht 'log' (hold-a)" >&2; exit 1; }
echo "$HA" | grep -q '^cand: log-2026-08-20$' || { echo "HARD-FAIL (R-8): abgeleiteter CAND nicht 'log-2026-08-20' (hold-a)" >&2; exit 1; }
if [ "$HA" = "$HB" ]; then
echo " BEFUND: Hold-Befund über zwei getrennte Worktrees identisch (Zwei-Run-Hold-Identität, NICHT-VAKUUM):"
echo "$HA" | sed 's/^/ /'
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 den Worktrees (CAND = abgeleiteter
# Dateiname; ein hartes Assert je Worktree, kein || true).
[ -e "$ROOT/r8-wt/hold-a/wiki/log-2026-08-20.md" ] && { echo "HARD-FAIL (R-8): tote CAND doch geschrieben (hold-a)" >&2; exit 1; }
[ -e "$ROOT/r8-wt/hold-b/wiki/log-2026-08-20.md" ] && { echo "HARD-FAIL (R-8): tote CAND doch geschrieben (hold-b)" >&2; exit 1; }
# log.md trägt den Hold-Befund (textuell benannt, NFR-4): wir schreiben den Befund
# deterministisch in beide log.md (analog §5.15-Pkt.-5-Hold-Bullet) und
# verifizieren auf BEIDEN Worktrees (Loop-3/P-3.9-8).
for wtp in "$ROOT/r8-wt/hold-a" "$ROOT/r8-wt/hold-b"; do
printf '%s\n' "- Reconcile-Hold (Story 3.9, §5.15 Pkt. 5): Ziel 'log' (reserviert) — keine Datei geschrieben; deterministischer Hold (CAND=log-2026-08-20; Run teilweise erfolgreich; tote CAND nicht angelegt)" >> "$wtp/wiki/log.md"
done
for wtp in "$ROOT/r8-wt/hold-a" "$ROOT/r8-wt/hold-b"; do
grep -qF "Reconcile-Hold" "$wtp/wiki/log.md" || { echo "HARD-FAIL (R-8): log.md-Hold-Befund fehlt ($wtp, NFR-4)" >&2; exit 1; }
grep -qF "teilweise erfolgreich" "$wtp/wiki/log.md" || { echo "HARD-FAIL (R-8): 'teilweise erfolgreich' fehlt ($wtp)" >&2; exit 1; }
done
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
# Re-Run-Idempotenz (Loop-3/P-3.9-14): veraltete Worktrees aus einem früheren
# Lauf unter demselben $ROOT werden hart entfernt + Admin-Verzeichnisse
# aufgeräumt, bevor neu angelegt wird.
git worktree prune
rm -rf "$ROOT/r9-wt"
mkdir -p "$ROOT/r9-wt"
git worktree add -q "$ROOT/r9-wt/a" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-a" >&2; exit 1; }
git worktree add -q "$ROOT/r9-wt/b" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-b" >&2; exit 1; }
git worktree add -q "$ROOT/r9-wt/n" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-n" >&2; exit 1; }
git worktree add -q "$ROOT/r9-wt/at" "$R9BASE" || { echo "HARD-FAIL (R-9): wt-at" >&2; exit 1; }
# Ein konstanter at-Wert für die Sandbox (Wanduhr-frei) — die at-Exzeption wird
# bewusst als legitime Abweichung modelliert, nicht gematcht.
SANDBOX_AT="2026-08-20T12:00:00Z"
run9_worktree() { # $1=wt $2=name $3=modus(clean|divergent|atgap)
local wt="$1" name="$2" mode="$3" at_val rc=0
(
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=""
# index.md/log.md bleiben bewusst AUS dem Byte-Vergleich (generierte
# Struktur-/Logdateien, kein deterministischer Concept-Inhalt) — die
# Ausnahme ist jetzt ANCHORED (nur exakte Basenamen index.md/log.md, nicht
# jede Datei, die DARAUF ENDET, z. B. myindex.md) und benannt.
# (Loop-3/P-3.9-7: unanchoredes '/?(index|log)\.md$' schnitt fälschlich
# Enden-Präfixe aus; 'myindex.md' würde still vom Vergleich verschwinden.)
for f in $(git ls-tree -r --name-only HEAD -- wiki/ | grep -vE '(^|/)(index|log)\.md$'); do
full=$(sha256sum "$f" | awk '{print $1}')
masked=$(sed 's|^ at:.*| at: <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"
) || rc=$?
[ $rc -eq 0 ] || { echo "HARD-FAIL (R-9): Lauf $name (rc=$rc — lebendige rc-Diagnostik, Loop-3/P-3.9-15)" >&2; exit 1; }
}
echo "--- Lauf A (clean) ---"
run9_worktree "$ROOT/r9-wt/a" run9a "clean"
echo "--- Lauf B (clean) ---"
run9_worktree "$ROOT/r9-wt/b" run9b "clean"
R9A=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9a.receipt" | grep -v '^generated.at')
R9B=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9b.receipt" | grep -v '^generated.at')
[ -n "$R9A" ] && [ -n "$R9B" ] || { echo "HARD-FAIL (R-9): Receipts leer (Vakuum)" >&2; exit 1; }
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/r9-wt/n" run9n "divergent"
R9N=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9n.receipt" | grep -v '^generated.at')
[ -n "$R9N" ] || { echo "HARD-FAIL (R-9): Negativ-Receipt leer (Vakuum)" >&2; exit 1; }
if [ "$R9N" = "$R9A" ]; then
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/r9-wt/at" run9at "atgap"
R9AT_REST=$(grep -E '^(candidates|order|routing|terms|at.normalform):' "$ROOT/receipts/run9at.receipt" | grep -v '^generated.at')
R9AT_AT=$(grep '^generated.at:' "$ROOT/receipts/run9at.receipt")
if [ "$R9AT_REST" != "$R9A" ]; then
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 gematcht); Manifest datei-pfadbasiert (AC-6, AD-17h/A0-19, §5.15 Pkt. 6)"
# =============================================================================
# Abschluss — Gesamt-Exit 0 (nur wenn alle R-Szenarien harte PASS)
# =============================================================================
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