#!/usr/bin/env bash # Story 3.7 — Sandbox-Tests der Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (§5.13, Revision 3.2) # Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb37) # Zweck: die Reason/Mutate-Phasen-Disziplin (§5.13) als re-executierbarer Run-Demonstrator # durchspielen — # CONSIST-1 KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7): der P2-Block (§5.9 Pkt. 6) # erzeugt aus committetem Input eine konsistente Änderungsplanung (Input-Zustand, # Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-Erhaltung) — als Variablen-Set # captured, Determinismus (AD-17h/A0-19); Plan-Defizit ⇒ Abbruch vor Mutationsphase # CONSIST-2 PLAN_FREEZE (AC-1, §5.13 Pkt. 2): Plan-Freeze = Veränderungs-Sperre — eine # Mutation ausserhalb der Plan-Pfadmengen (Kandidatenliste ∪ Neu-Anlage ∪ log.md ∪ # index.md) wird erkannt und verweigert/verhindert (Ghost-Diff-Kopplung §5.9 Pkt. 5) # CONSIST-3 ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2, §5.13 Pkt. 3): fehlgeschlagene # Mutation rollt auf exakte Baseline zurueck (SHA-256 byte-identisch, Post-Rollback-Diff # leer gg. Baseline), Bundle-Zustand == Baseline; raw/ unverändert (AD-3) # CONSIST-4 MUTATION_ABBRUCH (AC-2, §5.13 Pkt. 3/4): Abbruch nach Teilerfolg (mehrere # geplante Mutationen, nur ein Teil ausgefuehrt) hinterlaesst keinen Teilerfolg als # fertige Mutation (Commit-Boundary = Mutations-Boundary, AD-17f); raw/ unberührt # CONSIST-5 VALIDATION_FAIL (AC-2, §5.13 Pkt. 5): §6-Pkt.-3-Rollback, keine weiteren # Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante) # CONSIST-6 VALIDATION_SUCCESS (AC-3, §5.13 Pkt. 6): Mutationen als Ganzes committet, # Diff gg. Plan-Menge gedeckt, §6-Pkt.-4-Nachweis; kein Wanduhr-Trigger (A0-20) # CONSIST-7 DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4, §5.13 Pkt. 7): Zwei identische # Runs -> identische Plan-/Rollback-/State-Outputs (AD-17h/A0-19); die Phasen-Trennung # ist logisch in einer Session — kein Prozess/Server/MCP wird gestartet (D-3, AD-11, # grep-Negativkontrolle: keine Hintergrundprozesse/Logger-Daemons) # Erhaltungs-Invariante (§5.9 Pkt. 5 / AD-5 / FT-6) als HARDE Assertion je lauffaehigem # Run; Frontmatter-Konformitaet (Vertrag §3.3/§3.4-Subset) je erzeugtem/aktualisiertem Concept. # Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum. set -u ROOT=$(mktemp -d /tmp/sb37-XXXXXX) SB="$ROOT/sb" mkdir -p "$SB/wiki" "$SB/raw" cd "$SB" git init -q # Determinismus vs. Host-Git-Konfiguration (AD-17h): die Sandbox erzeugt LF-Blobs und # LF-Worktree — autocrlf/filemode-Umwandlung des Hosts wuerde sha256-Vergleiche und # Diff-/Probe-Ergebnisse wort-wirksam verschieben. Repo-Reparatur schliesst das aus. git config core.autocrlf false git config core.filemode false git config user.email "sandbox@test" git config user.name "Sandbox" # ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit = Merge-Base) ---------- # Mini-Bundle mit zwei Root-Concepts (alpha als Mutations-Objekt, gamma als Kontrolle). cat > wiki/index.md <<'EOF' # Index - [Alpha](alpha.md) - [Gamma](gamma.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 definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1). Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2). 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 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. ### S-2 Evidenz v1: ausschließlich lokale Netze. EOF cat > raw/gamma-v1.md <<'EOF' ### S-1 Evidenz v1: Gamma-Thema. EOF git add -A git commit -qm "Baseline" BASE=$(git rev-parse HEAD) echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE" echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)" echo # ---------- Helfer: Run-Label / Isolation (kein Carry-over zwischen Szenarien) ---------- runlabel() { echo; echo "########## $1 ##########"; } isolate() { git checkout -qf -B "$1" "$BASE" git reset -q --hard "$BASE" git clean -qfd wiki raw lease registry scratch plan-run } # Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6): Baseline-Diff + porcelain, # normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert). 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) ---------- inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß (msg stderr) local expected="$1" p u bad=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 for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||') case " $expected " in *" $u "*) ;; *) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (§5.9 Pkt. 5)" >&2; bad=1;; esac done return $bad } assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1 if inv_viol "$1"; then echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff" else exit 1 fi } # ---------- Zustands-Restaurations-Invariante (§5.13 Pkt. 3) — harte Assertion ---------- # Nach Abbruch/FAIL muss der Post-Rollback-Diff gg. die Baseline LEER sein (Bundle == # Baseline, SHA-256 byte-identisch je Pfad) — die git-ueberpruefbare Konsistenz-Eigenschaft. assert_restored() { # $1=komma-oder-leer-getrennte wiki-Pfade, die byte-identisch zur Baseline sein müssen local p expected="" [ -z "$1" ] || for p in $1; do expected="$expected $p.md"; done # 1) git-Diff gg. Baseline: keine modifizierte/geloeschte Datei if [ -n "$(git diff --name-only "$BASE" -- wiki/)" ]; then echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): Post-Rollback-Diff gg. Baseline NICHT leer:" >&2 git diff --name-only "$BASE" -- wiki/ | sed 's/^/ /' >&2 exit 1 fi # 2) porcelain: keine ungetrackten/gestagten Eintraege im wiki/-Scope if [ -n "$(git status --porcelain -- wiki/)" ]; then echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): ungetrackte/gestagte Reste im wiki/-Scope:" >&2 git status --porcelain -- wiki/ | sed 's/^/ /' >&2 exit 1 fi # 3) byte-identisch zur Baseline (SHA-256); Neu-Anlage-Randfall (Pfad nur im Run angelegt): # existiert der Pfad NICHT im Baseline-Commit, darf er nach dem Rollback auch nicht in der # Working-Copy existieren (statt eines spurious byte-Vergleichs). for p in $expected; do if git cat-file -e "$BASE:wiki/$p" 2>/dev/null; then [ -f "wiki/$p" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p fehlt (muss == Baseline sein)" >&2; exit 1; } bsha=$(git show "$BASE:wiki/$p" | sha256sum | cut -d' ' -f1) csha=$(sha256sum "wiki/$p" | cut -d' ' -f1) [ "$bsha" = "$csha" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p weicht von Baseline ab ($csha != $bsha)" >&2; exit 1; } else # Neu-Anlage-Pfad (nicht im Baseline-Commit): nach Rollback darf er NICHT existieren. [ ! -e "wiki/$p" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p ist Neu-Anlage-Pfad und muss nach Rollback NICHT existieren" >&2; exit 1; } fi done echo "RESULT: PASS — Zustands-Restaurations-Invariante: Post-Rollback-Diff gg. Baseline leer, Bundle == Baseline (SHA-256 byte-identisch)" } # ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5; Muster Sandbox-3-6) ---------- assert_frontmatter() { local f="$1"; shift 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 if (seenk[k]++) print "DUP_KEY:" k 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 && r0) 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)" } # ---------- log.md-Eintraege (§5.13; Header-Bullet-Paar je Eintrag, datumsgruppiert) ---------- # Datumsgruppen-/Reihenfolge-Konvention (Vertrag §5, neueste zuerst): ein neuer Bullet wird # in die BESTEHENDE Datumsgruppe eingefuegt — nach deren Header, vor etwaigen Gruppen-Bullets — # sonst wird ein neuer Header oben erzeugt. Deterministisch (kein Wanduhr). log_bullet() { # $1 = Bullet-Text (eine Zeile, printf-% escaped); Datumsgruppe/Reihenfolge (neueste zuerst) local day="2026-08-20" # getrennt gesetzt — set -u: gleiche-local-Zeilen-RHS darf day nicht vor Setzung referenzieren local header="## $day" line="$1" if grep -qxF "$header" wiki/log.md; then tmp=$(mktemp) awk -v h="$header" -v l="$line" ' BEGIN{p=0} { if (!p && $0==h) { print; print l; p=1; next } print } ' wiki/log.md > "$tmp" && mv "$tmp" wiki/log.md else tmp=$(mktemp) { printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" && mv "$tmp" wiki/log.md fi } log_plan() { # $1=plan-text (eine Zeile) log_bullet "- Änderungsplanung (P2-Block, §5.9 Pkt. 6 / §5.13 Pkt. 1): $1 (Determinismus AD-17h/A0-19; Baseline $BASE)" } log_abort() { # $1=benannte Abbruch-Ursache — NFR-4-textuelle Benennung (echo). Für einen # FAILED-Run wird log.md NICHT persistent beschrieben: §6 Pkt. 3 / §5.3 rollen auch die # log.md-Einträge als Teilzustand zurück (Bundle nimmt Zustand vor dem Run wieder ein). echo "NFR-4-Benennung (textuell): Mutations-Abbruch (§5.13 Pkt. 3/4) — $1; Zustands-Restaurations-Invariante (Pkt. 3), kein Teilerfolg committet (AD-17f); log.md-Einträge des Runs werden mit dem Teilzustand zurückgerollt (§6 Pkt. 3 / §5.3)" } log_commit() { # $1=plan-vs-ist-diff-befund log_bullet "- Validierung SUCCESS (§5.13 Pkt. 6): Mutationen als Ganzes committet; $1 (§6-Pkt.-4-Nachweis, kein Wanduhr-Trigger A0-20)" } # ---------- Reason/Mutate-Helfer (§5.13) ---------- # P2-Block (§5.9 Pkt. 6) als Änderungsplanung: capturet den konsistenten Plan als textuelles # Variablen-Set (Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur- # Erhaltung) — deterministisch aus dem committeten Git-State (AD-17h/A0-19). p2_plan() { # $1=komma-Liste Ziel-Pfade (OKF-ohne-.md; Neu-Anlage = +name) $2=komma-Liste raw-Quellen (Pfad) local targets="$1" srcs="$2" p s np err=0 # (1) Input-Zustand (AD-17a): Working-Copy von raw/ und wiki/ gegen HEAD — uncommittet = Abbruch # (Scope exakt wie Instruktion §5.9 Pkt. 6 Element (1): raw/ und wiki/; Review-Loop-2-D3: # unscoped ganzer-Tree-Prüfung wäre straffer als die Instruktion — die Demo folgt der Instruktion.) if [ -n "$(git status --porcelain -- raw wiki)" ]; then echo "HARD-FAIL (Änderungsplanung, P2-Block-Element (1): Input-Zustand AD-17a): uncommittete Zwischenstaende als Input — Abbruch vor der Mutation (published/committed Input erforderlich)" >&2 return 1 fi # (2) Ziel-Pfade (Kandidatenliste, §3 Pkt. 2) [ -n "$targets" ] || { echo "HARD-FAIL (Änderungsplanung): leere Ziel-Pfad-Menge (P2-Element (2))" >&2; return 1; } # (3) Quellen-Existenz (EC-1) for s in ${srcs//,/ }; do [ -f "raw/$s" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (3): Quellen-Existenz EC-1): raw/$s existiert nicht" >&2; return 1; } done # (4) Betroffenheit (§3 Pkt. 2, textuell-deterministisch nach §3.2): bestehende Kandidaten # oder geplante Neu-Anlage-Zielpfade (+Prefix; §5.13 Pkt. 2 "∪ Neu-Anlage") — Duplikat-Kontrolle: # ein +Pfad, der bereits existiert, ist kein Neu-Anlage. for p in ${targets//,/ }; do case "$p" in +*) np="${p#+}" [ -f "wiki/$np.md" ] && { echo "HARD-FAIL (Änderungsplanung, P2-Element (4)): Neu-Anlage-Pfad wiki/$np.md existiert bereits (Duplikat — kein Neu-Anlage, §5.10 Pkt. 8)" >&2; return 1; } ;; *) [ -f "wiki/$p.md" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (4)): Betroffenheits-Kandidat wiki/$p.md fehlt (und kein Neu-Anlage-Zielpfad +name)" >&2; return 1; } ;; esac done # (5) wiki/index.md-V-1 (Vertrag §2) [ -f "wiki/index.md" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (5)): fehlende Bundleroot — Run-FAIL (V-1)" >&2; return 1; } # (6) Struktur-Erhaltungs-Check (Vertrag §3.3/§3.4-Subset) je betroffenem Concept for p in ${targets//,/ }; do if [ -f "wiki/$p.md" ]; then v=$(awk ' /^---$/{n++; if(n==2) exit; next} /^[A-Za-z_][A-Za-z0-9_-]*:/{ k=$0; sub(/:.*/,"",k) if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") { print k; exit } }' "wiki/$p.md") [ -z "$v" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (6): Struktur-Erhaltung): unbefugter Key '$v' in wiki/$p.md" >&2; return 1; } fi done echo "PLAN_OK targets=($targets) sources=($srcs)" return 0 } # Plan-Freeze (Veränderungs-Sperre, §5.13 Pkt. 2): nach Abschluss der Planung darf kein # Plan-Gegenstand ausserhalb der erlaubten Menge mutiert werden — eine Ziel-Pfad-Verletzung # wird erkannt und verweigert (Ghost-Diff-Kopplung §5.9 Pkt. 5). freeze_check() { # $1=komma-Liste der im Run geplanten Ziel-Pfade (ok + neu.log + index) local allowed=" $1 " local m # Normalisierung (wie §5.9 Pkt. 5-Probe): wiki/-Praefix + .md-Suffix strippen (beide Stromarten). for m in $( { 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); do case "$allowed" in *" $m "*) ;; *) echo "HARD-FAIL (Plan-Freeze §5.13 Pkt. 2 / Ghost-Diff §5.9 Pkt. 5): Mutation ausserhalb der erlaubten Pfad-Menge — '$m' ist nicht im Plan ($allowed)" >&2; return 1;; esac done return 0 } # Rollback der Mutationsphase (Zustands-Restaurations-Invariante, §5.13 Pkt. 3): stellt die # Baseline wieder her — §5.3-Pkt.-3/§6-Pkt.-3-Mechanik + Ghost-Diff-Rollback §5.9 Pkt. 5. # (Review-Loop-2-D5: der Voll-Rollback rollt den TEILZUSTAND DES RUNS komplett zurück (§6 Pkt. 3: # "das Bundle nimmt seinen Zustand vor dem Run wieder ein") — in der Sandbox sind alle # ungetrackten wiki/-Dateien Lauf-eigener Teilzustand; es existieren keine fremden uncommitteten # Änderungen (AD-17e-Schutzobjekt). Ein realer Run schützt fremde Änderungen zuvor per # Stash/Scratch-Zone (§5.11 Pkt. 3), bevor ein Rollback greift.) rollback() { git checkout -q "$BASE" -- wiki/ 2>/dev/null || true untracked_rest=$(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}') if [ -n "$untracked_rest" ]; then echo "ROLLBACK: ungetrackte Reste im wiki/-Scope bei Rollback entdeckt:" >&2 echo "$untracked_rest" | sed 's/^/ /' >&2 # Ghost-Diff-Rollback §5.9 Pkt. 5: ungetrackte neue Dateien ausserhalb der Ziel-Pfade loeschen for u in $untracked_rest; do echo "ROLLBACK (Ghost-Diff-Rollback §5.9 Pkt. 5): entferne ungetrackte neue Datei '$u' ausserhalb erlaubter Menge" rm -f "$u" done fi git reset -q --hard "$BASE" git clean -qfd wiki } # ---- CONSIST-1: KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7; §5.13 Pkt. 1) ---- runlabel "CONSIST-1: KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7) — der P2-Block (§5.9 Pkt. 6) erzeugt aus committetem Input eine konsistente Änderungsplanung (Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-Erhaltung); textuelle Capture als Plan; Plan-Defizit (uncommitteter Input) ⇒ Abbruch VOR der Mutationsphase" isolate consist1 echo "--- Neue committete Evidenz als Input (AD-17a): raw/alpha-v2.md ---" cat > raw/alpha-v2.md <<'EOF' ### S-3 Evidenz v2: Alpha erweitert um eine synchrone Kopplung (Story 3.7-Plan). EOF git add -A && git commit -qm "Evidenz v2" echo "--- Änderungsplanung (P2-Block als Phase): p2_plan alpha raw/alpha-v2.md ---" PLAN1=$(p2_plan "alpha" "alpha-v2.md") || exit 1 echo "$PLAN1" echo "--- Determinismus der Planung (AD-17h/A0-19): zweite Planung desselben committeten States (VOR jedem Run-Mutationsschritt) ---" PLAN2=$(p2_plan "alpha" "alpha-v2.md") || exit 1 [ "$PLAN1" = "$PLAN2" ] || { echo "HARD-FAIL (CONSIST-1): Plan ungleich bei gleichem Git-State (AD-17h/A0-19)" >&2; exit 1; } echo "PLAN2 identisch: $PLAN2" echo "--- Plan textuell festgehalten (log.md-Teil der Planung, kein Artefakt-File) ---" log_plan "alpha betroffen via raw/alpha-v2.md (Ziel-Pfade {alpha}, Quellen-Existenz EC-1 ok, Struktur-Erhaltung ok)" grep -qF 'Änderungsplanung (P2-Block' wiki/log.md || { echo "HARD-FAIL (CONSIST-1): Plan-Nachweis fehlt in log.md (§5.13 Pkt. 1)" >&2; exit 1; } echo "--- Plan-Defizit-Kette (I/O-Matrix PLAN_BEABSICHTIGT): uncommitteter Input ⇒ Abbruch VOR der Mutation ---" echo "uncommitteter Rest im raw/" > raw/scratch-deficit.txt if p2_plan "alpha" "alpha-v2.md" >/dev/null 2>&1; then echo "HARD-FAIL (CONSIST-1): uncommitteter Input wurde nicht als Plan-Defizit abgefangen (P2-Element (1), AD-17a)" >&2 exit 1 else echo "BEFUND: Plan-Defizit textuell benannt (NFR-4) — Abbruch vor der Mutationsphase, keine Mutation" fi rm -f raw/scratch-deficit.txt echo "--- Plan-Defizit-Kette (I/O-Matrix PLAN_BEABSICHTIGT): fehlende Quellen-Existenz (P2-Element (3), EC-1) ⇒ Abbruch VOR der Mutation ---" # (Review-Loop-2-Patch: die Löschung wird COMMITTET — eine uncommittierte Löschung würde # Element (1) (uncommitteter Input) zuerst feuern und die EC-1-Kontrolle nicht isolieren; # im committeten Zustand ist Element (3) der einzige Trigger.) git rm -q raw/alpha-v2.md && git commit -qm "Quelle entfernt" if p2_plan "alpha" "alpha-v2.md" >/dev/null 2>&1; then echo "HARD-FAIL (CONSIST-1): fehlende Quelle wurde nicht als Plan-Defizit abgefangen (P2-Element (3), EC-1)" >&2 exit 1 else echo "BEFUND: Plan-Defizit (EC-1: raw/alpha-v2.md fehlt) textuell benannt (NFR-4) — Abbruch vor der Mutationsphase, keine Mutation" fi echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5): nur log.md ---"; probe assert_invariant "log" echo "RESULT: PASS — CONSIST-1: konsistente Änderungsplanung aus committetem Input (P2-Block, §5.9 Pkt. 6 / §5.13 Pkt. 1); textuelle Capture; Determinismus (AD-17h/A0-19); Plan-Defizit ⇒ Abbruch vor der Mutation (PLAN_BEABSICHTIGT; P2-Element (1) AD-17a isoliert [untracked Datei] + P2-Element (3) EC-1 isoliert [committete Löschung]); Erhaltungs-Invariante" # ---- CONSIST-2: PLAN_FREEZE (AC-1; §5.13 Pkt. 2) ---- runlabel "CONSIST-2: PLAN_FREEZE (§5.13 Pkt. 2) — Plan-Freeze = Veränderungs-Sperre: eine Mutation ausserhalb der Plan-Pfadmengen (Kandidatenliste ∪ Neu-Anlage ∪ log.md ∪ index.md) wird erkannt und verhindert (Ghost-Diff-Kopplung §5.9 Pkt. 5); Misch-Run (Update + Neu-Anlage): der geplante Neu-Anlage-Pfad ist erlaubter Teil der Plan-Menge (Review-Loop-2-Patch)" isolate consist2 cat > raw/alpha-v2.md <<'EOF' ### S-3 Evidenz v2: Alpha erweitert um eine synchrone Kopplung. ### S-4 Evidenz v2: Delta-Thema — bisher nicht im Bundle repraesentiert. EOF git add -A && git commit -qm "Evidenz v2" p2_plan "alpha,+delta" "alpha-v2.md" >/dev/null || exit 1 log_plan "alpha betroffen + delta neu angelegt via raw/alpha-v2.md (Plan-Freeze aktiv, Misch-Run)" echo "--- Mutationsphase: geplante Mutation von alpha — erlaubt (Plan-Ziel-Pfad) ---" cat >> wiki/alpha.md <<'EOF' Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3). EOF grep -q "synchrone Kopplung" wiki/alpha.md || { echo "HARD-FAIL (CONSIST-2): geplante Mutation fehlgeschlagen" >&2; exit 1; } echo "--- Mutationsphase: geplante Neu-Anlage von delta — erlaubt (Neu-Anlage im Plan, §5.13 Pkt. 2) ---" cat > wiki/delta.md <<'EOF' --- type: concept sources: - resource: raw/alpha-v2.md id: s1 generated: by: wow-compiler/0.1.0 at: 2026-08-20T00:00:00Z --- Delta-Thema (raw/alpha-v2.md#S-4). EOF [ -f wiki/delta.md ] || { echo "HARD-FAIL (CONSIST-2): geplante Neu-Anlage fehlgeschlagen" >&2; exit 1; } echo "--- Freeze-Verletzung simulieren: Ghost-Mutation auf NICHT geplanten Pfad gamma (Plan-Gegenstand ausserhalb der erlaubten Menge) ---" echo "unbefugte gamma-Aenderung" >> wiki/gamma.md if freeze_check "alpha delta log index"; then echo "HARD-FAIL (CONSIST-2): Mutation ausserhalb der Plan-Pfadmengen wurde nicht erkannt (Plan-Freeze, §5.13 Pkt. 2)" >&2 exit 1 else echo "BEFUND: Freeze-Verletzung erkannt (ghost gamma) — Veränderungs-Sperre greift" fi echo "--- Ghost-Diff-Rollback (§5.9 Pkt. 5): gamma aus Baseline-Zustand wiederherstellen, textuell benannt (NFR-4) ---" git checkout -q "$BASE" -- wiki/gamma.md echo "NFR-4-Nennung: Ghost-Diff auf 'gamma' (ausserhalb Plan-Menge) zurueckgerollt — textuell benannt" freeze_check "alpha delta log index" || exit 1 echo "--- Voll-Rollback (Abbruch des Runs): Teilzustand {alpha, delta, log} wird komplett zurückgerollt (Zustands-Restaurations-Invariante) ---" rollback assert_restored "alpha gamma index log delta" echo " BEFUND: Neu-Anlage-Pfad delta existiert nach Rollback NICHT (assert_restored-Neu-Anlage-Guard) — Misch-Run-Teilerfolg ist vollständig rückgerollt" echo "RESULT: PASS — CONSIST-2: Plan-Freeze = Veränderungs-Sperre (erlaubte Menge {alpha, delta (Neu-Anlage), log, index}); Verletzung (gamma) erkannt + zurueckgerollt (Ghost-Diff-Rollback §5.9 Pkt. 5); Neu-Anlage-Pfad im Plan ist erlaubter Teil der Menge; Misch-Run-Voll-Rollback: Neu-Anlage nach Rollback nicht existent (Zustands-Restaurations-Invariante); Erhaltungs-Invariante" # ---- CONSIST-3: ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2; §5.13 Pkt. 3) ---- runlabel "CONSIST-3: ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2) — fehlgeschlagene Mutation rollt auf exakte Baseline zurueck (Post-Rollback-Diff leer, SHA-256 byte-identisch); Bundle == Baseline; raw/ unverändert (AD-3)" isolate consist3 raw_sha_before=$(sha256sum raw/alpha-v1.md | cut -d' ' -f1) 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" echo "--- Planung (konsistent) + Mutationsphase: Teilzustand wird geschrieben (alpha erweitert), dann bricht die Mutation AB (Fehler) ---" p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1 log_plan "alpha betroffen via raw/alpha-v2.md" cat >> wiki/alpha.md <<'EOF' Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3). EOF echo " (Teilzustand geschrieben: alpha erweitert)" echo "--- Mutations-Abbruch: Rollback (Zustands-Restaurations-Invariante §5.13 Pkt. 3; §5.3-Pkt.-3-/§6-Pkt.-3-Rollback, Ghost-Diff-Rollback §5.9 Pkt. 5) ---" rollback log_abort "Fehler waehrend der Mutationsphase (alpha-Teilzustand)" echo "--- KERN-ASSERTION: Post-Rollback-Diff leer, Bundle == Baseline (SHA-256 je Pfad) ---" assert_restored "alpha gamma index log" echo "--- raw/ unverändert (AD-3) ---" [ "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" = "$raw_sha_before" ] || { echo "HARD-FAIL (CONSIST-3): raw/ verändert (AD-3)" >&2; exit 1; } [ -f raw/alpha-v1.md ] || { echo "HARD-FAIL (CONSIST-3): raw/alpha-v1.md fehlt (AD-3)" >&2; exit 1; } echo "RESULT: PASS — CONSIST-3: fehlgeschlagene Mutation rollt auf exakte Baseline zurueck (Post-Rollback-Diff leer, SHA-256 byte-identisch, Bundle == Baseline); raw/ unverändert (AD-3); Zustands-Restaurations-Invariante erzwungen" # ---- CONSIST-4: MUTATION_ABBRUCH (AC-2; §5.13 Pkt. 3/4) ---- runlabel "CONSIST-4: MUTATION_ABBRUCH (AC-2) — Abbruch nach Teilerfolg (mehrere geplante Mutationen, nur ein Teil ausgefuehrt) hinterlaesst KEINEN Teilerfolg als fertige Mutation (Commit-Boundary = Mutations-Boundary, AD-17f); raw/ unberührt" isolate consist4 cat > raw/alpha-v2.md <<'EOF' ### S-3 Evidenz v2: Alpha erweitert um eine synchrone Kopplung. EOF cat > raw/gamma-v2.md <<'EOF' ### S-1 Evidenz v2: Gamma schaerft seine Abgrenzung. EOF git add -A && git commit -qm "Evidenz v2" echo "--- Plan: ZWEI Ziel-Pfade (alpha, gamma) — Mutationsphase fuehrt nur alpha aus, dann Abbruch ---" p2_plan "alpha,gamma" "alpha-v2.md,gamma-v2.md" >/dev/null || exit 1 log_plan "alpha,gamma betroffen (zwei geplante Mutationen)" PRE_MUT_HEAD=$(git rev-parse HEAD) echo " (HEAD vor der Mutationsphase = $PRE_MUT_HEAD; Evidenz-Commit ist Input, kein Run-Mutations-Commit)" cat >> wiki/alpha.md <<'EOF' Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3). EOF echo " (alpha mutiert; gamma geplant aber NOCH NICHT ausgefuehrt — Abbruch)" echo "--- Kein Teilerfolg darf als fertige Mutation veröffentlicht werden (AD-17f): nicht committen, zurueckrollen ---" if [ "$(git log --oneline -1 --format='%H')" != "$PRE_MUT_HEAD" ]; then echo "HARD-FAIL (CONSIST-4): es wurde ein Commit während des Abbruchs erzeugt (Commit-Boundary verletzt, AD-17f)" >&2 exit 1 else echo " BEFUND: kein Commit während der Teilfolge (HEAD == $PRE_MUT_HEAD; Commit-Boundary = Mutations-Boundary gewahrt)" fi rollback log_abort "Abbruch nach Teilerfolg (alpha ausgefuehrt, gamma nicht) — kein Teilerfolg committet" echo "--- KERN-ASSERTION: Post-Rollback-Diff leer, Bundle == Baseline (kein Teilerfolg sichtbar) ---" assert_restored "alpha gamma index log" echo "--- raw/ unberührt (AD-3): Baseline-raw-Dateien byte-identisch nach Rollback ---" # Setup-Existenz-Assert: die Baselines müssen im Baseline-Commit existieren, sonst wäre der # SHA-Vergleich leerer-Input (der stille `|| sha256sum...`-Fallback wäre toter Pfad — kein pipefail). git cat-file -e "$BASE:raw/alpha-v1.md" || { echo "HARD-FAIL (Setup): raw/alpha-v1.md fehlt im Baseline-Commit" >&2; exit 1; } git cat-file -e "$BASE:raw/gamma-v1.md" || { echo "HARD-FAIL (Setup): raw/gamma-v1.md fehlt im Baseline-Commit" >&2; exit 1; } rawv1_a=$(git show "$BASE:raw/alpha-v1.md" | sha256sum | cut -d' ' -f1) rawv1_g=$(git show "$BASE:raw/gamma-v1.md" | sha256sum | cut -d' ' -f1) [ "$rawv1_a" = "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-4): raw/alpha-v1.md verändert (AD-3)" >&2; exit 1; } [ "$rawv1_g" = "$(sha256sum raw/gamma-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-4): raw/gamma-v1.md verändert (AD-3)" >&2; exit 1; } echo " BEFUND: raw/alpha-v1.md + raw/gamma-v1.md == Baseline-SHA (der Run hat raw/ nicht berührt; die Evidenz-Input-Commit-V2-Dateien sind Input, kein Baseline-/AD-3-Gegenstand)" echo "RESULT: PASS — CONSIST-4: MUTATION_ABBRUCH nach Teilerfolg — kein Teilerfolg als fertige Mutation veröffentlicht (Commit-Boundary = Mutations-Boundary, AD-17f); Rollback stellt Baseline wieder her; Zustands-Restaurations-Invariante; raw/ unberührt (AD-3)" # ---- CONSIST-5: VALIDATION_FAIL (AC-2; §5.13 Pkt. 5) ---- runlabel "CONSIST-5: VALIDATION_FAIL (AC-2) — §6-Pkt.-3-Rollback bei Validierungs-FAIL: keine weiteren Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante); raw/ unverändert (AD-3)" isolate consist5 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" p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1 log_plan "alpha betroffen via raw/alpha-v2.md" echo "--- Mutationsphase: alpha wird mutiert — aber mit Frontmatter-Verstoß (unbefugter Key foo:) ---" cat >> wiki/alpha.md <<'EOF' Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3). EOF printf '\nfoo: bar\n' >> wiki/alpha.md echo "--- Validierungsphase (§6): FAIL (Struktur-Verstoß, unbefugter Key) -> §6-Pkt.-3-Rollback, KEINE weiteren Mutationen ---" if grep -q '^foo:' wiki/alpha.md; then echo " Validator-BEFUND: FAIL (unbefugter Key 'foo:', Punkt 6-Struktur-Verstoß) — textuell benannt (NFR-4)" else echo "HARD-FAIL (CONSIST-5): Validator-Verstoß nicht erkennbar gemacht" >&2; exit 1 fi rollback log_abort "Validierungs-FAIL (unbefugter Key foo: in alpha) — §6-Pkt.-3-Rollback, keine weiteren Mutationen" echo "--- KERN-ASSERTION: Endzustand konsistent (Baseline wiederhergestellt) ---" assert_restored "alpha gamma index log" # (Review-Loop-2-Patch: gg. den Baseline-Commit vergleichen — der Selbstvergleich wäre # tautologisch und bewiese nichts; Muster CONSIST-4.) rawv1_b=$(git show "$BASE:raw/alpha-v1.md" | sha256sum | cut -d' ' -f1) [ "$rawv1_b" = "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-5): raw/ verändert (AD-3)" >&2; exit 1; } echo "RESULT: PASS — CONSIST-5: VALIDATION_FAIL — §6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante); raw/ unverändert (AD-3)" # ---- CONSIST-6: VALIDATION_SUCCESS (AC-3; §5.13 Pkt. 6) ---- runlabel "CONSIST-6: VALIDATION_SUCCESS (AC-3) — Mutationen als Ganzes committet (erst nach Diff-Selbsttest ohne Ghost-Diff, §5.9 Pkt. 5); Diff gg. Plan-Menge gedeckt; §6-Pkt.-4-Nachweis; kein Wanduhr-Trigger (A0-20)" isolate consist6 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" p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1 log_plan "alpha betroffen via raw/alpha-v2.md" echo "--- Mutationsphase: geplante Mutation von alpha (regelkonform) — §5.9-Pkt.-2-Update-Form: Body-Erweiterung + sources-Zuwachs + generated.at-Bump ---" cat > wiki/alpha.md <<'EOF' --- type: concept sources: - resource: raw/alpha-v1.md id: s1 - resource: raw/alpha-v2.md id: s3 generated: by: wow-compiler/0.1.0 at: 2026-08-20T00:00:00Z --- Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1). Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2). Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3). EOF echo "--- Diff-Selbsttest VOR dem Commit (§5.9 Pkt. 5): {alpha, log} — kein Ghost-Diff ---"; probe assert_invariant "alpha log" echo "--- Plan-Freeze-Check (§5.13 Pkt. 2): Diff gg. Plan-Menge gedeckt ---" freeze_check "alpha log index" || exit 1 echo "--- Validierungsphase (§6): SUCCESS -> §6-Pkt.-4-Nachweis und SUCCESS-Bullet VOR dem Commit (§5.13 Pkt. 6) ---" echo "--- §6-Pkt.-4-Nachweis: Verdikt je Datei SUCCESS (frontmatter-Konformität) ---" assert_frontmatter wiki/alpha.md "raw/alpha-v1.md" "raw/alpha-v2.md" echo " (§5.13-demo: der committete Zustand ist gültig — alpha frontmatter-konform (kein unbefugter Key, generated.at volles Datetime), sources-Nachführung der Update-Evidenz vollzogen (§5.9 Pkt. 2); der Body zitiert raw/alpha-v2.md#S-3 nur mit existierendem sources-Eintrag (EC-1/Punkt-3-konform))" log_commit "Diff gg. Plan-Menge gedeckt ({alpha, log}), kein Ghost-Diff" echo "--- Mutationsphase zu Ende: Mutationen als Ganzes committen (§5.13 Pkt. 6, Commit-Boundary AD-17f) — Commit enthält alpha + log.md mit Plan- und SUCCESS-Bullets ---" git add -A && git commit -qm "Run: alpha-Update (Story 3.7 CONSIST-6)" grep -qF 'Änderungsplanung (P2-Block' wiki/log.md || { echo "HARD-FAIL (CONSIST-6): Plan-Nachweis fehlt (Pkt. 1)" >&2; exit 1; } grep -qF 'Validierung SUCCESS' wiki/log.md || { echo "HARD-FAIL (CONSIST-6): §6-Pkt.-4-Nachweis fehlt (Pkt. 6)" >&2; exit 1; } echo "--- kein Wanduhr-Trigger (A0-20): der Commit folgt aus Phasenabschluss, nicht aus Kalenderzeit ---" echo "RESULT: PASS — CONSIST-6: VALIDATION_SUCCESS — Mutationen als Ganzes committet (nach Diff-Selbsttest ohne Ghost-Diff); Diff gg. Plan-Menge gedeckt; §6-Pkt.-4-Nachweis; Aktivierungs-Befund zu Wanduhr-Trigger siehe CONSIST-7" # ---- CONSIST-7: DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4; §5.13 Pkt. 7) ---- runlabel "CONSIST-7: DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4, AD-17h/A0-19, D-3/AD-11) — zwei identische Runs -> identische Plan-/Rollback-/State-Outputs; die Phasen-Trennung ist logisch in einer Session — keine Hintergrundprozesse/Server/MCP (grep-Negativkontrolle)" echo "--- KEINE_ENGINE-Negativkontrolle: der Run startet keinen Prozess/Server/MCP (nur Shell/Git/Datei, D-3/AD-11) ---" # Robustheits-Guard: pgrep bevorzugt, sonst ps-basierte Negativkontrolle (portabel, nicht-vakuum). if command -v pgrep >/dev/null 2>&1; then engine_proc=$(pgrep -f "wow-flow-engine|wow-server|wow-mcp" 2>/dev/null || true) else command -v ps >/dev/null 2>&1 || { echo "HARD-FAIL (KEINE_EIGENE_ENGINE): weder pgrep noch ps verfügbar — Negativkontrolle kann nicht greifen" >&2; exit 1; } engine_proc=$(ps aux 2>/dev/null | grep -E "wow-flow-engine|wow-server|wow-mcp" | grep -v "grep" || true) fi if [ -n "$engine_proc" ]; then echo "HARD-FAIL (KEINE_EIGENE_ENGINE §5.13 Pkt. 7 / D-3 / AD-11): ein Workflow-Engine-Prozess wurde gestartet" >&2 echo "$engine_proc" | sed 's/^/ /' >&2 exit 1 else echo " BEFUND: keine Workflow-Engine-Prozesse (wow-flow-engine/wow-server/wow-mcp) — keine eigene Engine (AC-4)" fi consist7_run() { # deterministischer Run auf isoliertem Zustand (identische Plan-/Rollback-/State-Outputs) local branch="$1" isolate "$branch" 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" local plan cap_commit plan=$(p2_plan "alpha" "alpha-v2.md") || return 99 # Plan-Capture-Disclaimer (BH-13): plan-run.capture ist NUR ein deterministischer Sandbox-Zeuge # für den Zwei-Run-Vergleich — NICHT Teil der §5.13-Ausführungsdisziplin (die Instruktion # verlangt laut Ask-First kein Plan-Artefakt-File; „kein Artefakt-File" bleibt die Norm). echo "$plan" > plan-run.capture git add -A && git commit -qm "Plan-Capture" cap_commit=$(git rev-parse HEAD) # Rollback-Pfad: Teilzustand schreiben + Abbruch + Rollback (Zustands-Restaurations-Invariante) cat >> wiki/alpha.md <<'EOF' Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3). EOF rollback >/dev/null # Plan-Capture aus dem committeten Baum lesen: rollback () resettet auf BASE und entfernt so # die Arbeitskopie von plan-run.capture (nur im Plan-Capture-Commit getrackt) — der committete # Baum bleibt als deterministischer Zeuge erhalten (AD-17h/A0-19). plan_state=$(git show "$cap_commit:plan-run.capture") plan_sha=$(git show "$cap_commit:plan-run.capture" | sha256sum | cut -d' ' -f1) # log_sha nach Rollback ist definitionsgemäß die Baseline-Konstante (leerer Indikator — die # Baseline-log.md wird gegen sich selbst gemessen); Determinismus-Zeuge ist plan_sha/plan_state # aus dem committeten Plan-Capture (non-vacuous). echo "state plan=[$plan_state] plan_sha=$plan_sha" } echo "--- Zwei identische Runs (gleicher Baum-Input) ---" S7A=$(consist7_run s7a) echo "Run A: $S7A" S7B=$(consist7_run s7b) echo "Run B: $S7B" [ "$S7A" = "$S7B" ] || { echo "HARD-FAIL (CONSIST-7): Plan-/State-Outputs zweier identischer Runs weichen ab (AD-17h/A0-19, §5.13 Pkt. 1/7)" >&2; exit 1; } echo "--- Zustands-Restaurations-Invariante im Zwei-Run-Pfad (Rollback >> Baseline leer) ---" isolate s7c [ "$(inv_set)" = "" ] || { echo "HARD-FAIL (CONSIST-7): kein sauberer Zustand nach Rollback erwartet (leer)" >&2; exit 1; } echo "RESULT: PASS — CONSIST-7: Zwei identische Runs -> identische Plan-/Rollback-/State-Outputs (AD-17h/A0-19); keine Workflow-Engine gestartet (D-3/AD-11, KEINE_EIGENE_ENGINE §5.13 Pkt. 7, AC-4)" echo echo "===== Sandbox abgeschlossen (CONSIST-1..CONSIST-7) =====" echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"