#!/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 for file in "${@:2}"; do # Body = alles nach dem zweiten "---" (Frontmatter exkludiert, deterministisch) body=$(awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}' "$file") # 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])${term}([^A-Za-z0-9]|$)"; then basename "$file" .md fi done } # log.md-Eintrag (datumsgruppiert, neueste zuerst; Vertrag §5). Deterministisch fester Tag. log_bullet() { # $1 = Bullet-Text (eine Zeile) local day="2026-08-20" 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 } # ===================================================================================== # 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 --- 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): Baseline-Commit + geordnete Source-Eingaben + # output-sichtbare Run-/Zeit-/Identitätswerte — identisch für beide Läufe. cat > "$ROOT/manifest.yaml" < raw/alpha-v2.md <<'EOF' ### S-3 Evidenz v2: Alpha erweitert um eine synchrone Kopplung. EOF git add -A && git commit -qm "Evidenz v2" # Mutations-Commit als commitierter Run-Ausgangszustand. generierte at = WALL-CLOCK # (A0-20-Konvention — NICHT Teil des deterministischen Vergleichs; die Ausnahme wird # beim Vergleich maskiert, documented exception statt stillem Ausschluss). 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: RUNAT --- 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 sed -i "s/^ at: .*/ at: AT/" wiki/alpha.md # Plan-/Kandidaten-/Reihenfolge-Ableitung (§5.14 Pkt. 2) — abgeleitet aus dem # deterministischen Run-State, NICHT hart codiert: Kandidat = Stufe-a-Treffer des # Terms "kopplung" über den (deterministischen) Body (dieselbe Ableitung wie DET-1). local cand cand=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md) local plan plan="cand=$cand;form=update;baseline=$BASE" log_bullet "- Determinismus-Bestätigung (Zwei-Run, §5.14 Pkt. 2): $plan; Baseline $BASE" git add -A && git commit -qm "Run: alpha-Update ($name)" # --- Bundle-State projizieren (deterministisch) + RUN-RECEIPT (außerhalb des Bundles) --- # Run-Receipt (A0-19): Candidate-Liste, Reihenfolge, Plan, Entscheidungen, Output-Hashes # — 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 Receipt-INHALT # ist die reine deterministische Projektion (kein run:-Feld), damit der Zwei-Run-Abgleich # byte-identisch über echte Content-Hashes laufen kann (identische Bundle-Dateien -> # identische Hashes; jede Bundle-State-Abweichung müsste einen Hash differieren lassen). mkdir -p "$ROOT/receipts" { echo "baseline: $BASE" echo "plan: $plan" echo "candidates: $cand" echo "sha_alpha=$(git show 'HEAD:wiki/alpha.md' | 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)" 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" echo "--- Abgleich der zwei Run-Receipts (nicht nur jeder Lauf gg. Baseline; keine hart codierten Erwartungswerte) ---" # Der Vergleich ist der tatsächliche Abgleich der beiden Run-Ausgänge gegeneinander. if ! diff -u "$ROOT/receipts/run2a.receipt" "$ROOT/receipts/run2b.receipt" > "$ROOT/det2.diff" 2>&1; then echo "HARD-FAIL (DET-2): Run-Receipts weichen ab (AD-17h/A0-19, §5.14 Pkt. 2):" >&2 cat "$ROOT/det2.diff" >&2 exit 1 fi # Nicht-vakuum-Witness: der produzierte Content weicht byte-weise von der Baseline ab 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 Receipt enthält keine verified-Maskierung; # die einzige erlaubte Differenz der Bundle-States ist die benannte at-Ausnahme (Pkt. 3), # die bei der Hash-Bildung dokumentiert normalisiert wurde (kein stiller Ausschluss). 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); 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: Plan-, log-, index-, alpha-Content-Hashes und Bundle-TREE, jeweils bis auf die benannte at-Ausnahme, die beim Vergleich dokumentiert normalisiert ist); 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)"