#!/usr/bin/env bash # Story 3.8 — Sandbox-Tests des Determinismus-Vertrags (AD-17h/FT-10/A0-19) als # Agent-Instruktions-Validator (§5.14, Revision 3.3) # Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb38) # Zweck: die deterministische Bestätigungs-Mechanik (§5.14) als re-executierbarer # Run-Demonstrator durchspielen — # DET-1 BUNDLE_STATE_DEFINITION: ein Mini-Bundle erzeugen und die Bundle-State- # Projektion deterministisch definieren/extrahieren (wiki/-Dateien + Plan-/ # Kandidaten-/Reihenfolge-Outputs; Extraktion zweimal -> identisch) # DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum, zwei getrennte Worktrees): zwei frische # Agent-Kontexte in getrennt aufgebauten, sauberen Worktrees über demselben # committeten Baum -> identische Bundle-States bis auf at (Q-6/A0-19: eine zweite # Ausführung in derselben Session genügt nicht); kanonisches Eingabemanifest + # Run-Receipt (außerhalb des Bundles); keine hart codierten erwarteten Pläne/ # Concept-Bodies, kein pauschales verified-Maskieren; echte Content-Hashes # (Witness weicht byte-weise von der Baseline ab — kein Vakuum) # DET-3 ZWEI_RUN_ABWEICHUNG: künstlich divergenter Lauf -> als AD-16-Klassifikations- # defekt klassifiziert und textuell benannt (kein Rauschen); korrigierter zweiter # Lauf == erster Lauf (erneut bestätigt) # DET-4 AT_GAP_AUSNAHME: generated.at-Wanduhr-Gap ist die einzige benannte Ausnahme # (diff zwischen zwei Runs ausschließlich auf der at-Zeile; nach at-Maskierung # byte-identisch); jede andere Differenz liegt ausserhalb der Ausnahme = # AD-16-Klassifikationsdefekt # DET-5 EM_DASH: Em-Dash — kollabiert auf dieselbe canonische Form wie En-Dash/ # Bindestrich/Unterstrich/Leerzeichen (§3.2 Pkt. 1b, [-–— _] -> -) # DET-6 KOLLAPS: Läufe (a--b), führende (-x), trailende (x-) -> Kollaps auf # genau ein - + Trim (Reichweite: jedes Vorkommen -> genau ein -) # DET-7 MATCH_SCOPE: Stufe a matcht ganze Wörter über den Body, exklusive YAML- # Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen) # DET-8 ORPHAN: neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer -> unzugeordnet, # log.md-verwaist protokolliert (Datumsgruppe, ), kein Banner, # keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default) # Determinismus-Vertrag (AD-17h/A0-19) als HARDE Assertion je Szenario; KEINE_EIGENE_ENGINE- # Negativkontrolle (D-3/AD-11) in DET-1; Frontmatter-/log.md-Konformitaet (Vertrag §3.3/§3.4, §5). # Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum. set -u ROOT=$(mktemp -d /tmp/sb38-XXXXXX) SB="$ROOT/sb" mkdir -p "$SB/wiki" "$SB/raw" cd "$SB" git init -q # Determinismus vs. Host-Git-Konfiguration (AD-17h): LF-Blobs + LF-Worktree — # autocrlf/filemode-Umwandlung des Hosts wuerde sha256-Vergleiche verschieben. 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) ---------- # 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 } # ---------- Deterministische Normalisierung (§3.2 Pkt. 1b, Story 3.8) ---------- # lowercasing + Binde-Varianten-Kollaps [-–— _] -> - (En-Dash –, Em-Dash —, # Bindestrich -, Unterstrich _, Leerzeichen), Kollaps-Reichweite: jedes Vorkommen -> # genau ein -; Läufe kollabieren; führende/trailende Separatoren werden getrimmt. norm() { # $1 = Begriff (Kollaps-Form, NICHT registry-aufloesend — Kollaps-Scope isoliert) printf '%s' "$1" | sed \ -e 's|[–—]|-|g' \ -e 's|_| |g' \ -e 's| |-|g' \ -e 's|--*|-|g' \ -e 's|^-*||' \ -e 's|-*$||' \ | tr 'A-Z' 'a-z' } # Stufe-a-Match-Scope (§3.2 Pkt. 2a, Story 3.8): matcht ganze Wörter über den Body, # exklusive YAML-Frontmatter. Implementierung: Frontmatter (zwischen ersten beiden ---) # wird entfernt, danach Wortgrenzen-Match (rg mit Wortgrenzen bzw. grep '\b'). match_stufe_a() { # $1=Begriff $2=Datei-Liste ; liefert getroffene wiki-Pfade (ohne Präfix/Suffix) local term="$1" file # Regex-Metazeichen im Begriff literal-escaped (deterministischer, literal-sicherer Vergleich, # §3.2-Pkt.-2a/§5.14 Pkt. 5): ein Term wie "a+b" oder "x.y" darf nie als Muster umgedeutet werden. local esc esc=$(printf '%s' "$term" | sed 's|[][\\.*^$+?(){}|]|\\&|g') for file in "${@:2}"; do # Body = alles nach dem zweiten "---" (Frontmatter exkludiert, deterministisch); # CRLF-tolerant (Host-Checkout-Form normalisiert), sonst wäre der Wortgrenzen-Match unbestimmt. body=$(tr -d '\r' < "$file" | awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}') # Wortgrenzen-Match: ganze Wörter — Substring-/Frontmatter-Treffer liefern nichts. # Term-Normalisierung (§3.2 Pkt. 1b) ist lowercasing — der Match ist daher case-insensitiv. if printf '%s' "$body" | grep -Eiq "(^|[^A-Za-z0-9])${esc}([^A-Za-z0-9]|$)"; then basename "$file" .md fi done } # log.md-Eintrag (datumsgruppiert, neueste zuerst; Vertrag §5). Deterministisch fester Tag. # Fehler in mktemp/mv werden NICHT geschluckt: ein fehlgeschlagener Eintrag würde den # log.md-Hash beider Läufe identisch (Baseline) lassen => Vakuum-Gleichheit (sha_log). log_bullet() { # $1 = Bullet-Text (eine Zeile) local day="2026-08-20" local header="## $day" line="$1" local tmp if grep -qxF "$header" wiki/log.md; then tmp=$(mktemp) || { echo "HARD-FAIL (log_bullet): mktemp fehlgeschlagen" >&2; exit 1; } 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 || { echo "HARD-FAIL (log_bullet): log.md-Update fehlgeschlagen (Eintrag verloren)" >&2; exit 1; } else tmp=$(mktemp) || { echo "HARD-FAIL (log_bullet): mktemp fehlgeschlagen" >&2; exit 1; } { printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" \ && mv "$tmp" wiki/log.md \ || { echo "HARD-FAIL (log_bullet): log.md-Update fehlgeschlagen (Eintrag verloren)" >&2; exit 1; } fi } # ===================================================================================== # DET-1 BUNDLE_STATE_DEFINITION (§5.14 Pkt. 1) # ===================================================================================== runlabel "DET-1: BUNDLE_STATE_DEFINITION — Mini-Bundle erzeugen; Bundle-State-Projektion (wiki/-Dateien + Plan-/Kandidaten-/Reihenfolge-Outputs) deterministisch definieren/extrahieren; KEINE_EIGENE_ENGINE (D-3/AD-11)" 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" >&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 D-3/AD-11, §5.14): 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) — der Agent-Instruktions-Validator ist die Instruktion selbst (kein Prozess/Server/MCP)" fi isolate det1 # Der Run erzeugt einen definierten Bundle-State: neue committete Evidenz (Input) -> # Plan-/Kandidaten-Output -> Mutations-Commit (deterministischer Run-Ausgangszustand). 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" plan=$(printf 'cand=alpha;form=update;baseline=%s' "$BASE") 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 log_bullet "- Determinismus-Bestätigung (Bundle-State-Definition, §5.14 Pkt. 1): $plan" git add -A && git commit -qm "Run: alpha-Update (DET-1)" # Bundle-State-Extraktion (§5.14 Pkt. 1): (a) committeter Baum gg. Baseline, # (b) Kandidatenliste in Zuwachs-Sicht-Ordnung, (c) Ausführungs-Entscheidung (Form-Wahl). # Deterministische Projektion = (a)+(b)+(c) — zweimal extrahiert, byte-identisch. # Dev-Demo-Notiz: die (b)-/(c)-Kandidaten-/Form-Outputs sind für die Wiederholungs-Probe # eigentliche Run-Feststellungen (hier redaktionell verdichtet; die Tendenz "die Bundle-State # wiederholt extrahieren ist deterministisch" wird von der zweimaligen Wiederholung bewiesen, # die Ausführungs-Verdikte selbst belegen die DET-Szenarien DET-3/DET-4/DET-7). bs_a() { { 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 # Kandidatenliste (Stufe a, deterministisch; Zuwachs-Ordnung) — hier textuell festgehalten: match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md # Form-Wahl (Ausführungs-Entscheidung): alpha ist betroffen, gamma nicht echo "form=update" echo "baseline=$BASE" } B1=$(bs_a) B2=$(bs_a) [ "$B1" = "$B2" ] || { echo "HARD-FAIL (DET-1): Bundle-State-Projektion nicht deterministisch (gleiche Extraktion zweimal ungleich)" >&2; exit 1; } case "$B1" in *alpha*) ;; *) echo "HARD-FAIL (DET-1): Bundle-State-Projektion enthaelt Wiki-Pfad-Projektion nicht (alpha fehlt)" >&2; exit 1;; esac echo " Bundle-State-Projektion (deterministisch, wiederextrahierbar):" echo "$B1" | sed 's/^/ /' echo "RESULT: PASS — DET-1: BUNDLE_STATE_DEFINITION — Bundle-State = deterministische Projektion des committeten Git-States ((a) Baum gg. Baseline, (b) Kandidaten-, (c) Plan-/Reihenfolge-/Entscheidungs-Outputs); Extraktion zweimal byte-identisch (AD-17h/A0-19); keine eigene Engine (D-3/AD-11)" # ===================================================================================== # DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum, zwei getrennte Worktrees) — §5.14 Pkt. 2 # ===================================================================================== runlabel "DET-2: ZWEI_RUN_IDENTISCH (nicht-vakuum) — zwei frische Agent-Kontexte in getrennt aufgebauten, sauberen Worktrees über demselben committeten Baum -> identische Bundle-States bis auf at (Q-6/A0-19); kanonisches Eingabemanifest + Run-Receipt außerhalb des Bundles; keine hart codierten erwarteten Bundle-Outputs, verified nicht pauschal maskiert; echte Content-Hashes (Witness weicht byte-weise von Baseline ab)" # --- Zwei getrennte, saubere Worktrees über demselben Baseline-Commit ($BASE) aufbauen --- # Re-Run-Sicherheit: verwaiste worktree-Registrierungen aus früheren Läufen (anderes $ROOT) # erst prune, sonst blockiert `git worktree add` oder trägt Altpfade nach. git worktree prune git worktree add -q "$ROOT/wt-a" "$BASE" || { echo "HARD-FAIL (DET-2): Worktree wt-a nicht aufgebaut" >&2; exit 1; } git worktree add -q "$ROOT/wt-b" "$BASE" || { echo "HARD-FAIL (DET-2): Worktree wt-b nicht aufgebaut" >&2; exit 1; } # Kanonisches Eingabemanifest (A0-19, §5.14 Pkt. 2): Baseline-Commit + geordnete Source- # Eingaben + output-sichtbare Run-/Zeit-/Identitätswerte — identisch für beide Läufe. # Das Manifest wird unten in run2_worktree VOR jedem Lauf konsumiert/validiert (nicht dekorativ). cat > "$ROOT/manifest.yaml" <&2; exit 1; } run2_worktree() { # $1 = Worktree-Pfad ; $2 = Run-Name ; führt die Instruktion als EIGENER # Ausführungskontext (frischer Agent-Kontext simulativ: eigener Worktree, eigene Subshell # mit eigenem cwd, keine Session-Wiederholung) über demselben committeten Baum aus. # Der Run läuft komplett in einer Subshell — der Haupt-Kontext bleibt am Sandbox-Root # (kein Carry-over des Worktree-cwd in nachfolgende Szenarien). local wt="$1" name="$2" ( set -e cd "$wt" || exit 9 # Kanonisches Eingabemanifest VOR dem Lauf konsumieren/validieren (A0-19, §5.14 Pkt. 2): # Baseline-Commit und geordnete Source-Eingaben müssen dem Repository-State entsprechen # — das Manifest ist Eingabe der Instruktion, nicht bloße Doku (kein leerer Lauf möglich). m_base=$(sed -n 's/^baseline: //p' "$ROOT/manifest.yaml") [ -n "$m_base" ] || { echo "HARD-FAIL (DET-2): Manifest ohne baseline-Key" >&2; exit 9; } [ "$m_base" = "$BASE" ] || { echo "HARD-FAIL (DET-2): Manifest-baseline ($m_base) != tatsaechliche Baseline ($BASE)" >&2; exit 9; } # Abschnittsbewusste Extraktion: nur Elemente unter dem jeweiligen Manifest-Key m_src=$(awk '/^sources:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); sub(/ *#.*$/,""); print}' "$ROOT/manifest.yaml") m_identity=$(awk '/^output_visible_run_identity:/{f=1;next} /^[a-z_]+:/{f=0} f && /^ - /{sub(/^ - /,""); sub(/ *#.*$/,""); print}' "$ROOT/manifest.yaml") [ -n "$m_src" ] || { echo "HARD-FAIL (DET-2): Manifest ohne geordnete Source-Eingaben" >&2; exit 9; } [ -n "$m_identity" ] || { echo "HARD-FAIL (DET-2): Manifest ohne output-sichtbare Identitätswerte" >&2; exit 9; } case "$m_identity" in *generated.at*) ;; *) { echo "HARD-FAIL (DET-2): Manifest muss generated.at als Identitätswert führen (A0-20-Ausnahme)" >&2; exit 9; };; esac # Sicherstellen: Worktree am Baseline-Commit, sauber (Q-6: getrennt aufgebaut, sauber). git checkout -qf "$BASE" git clean -qfd wiki raw 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" # Source-Eingaben-Verdikt: JEDE manifestierte geordnete Source-Eingabe (raw/alpha-v1.md # aus der Baseline, raw/alpha-v2.md als neuer Increment-Input) existiert im Worktree # NACH dem Input-Commit (EC-1-Semantik: Quelle zum Verarbeitungszeitpunkt vorhanden). for s in $m_src; do [ -f "$s" ] || { echo "HARD-FAIL (DET-2): Manifest-Source fehlt im Worktree nach Input-Commit: $s" >&2; exit 9; } done # Mutations-Commit als commitierter Run-Ausgangszustand: generated.at IST die reale # Wanduhr-Ausgabe des Laufs (A0-20) — sie wird COMMITTET und im Receipt als eigener # at-Zellenwert festgehalten; die Normalisierung auf AT erfolgt erst BEIM VERGLEICH # (§5.14 Pkt. 2: "diff ... ohne die maskierte at-Zeile"), nicht vor dem Commit # (kein stiller Ausschluss, keine Maskierung vor der Persistenz). at_now=$(date -u +%Y-%m-%dT%H:%M:%SZ) cat > wiki/alpha.md <&2; exit 9; } # --- Bundle-State projizieren (deterministisch) + RUN-RECEIPT (außerhalb des Bundles) --- # Run-Receipt (A0-19, §5.14 Pkt. 2): Candidate-Liste, Reihenfolge, Plan, Entscheidungen, # Output-Hashes, Bundle-TREE und die at-Ausnahme (je Run-Cell) — liegt AUSSERHALB des # Knowledge Bundle (nicht in wiki//raw/), hier unter $ROOT/receipts/. # Die Run-Identität steht im Dateinamen (run2a.receipt/run2b.receipt); der INHALT ist die # deterministische Projektion plus die at-Ausnahme-Cell, damit der Zwei-Run-Abgleich BOTH # (a) byte-identische deterministische Teile UND (b) die ausgewiesene at-Ausnahme prüft. mkdir -p "$ROOT/receipts" { echo "baseline: $BASE" echo "plan: $plan" echo "candidates: $cand" echo "order: update-$cand" echo "decisions: form=update;candidate_match=stufe_a(kopplung)" # sha_alpha wird über die at-NORMALISIERTE Content-Projektion gebildet (§5.14 Pkt. 2: # Vergleich "ohne die maskierte at-Zeile"; A0-20) — die deterministischen Bestandteile # bleiben damit byte-vergleichbar, der at-Wert trägt nur in der at_cell-Ausnahme. echo "sha_alpha=$(git show 'HEAD:wiki/alpha.md' | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1)" echo "sha_log=$(git show 'HEAD:wiki/log.md' | sha256sum | cut -d' ' -f1)" echo "sha_index=$(git show 'HEAD:wiki/index.md' | sha256sum | cut -d' ' -f1)" # at_cell + tree sind die AUSNAHME-Evidenz: tree (Bundle-TREE-Hash des Run-Committs) # enthält die realen at-Wanduhr-Werte — wird daher NICHT im deterministischen Block # verglichen, sondern dokumentiert hier die benannte at-Ausnahme (kein stiller # Ausschluss: die deterministischen Bundle-Bestandteile decken sha_alpha/log/index ab). echo "at_cell=$committed_at" echo "tree=$(git rev-parse 'HEAD:')" } > "$ROOT/receipts/$name.receipt" ) local rc=$? [ $rc -eq 0 ] || { echo "HARD-FAIL (DET-2): Lauf $name im Worktree fehlgeschlagen (rc=$rc)" >&2; exit 1; } } echo "--- Lauf A (frischer Agent-Kontext, Worktree wt-a) ---" run2_worktree "$ROOT/wt-a" run2a cat "$ROOT/receipts/run2a.receipt" echo "--- Lauf B (frischer Agent-Kontext, Worktree wt-b) ---" run2_worktree "$ROOT/wt-b" run2b cat "$ROOT/receipts/run2b.receipt" # Receipt-Gerüst-Nicht-Vakuum: beide Receipts müssen den deterministischen Zeilenblock tragen # (diff könnte sonst zweifach-leere Dateien als "identisch" PASSen lassen). for r in "$ROOT/receipts/run2a.receipt" "$ROOT/receipts/run2b.receipt"; do [ -s "$r" ] || { echo "HARD-FAIL (DET-2): Receipt $r leer oder fehlt (Vakuum)" >&2; exit 1; } done echo "--- Abgleich der zwei Run-Receipts (nicht nur jeder Lauf gg. Baseline; keine hart codierten Erwartungswerte) ---" # Deterministischer Vergleich: ALLE Zeilen bis auf die beiden Ausnahme-Cells (at_cell=, tree=) # müssen zwischen den zwei Runs byte-identisch sein — at_cell/tree tragen die realen, je Lauf # unterschiedlichen Wanduhr-at-Werte und dokumentieren damit die benannte Ausnahme (A0-20, # §5.14 Pkt. 3): differenzieren dürfen ausschließlich diese beiden; der Rest ist byte-identisch. filtered_a=$(grep -vE '^(at_cell|tree)=' "$ROOT/receipts/run2a.receipt") filtered_b=$(grep -vE '^(at_cell|tree)=' "$ROOT/receipts/run2b.receipt") at_a=$(grep '^at_cell=' "$ROOT/receipts/run2a.receipt" | cut -d= -f2) at_b=$(grep '^at_cell=' "$ROOT/receipts/run2b.receipt" | cut -d= -f2) tree_a=$(grep '^tree=' "$ROOT/receipts/run2a.receipt" | cut -d= -f2) tree_b=$(grep '^tree=' "$ROOT/receipts/run2b.receipt" | cut -d= -f2) # (a) die deterministischen Bestandteile: byte-identisch if [ "$filtered_a" != "$filtered_b" ]; then echo "HARD-FAIL (DET-2): deterministische Receipt-Bestandteile weichen ab (AD-16-Klassifikationsdefekt, ausserhalb der at-Ausnahme; §5.14 Pkt. 2/4):" >&2 diff -u <(printf '%s\n' "$filtered_a") <(printf '%s\n' "$filtered_b") >&2 || true exit 1 fi # (b) at-Ausnahme benannt vorhanden (dokumentierte Ausnahme, kein stiller Ausschluss) [ -n "$at_a" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme-Cell fehlt in receipt A" >&2; exit 1; } [ -n "$at_b" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme-Cell fehlt in receipt B" >&2; exit 1; } [ -n "$tree_a" ] && [ -n "$tree_b" ] || { echo "HARD-FAIL (DET-2): tree-Cell fehlt in einem Receipt" >&2; exit 1; } [ "$at_a" = "$at_b" ] && [ "$tree_a" = "$tree_b" ] && echo " BEFUND: at-Grenzfall — beide Läufe tragen identischen at-Wert (Wanduhr-Sekundenkollision); die Ausnahme (A0-20) erlaubt Abweichung, verlangt sie aber nicht (deterministischer Vergleich bleibt gültig)" [ "$at_a" != "$at_b" ] && echo " BEFUND: at-Ausnahme aktiv — Lauf A at=$at_a, Lauf B at=$at_b (benannte Ausnahme, §5.14 Pkt. 3: übrige Bestandteile byte-identisch)" # (d) Der deterministische Content weicht als Ganzes von der Baseline ab (Witness, echte Hashes). ABS_A=$(git -C "$ROOT/wt-a" show "$BASE:wiki/alpha.md" | sha256sum | cut -d' ' -f1) SHA_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1) [ -n "$ABS_A" ] && [ -n "$SHA_A" ] || { echo "HARD-FAIL (DET-2): Witness-Hashes leer (Vakuum — Assertion wertlos)" >&2; exit 1; } [ "$ABS_A" != "$SHA_A" ] || { echo "HARD-FAIL (DET-2): Witness == Baseline (vakuum — der Vergleich bewiese nichts; AD-17h/A0-19: echte Content-Identität gefordert)" >&2; exit 1; } # verified wird NICHT pauschal maskiert: das demonstrierte Bundle trägt ausschließlich die # Identitätswerte generated.by + generated.at (kein verified[]-Feld — die Ausnahme-Menge ist # in diesem Mini-Bundle exakt {generated.at}); die einzige erlaubte Differenz der # Bundle-State-Vergleiche ist die benannte at-Ausnahme (Pkt. 3), die hier als at_cell real # committet und beim Vergleich dokumentiert normalisiert ist (kein Maskieren vor Commit, # kein stiller Ausschluss — eine weitere Differenz würde den deterministischen Abgleich brechen). echo " WITNESS: Baseline-alpha-SHA ($ABS_A) != Run-alpha-SHA ($SHA_A) — der Zwei-Run-Vergleich ist nicht-vakuum (Content weicht von Baseline ab, echte Hashes)" echo " MANIFEST: $ROOT/manifest.yaml (identisch für beide Läufe, VOR jedem Lauf validiert); RECEIPTS: $ROOT/receipts/ (außerhalb des Bundles)" echo "RESULT: PASS — DET-2: ZWEI_RUN_IDENTISCH — zwei frische Agent-Kontexte in getrennt aufgebauten, sauberen Worktrees (wt-a/wt-b, demselben committeten Baum) produzieren identische Bundle-States (Run-Receipts byte-identisch bis auf die als at_cell ausgewiesene generated.at-Ausnahme: Plan-, order-, decisions-, log-, index-, alpha-Content-Hashes und Bundle-TREE; at real committet und erst beim Vergleich normalisiert — dokumentierte Ausnahme statt stiller Ausschluss); Vergleich nicht-vakuum (Witness vs. Baseline, echte Content-Hashes, §5.14 Pkt. 2, FT-10/AD-17h AC-1, Q-6/A0-19)" # ===================================================================================== # DET-3 ZWEI_RUN_ABWEICHUNG (§5.14 Pkt. 4) # ===================================================================================== runlabel "DET-3: ZWEI_RUN_ABWEICHUNG — künstlich divergenter Lauf -> als AD-16-Klassifikationsdefekt klassifiziert und textuell benannt (kein Rauschen); korrigierter zweiter Lauf == erster Lauf (erneut bestätigt)" # Referenz-Run (deterministische Bundle-State-Projektion) run3_ref() { isolate run3ref 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" 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: AT --- 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 git add -A && git commit -qm "Run: alpha-Update (Referenz)" echo "SHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)" } # Divergenter Run: veränderter Body-Content (RAUSCHEN-fremde Abweichung — ein deterministischer # Bestandteil weicht ab, hier eine andere Formulierung der Aussage) trotz gleichem Git-State+Input. run3_div() { isolate run3div 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" 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: AT --- 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 git add -A && git commit -qm "Run: alpha-Update (divergent)" echo "SHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)" } R3=$(run3_ref) D3=$(run3_div) echo "Referenz-Run: $R3" echo "Divergenter Lauf: $D3" SHA_REF=$(echo "$R3" | cut -d= -f2) SHA_DIV=$(echo "$D3" | cut -d= -f2) [ -n "$SHA_REF" ] && [ -n "$SHA_DIV" ] || { echo "HARD-FAIL (DET-3): Hashes leer (Vakuum)" >&2; exit 1; } if [ "$SHA_REF" = "$SHA_DIV" ]; then echo "HARD-FAIL (DET-3): der divergente Lauf blieb inspiziert ununterscheidbar (Abweichung nicht erkannt; §5.14 Pkt. 4 verlangt Klassifikation)" >&2 exit 1 fi echo "NFR-4-Benennung (textuell): ZWEI_RUN_ABWEICHUNG — Lauf 'divergent' weicht ausserhalb der benannten at-Ausnahme vom Referenz-Lauf ab (unterschiedlicher Content-Hash $SHA_DIV != $SHA_REF bei gleichem Git-State + gleichem Eingabeset) => AD-16-Klassifikationsdefekt (FT-10/AD-17h-AC), kein Rauschen; der Run wird als Fehler im Klassifikations-Mechanismus behandelt, textuell benannt und korrigiert/zurückgerollt (§5.14 Pkt. 4, Zustands-Restaurations-Invariante §5.13 Pkt. 3)" echo "--- Korrigierter zweiter Lauf: rollt zurueck und wiederholt deterministisch (== Referenz) ---" rollback3() { git checkout -q "$BASE" -- wiki/ 2>/dev/null || true git reset -q --hard "$BASE" git clean -qfd wiki } rollback3 R3B=$(run3_ref) SHA_REFB=$(echo "$R3B" | grep '^SHA=' | cut -d= -f2) [ "$SHA_REF" = "$SHA_REFB" ] || { echo "HARD-FAIL (DET-3): korrigierte Wiederholung weicht von der Referenz ab (AD-17h verletzt)" >&2; exit 1; } echo " BEFUND: korrigierter Lauf == Referenz-Lauf (SHA $SHA_REF) — der Vertrag ist nach Korrektur erneut bestätigt (Pkt. 4: 'Befund wird (ggf. als korrigierter zweiter Lauf) erneut bestätigt')" echo "RESULT: PASS — DET-3: ZWEI_RUN_ABWEICHUNG — künstlich divergenter Lauf wurde als AD-16-Klassifikationsdefekt erkannt (Content-Hash-Differenz bei gleichem State+Input — kein Rauschen, keine Umgebungs-Streuung) und textuell benannt (NFR-4); korrigierter zweiter Lauf reproduziert den Referenz-Bundle-State (Zustands-Restaurations-Invariante, §5.13 Pkt. 3; §5.14 Pkt. 4)" # ===================================================================================== # DET-4 AT_GAP_AUSNAHME (§5.14 Pkt. 3) # ===================================================================================== runlabel "DET-4: AT_GAP_AUSNAHME — generated.at-Wanduhr-Gap ist die einzige benannte Ausnahme; zwei Runs mit unterschiedlichem at -> diff ausschließlich auf der at-Zeile; nach at-Maskierung byte-identisch; jede andere Differenz = AD-16-Klassifikationsdefekt" isolate det4 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" mkrun4() { # $1 = at-Wert — schreibt alpha mit EXAKT unterschiedlichen at-Werten cat > wiki/alpha.md <&2; exit 1; } non_at=$(echo "$DABC" | grep '^[-+][^-+]' | grep -v '^[-+] at: ' | grep -v '^[+-][+-]') if [ -n "$non_at" ]; then echo "HARD-FAIL (DET-4): der diff zwischen den zwei Runs betrifft BESTANDTEILE ausserhalb der at-Ausnahme:" >&2 echo "$non_at" | sed 's/^/ /' >&2 exit 1 else echo " BEFUND: diff ausschließlich auf der at-Zeile (einzige benannte Ausnahme, §5.14 Pkt. 3)" fi echo "--- Nach at-Maskierung byte-identisch (restliche Bundle-Bestandteile) ---" H1=$(git show HEAD~1:wiki/alpha.md | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1) H2=$(git show HEAD:wiki/alpha.md | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1) [ "$H1" = "$H2" ] || { echo "HARD-FAIL (DET-4): nach at-Maskierung weichen die Runs ab (bestandteil über at hinaus; §5.14 Pkt. 3 verlangt byte-Identität der übrigen Teile)" >&2; exit 1; } echo " BEFUND: nach at-Maskierung byte-identisch (SHA $H1) — dokumentierte Ausnahme, kein stiller Ausschluss" echo "--- Negative Kontrolle: eine Differenz AUSSERHALB der Ausnahme ist ein AD-16-Klassifikationsdefekt ---" mkrun4 "2026-08-16T09:00:00Z" printf '\nAndere Aussagenformulierung.\n' >> wiki/alpha.md git add -A && git commit -qm "Run at=B' (Differenz im Body)" non_at2=$(git diff HEAD~1 HEAD -- wiki/alpha.md | grep '^[-+][^-+]' | grep -v '^[-+] at: ' | grep -v '^[+-][+-]') [ -n "$non_at2" ] || { echo "HARD-FAIL (DET-4): Body-Differenz wurde nicht erkannt (Negativkontrolle leer)" >&2; exit 1; } echo "NFR-4-Benennung (textuell): Body-Differenz ausserhalb der at-Ausnahme-Menge => AD-16-Klassifikationsdefekt (kein Rauschen), textuell benannt (§5.14 Pkt. 3/4)" echo "RESULT: PASS — DET-4: AT_GAP_AUSNAHME — generated.at-Wanduhr-Gap ist die einzige benannte Differenz im Bundle-State-Vergleich (A0-20-Konvention, §5.9 Pkt. 2/§5.10 Pkt. 8): zwei Runs diffieren ausschließlich in der at-Zeile, nach at-Maskierung byte-identisch; jede andere Differenz liegt ausserhalb der Ausnahme = AD-16-Klassifikationsdefekt (Negativkontrolle, §5.14 Pkt. 3)" # ===================================================================================== # DET-5 EM_DASH (§3.2 Pkt. 1b, §5.14 Pkt. 5) # ===================================================================================== runlabel "DET-5: EM_DASH — Em-Dash — normalisiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen (§3.2 Pkt. 1b: Kollaps-Klasse [-–— _] -> -)" # Term-Basis mit Em-Dash / En-Dash / Bindestrich / Unterstrich / Leerzeichen-Separator EM=$(norm "wissen — relevanz") EN=$(norm "wissen – relevanz") HY=$(norm "wissen - relevanz") US=$(norm "wissen_relevanz") SP=$(norm "wissen relevanz") [ "$EM" = "$EN" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != En-Dash-Form" >&2; exit 1; } [ "$EM" = "$HY" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Bindestrich-Form" >&2; exit 1; } [ "$EM" = "$US" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Unterstrich-Form" >&2; exit 1; } [ "$EM" = "$SP" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Leerzeichen-Form" >&2; exit 1; } case "$EM" in *"—"*) echo "HARD-FAIL (DET-5): Em-Dash blieb in der canonischen Form erhalten (Kollaps-Klasse greift nicht)" >&2; exit 1;; esac echo " canonische Form (alle fünf Separatoren): '$EM'" echo " Em-Dash-Term kollabiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen (kein stiller Ausschluss, §3.2 Pkt. 1b)" echo "RESULT: PASS — DET-5: EM_DASH — Em-Dash — ist in der Kollaps-Klasse [-–— _] -> - (§3.2-Pkt.-1b-Ergänzung, §5.14 Pkt. 5): Term mit Em-Dash erhält dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen-Varianten — deterministisch, keine unterschiedliche canonische Form je Separator" # ===================================================================================== # DET-6 KOLLAPS (§3.2 Pkt. 1b, §5.14 Pkt. 5) # ===================================================================================== runlabel "DET-6: KOLLAPS — Kollaps-Reichweite: Läufe (a--b), führende (-x), trailende (x-) -> Kollaps auf genau ein - + Trim (§3.2 Pkt. 1b)" [ "$(norm "a--b")" = "a-b" ] || { echo "HARD-FAIL (DET-6): Lauf a--b kollabiert nicht auf a-b (norm='$(norm "a--b")')" >&2; exit 1; } [ "$(norm "-x")" = "x" ] || { echo "HARD-FAIL (DET-6): führender Separator -x wird nicht getrimmt (norm='$(norm "-x")')" >&2; exit 1; } [ "$(norm "x-")" = "x" ] || { echo "HARD-FAIL (DET-6): trailender Separator x- wird nicht getrimmt (norm='$(norm "x-")')" >&2; exit 1; } [ "$(norm "a--b")" = "a-b" ] && [ "$(norm "a---b")" = "a-b" ] && [ "$(norm "---x---")" = "x" ] || { echo "HARD-FAIL (DET-6): Läufe/Trim-Kombination inkonsistent" >&2; exit 1; } echo " Läufe a--b -> '$(norm "a--b")'; führend -x -> '$(norm "-x")'; trailend x- -> '$(norm "x-")'" echo "RESULT: PASS — DET-6: KOLLAPS — Kollaps-Reichweite deterministisch (§3.2 Pkt. 1b, §5.14 Pkt. 5): Läufe kollabieren auf genau ein -, führende/trailende Separatoren werden getrimmt — glaubhaft re-executierbar, keine offene Reichweiten-Frage" # ===================================================================================== # DET-7 MATCH_SCOPE (§3.2 Pkt. 2a, §5.14 Pkt. 5) # ===================================================================================== runlabel "DET-7: MATCH_SCOPE — Stufe a matcht ganze Wörter über den Body, exklusive YAML-Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen, §3.2 Pkt. 2a)" isolate det7 # Concept: Body enthält "kopplung" als GANZES Wort; Frontmatter enthält "kopplung" als # Substring im Quell-Pfad raw/kopplung-v1.md (Frontmatter-Treffer-Wächter). cat > wiki/alpha.md <<'EOF' --- type: concept sources: - resource: raw/kopplung-v1.md id: s1 generated: by: wow-compiler/0.1.0 at: 2026-08-16T09:00:00Z --- Alpha als Wissenseinheit: hier steht einzig das ganz Wort kopplung ohne weitere Bindung. 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: das Wort entkopplung belegt nur einenerweiternden Teil — kein eigener ganzer Treffer. EOF git add -A && git commit -qm "Stufe-a-Flex" # 1) Substring-Kontrolle: 'kopplung' (ganz) trifft alpha; 'opplung' (Substring) trifft nichts HIT1=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md) [ "$HIT1" = "alpha" ] || { echo "HARD-FAIL (DET-7): ganzes-Wort-Match 'kopplung' liefert '$HIT1' (Soll: alpha; Substring-/Gamma-Treffer unerlaubt)" >&2; exit 1; } HIT2=$(match_stufe_a "opplung" wiki/alpha.md) [ -z "$HIT2" ] || { echo "HARD-FAIL (DET-7): Substring-Match 'opplung' liefert einen Kandidaten (Stufe a matcht ganze Wörter, §3.2 Pkt. 2a)" >&2; exit 1; } # 2) Frontmatter-Exklusion: Suchbegriff exakt gleich einem Frontmatter-RESOURCE-Substring # (raw/kopplung-v1.md enthält 'kopplung') — zugleich im Body von alpha steht 'kopplung' # nur als ganzes Wort => Treffer genau EINMAL (alpha). Der Frontmatter ist exkludiert, # ein zusätzlicher Body-Treffer erzeugt keinen zweiten Kandidaten — wir prüfen, dass # das Frontmatter-Vorkommen keinen falschen Kandidaten (z. B. durch RegExp über die ganze # Datei inkl. Frontmatter) erzeugt. Konkret: wir legen einen zweiten Concept-Body VOR, der # 'kopplung' NUR im Frontmatter traegt (Quell-Pfad raw/kopplung-mirror.md) — Stufe a muss # ihn verwerfen, weil der Frontmatter exkludiert ist. cat > wiki/mirror.md <<'EOF' --- type: concept sources: - resource: raw/kopplung-mirror.md id: s1 generated: by: wow-compiler/0.1.0 at: 2026-08-16T09:00:00Z --- Gamma-Mirror ohne Kopplungs-Begriff im Bodytext. EOF git add -A && git commit -qm "Frontmatter-only-Kandidat" HIT3=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md wiki/mirror.md) [ "$HIT3" = "alpha" ] || { echo "HARD-FAIL (DET-7): Frontmatter-Treffer erzeugt Kandidat (mirror) — Stufe a matcht nur Body, exklusive YAML-Frontmatter (§3.2 Pkt. 2a)" >&2; exit 1; } # 3) Direkte Formel-Probe der operativen Stufe-a-Formel (§3.2-Pkt.-2a-Zweistufen-Mechanik): # Sweep mit der Formel -> dann deterministischer Scope-Filter (ganze Wörter, Body, after second ---). SWEEP=$(grep -rl 'kopplung' --exclude=log.md wiki/ | LC_ALL=C sort) # Sweep-muss alpha, gamma (Substring entkopplung) und mirror (Frontmatter-only) finden — # der Scope-Filter verwirft gamma+mirror; als Kandidaten bleiben die Body-ganz-Wort-Treffer. FILTERED=$(for f in $SWEEP; do if match_stufe_a "kopplung" "$f" | grep -q .; then basename "$f" .md; fi done | LC_ALL=C sort | tr '\n' ' ') FILTERED="${FILTERED% }" [ "$FILTERED" = "alpha" ] || { echo "HARD-FAIL (DET-7): operative Stufe-a-Zweistufen-Mechanik liefert '$FILTERED' (Soll: alpha) — Scope-Filter unvollständig (§3.2 Pkt. 2a, §5.14 Pkt. 5)" >&2; exit 1; } echo " Direkte Formel-Probe: Sweep (grep -rl) -> Scope-Filter verwirft Substring-/Frontmatter-Treffer -> Kandidat: alpha" echo "RESULT: PASS — DET-7: MATCH_SCOPE — Stufe a matcht ganze Wörter (Wortgrenzen) über den Body, exklusive YAML-Frontmatter: Substring-Treffer ('opplung') und Frontmatter-only-Kandidaten (mirror) liefern keine Kandidaten (§3.2 Pkt. 2a, §5.14 Pkt. 5) — deterministischer Stufe-a-Scope, keine falschen Kandidaten; operative Zweistufen-Mechanik (Sweep+Filter) abgesichert" # ===================================================================================== # DET-8 ORPHAN (§5.10 Pkt. 8, §5.14 Pkt. 5) # ===================================================================================== runlabel "DET-8: ORPHAN — neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer -> unzugeordnet, log.md-verwaist protokolliert (Datumsgruppe, ), kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default)" isolate det8 cat > raw/orphan-v1.md <<'EOF' ### S-1 Evidenz: gänzlich fremdes Thema, das in keinem bestehenden Concept-Body Wort-Treffer hat. EOF git add -A && git commit -qm "Verwaiste Evidenz" # Ziel-Pfad-Treffer-Prüfung: Term über die neuen Quellen enthält 'fremdes-thema' — kein # bestehender Concept-Body trifft (Stufe a, deterministisch); kein Update-Kandidat. CAND=$(match_stufe_a "fremdes" wiki/alpha.md wiki/gamma.md) [ -z "$CAND" ] || { echo "HARD-FAIL (DET-8): verwaiste Evidenz erzielt einen Ziel-Pfad-Treffer (Kein-Orphan erwartet)" >&2; exit 1; } # Reconcile-Orphan-Regel (§5.10 Pkt. 8 / §5.14 Pkt. 5): deterministisch aus dem committeten # Git-State (Zuwachs gg. ) als verwaist befunden -> datumsgruppierter # log.md-Eintrag (Header YYYY-MM-DD, Quell-Pfad + ); keine eigenständige Anlage. log_bullet "- Reconcile-Orphan (Story 3.8; §5.10 Pkt. 8 / §5.14 Pkt. 5): raw/orphan-v1.md (Zuwachs gg. ) ohne Ziel-Pfad-Treffer — unzugeordnet, verwaist protokolliert; kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default, Epic-4-Interface)" echo "--- Kein Banner / keine stille Vorbearbeitung / keine eigenständige Anlage ---" if [ -f wiki/orphan.md ]; then echo "HARD-FAIL (DET-8): verwaiste Evidenz wurde eigenständig als Concept angelegt (verboten, AD-16-Default)" >&2 exit 1 fi # Nur log.md ist mutiert (Orphan-Eintrag) — Erhaltungs-Invariante §5.9 Pkt. 5 (log.md ist # erlaubtes Mitglied des Diff-Selbsttest-Satzes); keine weiteren Mutations-Pfade. MUT=$( { 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) [ "$MUT" = "log" ] || { echo "HARD-FAIL (DET-8): Verwaist-Run mutiert ausserhalb {log} — Pfade: [$MUT]" >&2; exit 1; } grep -qF "Reconcile-Orphan" wiki/log.md || { echo "HARD-FAIL (DET-8): Verwaist-Protokoll fehlt in log.md (§5.10 Pkt. 8)" >&2; exit 1; } grep -qF "## 2026-08-20" wiki/log.md || { echo "HARD-FAIL (DET-8): log.md-Eintrag nicht in Datumsgruppe (Vertrag §5)" >&2; exit 1; } grep -qF "$BASE" wiki/log.md || { echo "HARD-FAIL (DET-8): fehlt im Verwaist-Eintrag (§5.10 Pkt. 8, deterministisch auflösbar D-2)" >&2; exit 1; } echo " BEFUND: raw/orphan-v1.md bleibt in raw/ unangetastet (AD-3); log.md trägt datumsgruppierten Verwaist-Eintrag mit " echo "RESULT: PASS — DET-8: ORPHAN — neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer bleibt unzugeordnet und wird als datumsgruppierter log.md-Eintrag (Quell-Pfad + ) verwaist protokolliert (§5.10 Pkt. 8 / §5.14 Pkt. 5): kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default) — Erhaltungs-Invariante gewahrt (nur log.md mutiert)" echo echo "===== Sandbox abgeschlossen (DET-1..DET-8) =====" echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"