Files
wow20/_bmad-output/implementation-artifacts/sandbox-3-3/run-sandbox.sh
T

393 lines
22 KiB
Bash

#!/usr/bin/env bash
# Story 3.3 — Sandbox-Tests der operationellen Update-Formen (§5.9 Pkt. 2, Revision 2.8)
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb33)
# Zweck: alle drei Update-Formen (Erweitern + Präzisieren + Korrigieren) + No-Op
# durchspielen; die Erhaltungs-Invariante (§5.9 Pkt. 5) als HARDE Assertion
# erzwingen (Diff nach Normalisierung ⊆ {betroffener Concept-Pfad, log}; keine
# neue Datei; kein Ghost-Diff); Zwei-Run-Identität (AD-17h/A0-19);
# Frontmatter-Konformität je mutiertem Concept (Vertrag §3.3/§3.4-Subset).
# Linux-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/-Baum.
set -u
ROOT=$(mktemp -d /tmp/sb33-XXXXXX)
SB="$ROOT/sb"
mkdir -p "$SB/wiki" "$SB/raw"
cd "$SB"
git init -q
git config user.email "sandbox@test"
git config user.name "Sandbox"
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit) ----------
# Concept alpha mit drei belegten Aussagen; Aussagen 1/2 sind fachlich korrekt,
# Aussage 3 ist fehlerhaft (Korrigieren-Ziel des Szenarios U3). beta ist ein
# nicht-betroffenes Kontroll-Concept (Ghost-Diff-Kontrolle, U5).
cat > wiki/index.md <<'EOF'
# Index
- [Alpha](alpha.md)
- [Beta](beta.md)
EOF
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 verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).
Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).
Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).
EOF
cat > wiki/beta.md <<'EOF'
---
type: concept
sources:
- resource: raw/beta-v1.md
id: s1
generated:
by: wow-compiler/0.1.0
at: 2026-08-16T09:00:00Z
---
Beta beschreibt ein anderes, hier nicht betroffenes Thema.
EOF
cat > wiki/log.md <<'EOF'
# Log
EOF
cat > raw/alpha-v1.md <<'EOF'
### S-1
Evidenz v1: deterministische-init-sequenz für den Abgleich.
### S-2
Evidenz v1: keine Rotation der Schlüssel.
### S-3
Evidenz v1: Alpha läuft auf isolierten Netzen.
EOF
cat > raw/beta-v1.md <<'EOF'
### S-1
Evidenz v1: Beta-Thema.
EOF
git add -A
git commit -qm "Baseline"
BASE=$(git rev-parse HEAD)
echo "BASELINE-COMMIT: $BASE"
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
echo
runlabel() { echo; echo "########## $1 ##########"; }
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien)
isolate() { git checkout -q -b "$1" "$BASE" 2>/dev/null || git checkout -q "$1"; git reset -q --hard "$BASE"; git clean -qfd wiki raw; }
# Pkt.-5-Probe (D-1-Form): Baseline-Diff + porcelain, normalisiert
# (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert; deterministisch, AD-17h)
probe() {
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
}
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
# Erwartete erlaubte Menge = Space-getrennte normalisierte Pfade
# (z. B. "alpha log"). Jeder Proben-Eintrag MUSS in der erlaubten Menge liegen —
# sonst Ghost-Diff (nicht betroffener Pfad) → HARD-FAIL, Exit 1 (NFR-4/FT-6).
assert_invariant() {
local expected="$1" got p bad=0 rcs=0
got=$(inv_set)
for p in $got; do
case " $expected " in
*" $p "*) ;;
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
esac
done
# Keine neue Datei (Duplikat-Kontrolle: die ??-Sicht fehlt git diff allein)
rcs=$(git status --porcelain -- wiki/ | grep -c '^??' || true)
[ "$rcs" -eq 0 ] || { echo "HARD-FAIL (keine neue Datei): $rcs ungetrackte neue Datei(en) unter wiki/ (Duplikat/Ghost-Diff)" >&2; bad=1; }
if [ "$bad" -eq 0 ]; then
echo "RESULT: PASS — Probe {$got} ⊆ erlaubte Menge {$expected}; keine neue Datei; kein Ghost-Diff"
else
exit 1
fi
}
# ---------- Frontmatter-Konformitaet je mutiertem Concept (Vertrag §3.3/§3.4, §6.5) ----------
# $1=Datei; $2...=erwartete sources[].resource-Werte. Prueft: Top-Level-Key-Subset
# {type,sources,generated,verified,status,stale_after}, sources-Eintrag-Key-Subset
# (Vertrag §3.3-Innen-Ebene), type=concept, generated.at = volle ISO-8601-Datetime
# (die erlaubten Key-Mengen sind inline im awk-Subset-Vergleich hartkodiert).
assert_frontmatter() {
local f="$1"; shift
local r bad=0
# Kontextuelle Validierung ueber awk: Top-Level-Subset {type,sources,generated,
# verified,status,stale_after}; Innen-Ebenen je Sektion — sources: Vertrag §3.3-
# Subset {resource,id,title,author,usage_count,last_modified}; generated/verified:
# {by,at} (Vertrag §3.4/§3.5). Jeder andere Key = unbefugt (Punkt 6, §6.5).
local v
v=$(awk '
/^---$/{n++; if(n==2) exit; next}
/^[A-Za-z_][A-Za-z0-9_-]*:/{
k=$0; sub(/:.*/,"",k)
if (k=="sources") top="sources"
else if (k=="generated" || k=="verified") top="genver"
else top="other"
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
# Duplikat-Prohibition: jeder Top-Level-Key hoechstens einmal (Punkt 13).
if (seenk[k]++) print "DUP_KEY:" k
# Canonische Reihenfolge (Vertrag §3.x Normalform: type, sources, generated,
# verified, status, stale_after): jeder neue Key-Kandidat muss der canonichen
# Ordnung folgen — Verstoß via Ordnungs-Rang erkannt (ORDER_VIOLATION).
r=0
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
if (r>0) lastr=r
next
}
/^[[:space:]]/{
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
ik=substr($0,1,RLENGTH-1)
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
if (top=="other") print "INNER_UNBEFUGT:" ik
}
}
' "$f")
if [ -n "$v" ]; then
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
exit 1
fi
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
for r in "$@"; do
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
done
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
}
# ---------- Update-Helfer (deterministisch; at gepinnt fuer Zwei-Run-Identitaet) ----------
AT_NEW="2026-08-19T12:00:00Z"
bump_at() { sed -i "s|^ at: .*| at: $AT_NEW|" "$1"; }
add_source() { # $1=Datei $2=resource $3=id (Eintrag vor der generated:-Zeile einfuegen)
sed -i "s|^generated:| - resource: $2\n id: $3\ngenerated:|" "$1"
}
log_update() { # $1=Concept-Pfad $2=raw-Datei
printf '\n## 2026-08-19\n- Story 3.1-Update: %s (%s; Baseline %s)\n' "$1" "$2" "$BASE" >> wiki/log.md
}
# =====================================================================
runlabel "U1: ERWEITERN — neue belegte Aussage, bestehender Pfad wird erweitert (AC-1/FR-6)"
isolate u1
cat > raw/alpha-v2.md <<'EOF'
### S-4
Evidenz v2 (neue Aussage): Alpha-Replikation traegt einen zusaetzlichen Schluessel-Rotationszyklus.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- Kandidaten-Erhebung: rg -l 'schluessel-rotationszyklus' wiki/ ---"
if command -v rg >/dev/null 2>&1; then rg -l 'schluessel-rotationszyklus' -g '!log.md' wiki/ || true
else grep -rl 'schluessel-rotationszyklus' --exclude=log.md wiki/ || true; fi
echo "--- UPDATE: neue Aussage als Absatz angefuegt (§5.5-Inline-Beleg), sources-Zuwachs s2, at-Bump ---"
cat >> wiki/alpha.md <<'EOF'
Alpha führt für die Replikation einen zusätzlichen Schlüssel-Rotationszyklus (raw/alpha-v2.md#S-4).
EOF
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
echo "--- Struktur-Erhaltung: bestehende drei Aussagen byte-identisch erhalten? ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 wurde beim Erweitern umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 (Netze) wurde beim Erweitern umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 3 wurde beim Erweitern umgeschrieben" >&2; exit 1; }
echo "RESULT: PASS — bestehende belegte Aussagen unveraendert (Struktur-Erhaltungsregel Erweitern)"
echo "--- Probe (Pkt. 5, vor dem Commit) ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
# =====================================================================
runlabel "U2: PRAEZISIEREN — bestehende Aussage wird an ihr selbst geschaerft (AC-2/FR-6)"
isolate u2
cat > raw/alpha-v2.md <<'EOF'
### S-1
Evidenz v2 (Praezisierung): die deterministische-init-sequenz wird nach der initialen Synchronisation zusaetzlich rotiert.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- UPDATE: Aussage 1 an Position geschaerft, Beleg nachgefuehrt (Multi-Beleg §5.5); kein Satz-Umbau ---"
sed -i 's|^Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1)\.|Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich und rotiert sie nach der initialen Synchronisation zusätzlich (raw/alpha-v1.md#S-1; raw/alpha-v2.md#S-1).|' wiki/alpha.md
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
echo "--- Struktur-Erhaltung: Aussagen 2/3 unveraendert, geschaerfte Aussage 1 an Position? ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich und rotiert sie nach der initialen Synchronisation zusätzlich (raw/alpha-v1.md#S-1; raw/alpha-v2.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: geschaerfte Aussage fehlt" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 wurde beim Praezisieren veraendert" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 3 wurde beim Praezisieren veraendert" >&2; exit 1; }
echo "RESULT: PASS — Schaerfung an der Aussage selbst, Position/Struktur erhalten, kein Satz-Umbau"
echo "--- Probe ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
# =====================================================================
runlabel "U3: KORRIGIEREN — fehlerhafte Aussage abgeloest, nicht still geloescht (AC-2, AD-16-Default)"
isolate u3
cat > raw/alpha-v2.md <<'EOF'
### S-2
Evidenz v2 (Korrektur): Alpha rotiert seine Schluessel bei jeder Sitzung.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- UPDATE: Aussage 3 wird explizit abgeloest + Ersetzungsbeleg; keine stille Loeschung ---"
sed -i 's|^Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2)\.|Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2) — überholt durch: Alpha rotiert seine Schlüssel bei jeder Sitzung (raw/alpha-v2.md#S-2).|' wiki/alpha.md
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
echo "--- AD-16-Default: alte Aussagen 1/2 textuell erhalten (keine stille Loeschung, keine flankierende Umschreibung)? ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 wurde beim Korrigieren flankierend umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 wurde beim Korrigieren flankierend umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2)' wiki/alpha.md || { echo "HARD-FAIL: alte Aussage wurde still geloescht oder ihr Wortlaut verändert (AD-16, Struktur-Erhaltungsregel Korrigieren)" >&2; exit 1; }
grep -qF 'rotiert seine Schlüssel bei jeder Sitzung (raw/alpha-v2.md#S-2)' wiki/alpha.md || { echo "HARD-FAIL: Ersetzungsbeleg fehlt" >&2; exit 1; }
echo "RESULT: PASS — Abloesung mit Ersetzungsbeleg, Provenienz erhalten (AD-16-Default Erhaltung)"
echo "--- Probe ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
# =====================================================================
runlabel "U4: NO_OP — Evidenz ist bereits vollstaendig im Body (engere Auslegung, Pkt. 2)"
isolate u4
cat > raw/alpha-v2.md <<'EOF'
Evidenz v2 (Redundanz): deterministische-init-sequenz fuer den Abgleich; Alpha rotiert seine Schluessel nie.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
echo "--- Kandidaten-Erhebung: Terme treffen alpha; die Evidenz ist bereits vollstaendig enthalten ---"
echo "--- UPDATE: KEINE Body-Mutation, KEIN at-Bump, KEIN sources-Zusatz, KEIN log.md-Eintrag (byte-identisch) ---"
if git status --porcelain -- wiki/ | grep -q .; then
echo "HARD-FAIL (No-Op): Wiki-Baum ist nicht byte-identisch" >&2
git status --porcelain -- wiki/
exit 1
fi
echo "RESULT: PASS — No-Op: keine Mutation, kein at-Bump, kein sources-Zusatz, kein log.md-Eintrag (byte-identisch)"
echo "--- Probe (leer = Ghost-Diff-negativ, leere Menge ist Teilmenge jeder erlaubten Menge) ---"; probe
assert_invariant ""
# =====================================================================
runlabel "U5: NEGATIV-KONTROLLE — Assertions-Mechanik erkennt einen Ghost-Diff (nicht vacuous)"
isolate u5
echo "unbefugte Mutation auf nicht-betroffenem beta" >> wiki/beta.md # simulierter Ghost-Diff
echo "--- Probe (erwartet: beta ausserhalb der erlaubten Menge {alpha log}) ---"; probe
if ( assert_invariant "alpha log" ) >/dev/null 2>&1; then
echo "HARD-FAIL (Assertions-Mechanik): Ghost-Diff auf beta wurde NICHT erkannt" >&2
exit 1
else
echo "RESULT: PASS — Ghost-Diff auf beta wurde erkannt (Assertions-Mechanik loest verlaesslich HARD-FAIL aus, kein Ghost-Diff kommt durch)"
fi
# =====================================================================
runlabel "U6: ZWEI-RUN-IDENTITAET (AD-17h/A0-19) — gleicher Git-State + gleiches Eingabeset -> identischer Update-Vorgang"
# Zwei unabhaengige Runs ueber dieselbe Evidenz (Erweitern); die Ergebnisse
# (mutierter Body + Frontmatter + Status) muessen byte-identisch sein.
run_erweitern() { # $1 = Branchname; mutiert Worktree, dann git add -A (Staging)
isolate "$1"
cat > raw/alpha-v2.md <<'EOF'
### S-4
Evidenz v2 (neue Aussage): Alpha-Replikation traegt einen zusaetzlichen Schluessel-Rotationszyklus.
EOF
git add -A; git commit -qm "Zuwachs raw/alpha-v2.md"
cat >> wiki/alpha.md <<'EOF'
Alpha führt für die Replikation einen zusätzlichen Schlüssel-Rotationszyklus (raw/alpha-v2.md#S-4).
EOF
add_source wiki/alpha.md raw/alpha-v2.md s2
bump_at wiki/alpha.md
log_update alpha raw/alpha-v2.md
git add -A # Staging — die Mutationen sind Teil des Runs (Commit-Boundary = Mutations-Boundary)
}
run_erweitern u6a
A_ALPHA=$(sha256sum wiki/alpha.md | cut -d' ' -f1) # Worktree-Inhalt = staged-Inhalt (git add)
A_LOG=$(sha256sum wiki/log.md | cut -d' ' -f1)
git commit -qm "Run A (Erweitern)"
A_ALPHA_COM=$(git show HEAD:wiki/alpha.md | sha256sum | cut -d' ' -f1)
run_erweitern u6b
B_ALPHA=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
B_LOG=$(sha256sum wiki/log.md | cut -d' ' -f1)
git commit -qm "Run B (Erweitern)"
B_ALPHA_COM=$(git show HEAD:wiki/alpha.md | sha256sum | cut -d' ' -f1)
echo "Run A (alpha.md): $A_ALPHA"
echo "Run B (alpha.md): $B_ALPHA"
echo "Run A (log.md): $A_LOG"
echo "Run B (log.md): $B_LOG"
echo "Run A commit (alpha.md): $A_ALPHA_COM ; Run B commit (alpha.md): $B_ALPHA_COM"
if [ "$A_ALPHA" = "$B_ALPHA" ] && [ "$A_LOG" = "$B_LOG" ] && [ "$A_ALPHA_COM" = "$B_ALPHA_COM" ]; then
echo "RESULT: PASS — zwei unabhaengige Runs ueber dieselbe Evidenz -> identischer Update-Vorgang (AD-17h/A0-19)"
else
echo "HARD-FAIL (Determinismus-Vertrag): Run A und Run B weichen ab" >&2
exit 1
fi
# =====================================================================
runlabel "U7: MEHRFACH_TREFFER — zwei Neuevidenzen desselben Runs treffen denselben Concept-Pfad (Konsolidierung, I/O-Matrix)"
# Matrix-Zeile MEHRFACH_TREFFER: mehrere neue Wissenseinheiten desselben Runs auf
# denselben Pfad -> EIN konsolidiertes Update (eine Body-Mutation, EIN log.md-Eintrag,
# EIN konsolidierter sources-Zuwachs, EIN generated.at). Reihenfolge = Zuwachs-Sicht
# (Ankunftsreihenfolge der Belege); lexikografischer Tie-Break bei identischem Ort
# (Body-Ende) -> alpha-v2.md vor alpha-v3.md.
isolate u7
cat > raw/alpha-v2.md <<'EOF'
### S-4
Evidenz v2 (neue Aussage): Alpha-Replikation nutzt Quorum-Bestaetigung.
EOF
cat > raw/alpha-v3.md <<'EOF'
### S-4
Evidenz v3 (neue Aussage): Alpha-Replikation waechst linear zum Cluster.
EOF
git add -A; git commit -qm "Zwei Einheiten, ein Run (raw/alpha-v2.md + raw/alpha-v3.md)"
echo "--- Kandidaten-Erhebung: beide Terme treffen denselben Pfad alpha ---"
echo "--- KONSOLIDIERTES UPDATE: beide Aussagen als Absaetze angehaengt (Zuwachs-Sicht: v2 vor v3), EIN log-Eintrag, sources-Zuwachs s2+s3, EIN at ---"
cat >> wiki/alpha.md <<'EOF'
Alpha-Replikation nutzt Quorum-Bestaetigung (raw/alpha-v2.md#S-4).
Alpha-Replikation waechst linear zum Cluster (raw/alpha-v3.md#S-4).
EOF
add_source wiki/alpha.md raw/alpha-v2.md s2
add_source wiki/alpha.md raw/alpha-v3.md s3
bump_at wiki/alpha.md
log_update alpha "raw/alpha-v2.md + raw/alpha-v3.md"
echo "--- Konsolidierung: genau EIN log.md-Eintrag und EIN generated.at trotz zweier Einheiten? ---"
[ "$(grep -c 'Story 3.1-Update: alpha' wiki/log.md)" -eq 1 ] || { echo "HARD-FAIL: Mehrfach-Treffer erzeugte nicht exakt einen log.md-Eintrag (Konsolidierung oder Eintragspflicht Pkt. 4 verletzt)" >&2; exit 1; }
grep -qF 'raw/alpha-v2.md' wiki/log.md || { echo "HARD-FAIL: konsolidierter log.md-Eintrag verlinkt raw/alpha-v2.md nicht (Pkt. 4, genutzte raw/-Quellen)" >&2; exit 1; }
grep -qF 'raw/alpha-v3.md' wiki/log.md || { echo "HARD-FAIL: konsolidierter log.md-Eintrag verlinkt raw/alpha-v3.md nicht (Pkt. 4, genutzte raw/-Quellen)" >&2; exit 1; }
[ "$(grep -c '^ at:' wiki/alpha.md)" -eq 1 ] || { echo "HARD-FAIL: Mehrfach-Treffer erzeugte mehrere generated.at" >&2; exit 1; }
echo "--- Struktur-Erhaltung: alte Aussagen byte-identisch, beide neuen Aussagen vorhanden, Zuwachs-Sicht-Ordnung v2-vor-v3 ---"
grep -qF 'Das Alpha-Protokoll verwendet deterministische-init-sequenz für den Abgleich (raw/alpha-v1.md#S-1).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 1 wurde beim konsolidierten Update umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha läuft ausschließlich auf isolierten Netzen (raw/alpha-v1.md#S-3).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 2 wurde beim konsolidierten Update umgeschrieben" >&2; exit 1; }
grep -qF 'Alpha rotiert seine Schlüssel nie (raw/alpha-v1.md#S-2).' wiki/alpha.md || { echo "HARD-FAIL: Aussage 3 wurde beim konsolidierten Update umgeschrieben" >&2; exit 1; }
grep -qF 'Quorum-Bestaetigung (raw/alpha-v2.md#S-4)' wiki/alpha.md || { echo "HARD-FAIL: neue Aussage aus v2 fehlt" >&2; exit 1; }
grep -qF 'waechst linear zum Cluster (raw/alpha-v3.md#S-4)' wiki/alpha.md || { echo "HARD-FAIL: neue Aussage aus v3 fehlt" >&2; exit 1; }
v2pos=$(grep -n 'Quorum-Bestaetigung' wiki/alpha.md | cut -d: -f1); v3pos=$(grep -n 'waechst linear' wiki/alpha.md | cut -d: -f1)
[ "$v2pos" -lt "$v3pos" ] || { echo "HARD-FAIL: Zuwachs-Sicht-Ordnung verletzt (v3 vor v2)" >&2; exit 1; }
echo "RESULT: PASS — ein konsolidiertes Update (eine Body-Aenderung, ein log-Eintrag, ein sources-Zuwachs s2+s3, ein at); Zuwachs-Sicht-Ordnung v2-vor-v3"
echo "--- Probe ---"; probe
assert_invariant "alpha log"
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md raw/alpha-v3.md
# =====================================================================
runlabel "U8: STRUKTUR-VERLETZUNG — unbefugter Frontmatter-Key wird erkannt (Pkt. 5 Struktur-Ebene, P2-Check-Element (6))"
isolate u8
echo "--- simulierter Verstoß gegen die Struktur-Erhaltung: unbefugter Frontmatter-Key 'foo' im YAML-Block ---"
awk '/^type: concept$/{print; print "foo: bar"; next} {print}' wiki/alpha.md > /tmp/sb33-fm && mv /tmp/sb33-fm wiki/alpha.md
echo "--- Probe + Frontmatter-Konformität (erwartet: HARD-FAIL, Verstoß textuell benannt, NFR-4) ---"; probe
if ( assert_invariant "alpha log" && assert_frontmatter wiki/alpha.md ) >/dev/null 2>&1; then
echo "HARD-FAIL (Struktur-Ebene): unbefugter Frontmatter-Key wurde NICHT erkannt (Pkt. 5/Pkt. 6-Element (6))" >&2
exit 1
else
echo "RESULT: PASS — unbefugter Frontmatter-Key wird als Struktur-Verstoß erkannt (Frontmatter-Subset-HARD-FAIL ausgelöst)"
fi
# =====================================================================
runlabel "U9: NEUE-DATEI — ungetrackte neue Datei unter wiki/ wird erkannt (??-Sicht, Duplikat-Kontrolle)"
isolate u9
echo "--- simulierter Ghost-Diff: ungetrackte neue Datei wiki/neu.md (Duplikat, ??-Sicht) ---"
echo "unbefugte neue Concept-Datei" > wiki/neu.md
echo "--- Probe (erwartet: neu ausserhalb erlaubter Menge + ??-Sicht-HARD-FAIL) ---"; probe
if ( assert_invariant "alpha log" ) >/dev/null 2>&1; then
echo "HARD-FAIL (Keine-neue-Datei): ungetrackte neue Datei wiki/neu.md wurde NICHT erkannt (??-Sicht, Duplikat-Kontrolle)" >&2
exit 1
else
echo "RESULT: PASS — ungetrackte neue Datei unter wiki/ wird über die ??-Sicht erkannt (Duplikat/Ghost-Diff, keine neue Datei)"
fi
echo
echo "===== Sandbox abgeschlossen (alle 9 Tests U1-U9) ====="
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"