Files

573 lines
33 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 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 unabhängige Läufe über demselben
# committeten Baum -> identische Bundle-States bis auf at; die Assertion
# vergleicht echte Content-Hashes (nicht nur Vorhandensein); der Witness
# (wiki/alpha.md) 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, <Baseline-Commit>), 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) — §5.14 Pkt. 2
# =====================================================================================
runlabel "DET-2: ZWEI_RUN_IDENTISCH (nicht-vakuum) — zwei unabhängige Läufe über demselben committeten Baum -> identische Bundle-States bis auf at; echte Content-Hashes (Witness weicht byte-weise von Baseline ab)"
run2() { # $1 = Run-Name ; führt die Instruktion über dem committeten Baum aus
isolate "$1"
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-/Kandidaten-Output (§5.14 Pkt. 2: identische Plan-/Kandidaten-/Reihenfolge-Outputs)
local plan
plan="cand=alpha;form=update;graphic=aktualisieren;baseline=$BASE"
# Mutations-Commit als ein commitierter Run-Ausgangszustand (unterschiedliches at je Run:
# A0-20-Konvention — der at-Wert ist Wanduhr, NICHT Teil des deterministischen Vergleichs)
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
# at-Maskierung für den deterministischen Vergleich (§5.14 Pkt. 3): DET-2 demonstriert
# die Identität der NICHT-at-Bestandteile (Plan-, log-, index-, alpha-Content + TREE) —
# dafür wird die at-Zeile vor dem Hash auf ein festes Token normalisiert (die at-Varianz
# selbst ist die benannte Ausnahme, A0-20; ihre Behandlung demonstriert DET-4 positiv und
# negativ). Die Ausnahme ist dokumentiert, kein stiller Ausschluss; alle übrigen
# Bestandteile gehen byte-identisch in den Hash ein.
sed -i "s/^ at: .*/ at: AT/" wiki/alpha.md
log_bullet "- Determinismus-Bestätigung (Zwei-Run, §5.14 Pkt. 2): $plan; Baseline $BASE"
git add -A && git commit -qm "Run: alpha-Update ($1)"
# --- Bundle-State projizieren (deterministisch): echte Content-Hashes (nicht-vakuum) ---
echo "PLAN=$plan"
echo "SHA_ALPHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)"
echo "ABS_BASELINE=$(git show "$BASE: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:')"
}
echo "--- Lauf A (unabhängig) ---"
SA=$(run2 run2a)
echo "$SA"
echo "--- Lauf B (unabhängig, über demselben committeten Baum) ---"
SBV=$(run2 run2b)
echo "$SBV"
# Zwei unabhängige Läufe -> identische Bundle-States bis auf at
[ "$(echo "$SA" | grep '^PLAN=')" = "$(echo "$SBV" | grep '^PLAN=')" ] || { echo "HARD-FAIL (DET-2): Plan-/Kandidaten-Outputs weichen ab (AD-17h/A0-19, §5.14 Pkt. 2)" >&2; exit 1; }
[ "$(echo "$SA" | grep '^SHA_ALPHA=')" = "$(echo "$SBV" | grep '^SHA_ALPHA=')" ] || { echo "HARD-FAIL (DET-2): alpha-content-Hash weicht zwischen Runs ab (Byte-Differenz ausserhalb at, §5.14 Pkt. 3)" >&2; exit 1; }
[ "$(echo "$SA" | grep '^SHA_LOG=')" = "$(echo "$SBV" | grep '^SHA_LOG=')" ] || { echo "HARD-FAIL (DET-2): log.md-Hash weicht ab" >&2; exit 1; }
[ "$(echo "$SA" | grep '^SHA_INDEX=')" = "$(echo "$SBV" | grep '^SHA_INDEX=')" ] || { echo "HARD-FAIL (DET-2): index.md-Hash weicht ab" >&2; exit 1; }
[ "$(echo "$SA" | grep '^TREE=')" = "$(echo "$SBV" | grep '^TREE=')" ] || { echo "HARD-FAIL (DET-2): Bundle-Baum nach at-Maskierung weicht ab (Soll: byte-identisch, §5.14 Pkt. 2)" >&2; exit 1; }
# Nicht-vakuum-Witness: der produzierte Content weicht byte-weise von der Baseline ab
ABS_A=$(echo "$SA" | grep '^ABS_BASELINE=' | cut -d= -f2)
SHA_A=$(echo "$SA" | grep '^SHA_ALPHA=' | cut -d= -f2)
[ -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; }
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 "RESULT: PASS — DET-2: ZWEI_RUN_IDENTISCH — zwei unabhängige Läufe über demselben committeten Baum produzieren identische Bundle-States (Plan-, log-, index-, alpha-Content-Hashes und Bundle-TREE byte-identisch, jeweils bis auf die benannte at-Ausnahme, die für den Vergleich maskiert ist); Vergleich nicht-vakuum (Witness vs. Baseline, echte Content-Hashes, §5.14 Pkt. 2, FT-10/AD-17h AC-1)"
# =====================================================================================
# 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 <<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: $1
---
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
}
mkrun4 "2026-08-16T09:00:00Z"
git add -A && git commit -qm "Run at=A"
mkrun4 "2026-08-19T23:59:59Z"
git add -A && git commit -qm "Run at=B"
# diff zwischen beiden Run-Zuständen: ausschließlich die at-Zeile
DABC=$(git diff HEAD~1 HEAD -- wiki/alpha.md)
[ -n "$DABC" ] || { echo "HARD-FAIL (DET-4): die zwei Runs sind identisch — at-Gap nicht demonstriert (Vakuum)" >&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, <Baseline-Commit>), 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. <Baseline-Commit>) als verwaist befunden -> datumsgruppierter
# log.md-Eintrag (Header YYYY-MM-DD, Quell-Pfad + <Baseline-Commit>); 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. <Baseline $BASE>) 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): <Baseline-Commit> 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 <Baseline-Commit>"
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 + <Baseline-Commit>) 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)"