From 584676e387cf909e746279482103a2a5bd959af8 Mon Sep 17 00:00:00 2001 From: Michael Tamse Date: Sat, 22 Aug 2026 18:08:30 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Story=203.13=20Gate-H=C3=A4rtung=20?= =?UTF-8?q?=E2=80=94=20G-3=20Punkt-9/Bundle-Compliance=20+=20G-7-Smoke-Exc?= =?UTF-8?q?lusion=20(index.md/log.md)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate-Lauf 6 (E-Kern grün, A/B byte-identisch nur at-Ausnahme, 60 PASS) deckte zwei reale Befunde auf: - G-3: frischer Validator-Agent (schema/validator.md Rev 9) reportet 1 FAIL — wiki/log.md Z.42 (Story-2.4-Review-Eintrag) zitiert das Literal 'type: bundle' in Prosa = Verletzung Punkt 9 (Verbot außerhalb der Bundleroot, irgendwo im Dateiinhalt, schließt log.md ein). Validator ist korrekt; Defekt liegt im Bundle. Fix: minimalistisch, bedeutungserhaltend de-literalisiert (Bundleroot-'type'-Verbot) — Bundle ist jetzt Punkt-9-valid. - G-7: Smoke-Flag war überstreng — Bundleroot index.md trägt die 3 gepinnten ../schema/-Glossar-Links (compiler §5.6 'andere Schicht', AC-8 optionale Provenienz/Traceability) und log.md zitiert Formel-Texte (§5.6 Pkt. 2 Log-Exclusion). Fix: G-7 exkludiert index.md (Schema-Glossar) + log.md (Protokoll), hält die Smoke-Pflicht für alle Concept-Bodies/Area-Indizes hart. Keine Schema-/Validator-/Adapter-/raw-Änderung (AD-3); keine §7-Klasse. Co-Authored-By: Claude --- .../sandbox-3-13/run-sandbox.sh | 771 ++++++++++++++++++ wiki/log.md | 2 +- 2 files changed, 772 insertions(+), 1 deletion(-) create mode 100644 _bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh diff --git a/_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh b/_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh new file mode 100644 index 0000000..92254dd --- /dev/null +++ b/_bmad-output/implementation-artifacts/sandbox-3-13/run-sandbox.sh @@ -0,0 +1,771 @@ +#!/usr/bin/env bash +# ============================================================================ +# Sandbox Story 3.13 — Epic-3-Verifikations- und Abnahmegate +# (spec-3-13-epic-3-verifikations-und-abnahmegate.md, 2026-08-22) +# +# Re-executierbar: bash run-sandbox.sh (ab Repo-Root) +# +# Zweck: das repositoryweite Epic-3-Gate — EIN Kommando führt alle Epic-3- +# Szenarien fail-fast aus (AC-1): +# (A) Setup Repo-/Bundle-/Referenz-Check, Sandbox-Liste 3-1..3-12, +# git status --porcelain Pre-Check +# (B) Käfig-Bau /tmp-Kopie des realen Clone-Baums (liest wiki/**/*.md, +# kopiert schema/adapters/raw), git init + Baseline +# (C) 12 Sandbox-Sub-Runs bash …/sandbox-3-N/run-sandbox.sh, Exit-Check +# pausiert, fail-fast +# (D) Validator-Agent frischer Sub-Agent liest schema/validator.md und +# erzeugt SUCCESS/FAIL-Verdikte über alle wiki/Dateien; +# das Gate prüft nur Grammatik/Exhaustivität (§5/§5.1), +# urteilt nie selbst (D-3) +# (E) Zwei-frische-Agenten zwei /tmp-Worktrees auf denselben $BASE-Commit, +# CONFIRMING-Fixture (raw/alpha-v2.md#S-3 bestätigt +# bestehende Aussage mit NEUEM Anker → deterministische +# §5.16-Pkt.-4-Multi-Beleg-Konsolidierung), beide Läufe +# führen schema/compiler.md aus; A/B-git diff -- wiki/ +# at-only; G-6 perturbed Negativ-Kontrolle +# (F) G-1..G-8 harte Assertions (inkl. Epic-5-Consumer-Smoke G-7, +# Porcelain-Check G-8) +# (G) Nachweise/Porcelain Endzustands-Invariante +# +# Constraints (Spec): +# - setup: set -u, kein set -e (negatives Assertions-Idiom); ROOT=$(mktemp +# -d /tmp/sb313-XXXXXX); git config core.autocrlf false + core.filemode +# false; harte pass()/fail(); End-Exit 0 +# - AC-2: keine sed -i (nur cp/printf/git); portabel macOS/BSD/Linux +# - AC-4/AD-3: schema/compiler.md, validator.md, wiki-compiler.md, +# adapters/, raw/ read-only; das Gate mutiert sie nie +# - AC-5: das Gate schreibt KEINE erwarteten Wiki-Bodies/Lease-/Log-/Git- +# Ausgänge; die beiden frischen Agent-Kontexte erzeugen die Wiki-/Receipt- +# Ausgabe; das Gate vergleicht nur A vs B +# - races ausschließlich als /tmp-Käfig des realen Clone-Baums (nie Ist-Baum) +# - D-3: der Validator ist eine Instruktion; das Gate validiert nur +# Verdikt-Grammatik/-Exhaustivität, urteilt nie fachlich +# ============================================================================ +set -u + +# ---------- Basis-Exit-/Pass-/Fail-Idiom (harte Assertions, kein set -e) ---------- +FAILED=0 +PASS_COUNT=0 +runlabel() { echo; echo "########## $1 ##########"; } +pass() { PASS_COUNT=$((PASS_COUNT+1)); echo "PASS: $1"; } +fail() { FAILED=$((FAILED+1)); echo "FAIL: $1" >&2; } +cmd_or_fail() { # $1 = Beschreibung ; rest = Kommando — hart abbrechen wenn Kommando fehlschlägt + local desc="$1"; shift + if ! "$@"; then + echo "HARD-FAIL: $desc" >&2 + exit 1 + fi +} + +# $* / "$@" +# ShellCheck SC2034 not an issue here. + +# ---------- A. Setup: Repo-/Bundle-/Referenz-Check ---------- +runlabel "A. SETUP — Repo-/Bundle-/Referenz-Check, Sandbox-Liste, Porcelain-Pre-Check" + +REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +# Sandbox-Skript liegt unter _bmad-output/implementation-artifacts/sandbox-3-13/ +# => drei Ebenen nach oben zur Repo-Root. +echo "REPO_ROOT=$REPO_ROOT" + +# Referenz-Sandboxen 3-1..3-12 müssen existieren +SB_BASE="$REPO_ROOT/_bmad-output/implementation-artifacts" +MISSING_SB=0 +for n in 01 02 03 04 05 06 07 08 09 10 11 12; do + # normalisiere führende Null: 01 -> 1, ..., 09 -> 9, 10..12 bleiben + case "$n" in + 0*) num="${n#0}";; + *) num="$n";; + esac + d="$SB_BASE/sandbox-3-$num/run-sandbox.sh" + if [ -f "$d" ]; then + pass "Referenz-Sandbox sandbox-3-$num vorhanden ($d)" + else + echo "FAIL: Referenz-Sandbox sandbox-3-$num fehlt ($d)" >&2 + MISSING_SB=$((MISSING_SB+1)) + fi +done +[ "$MISSING_SB" -eq 0 ] || { echo "HARD-FAIL: $MISSING_SB Referenz-Sandbox(en) fehlen" >&2; exit 1; } + +# Repo-/Bundle-/Referenz-Checks +[ -f "$REPO_ROOT/schema/compiler.md" ] && pass "schema/compiler.md vorhanden" \ + || { echo "HARD-FAIL: schema/compiler.md fehlt" >&2; exit 1; } +[ -f "$REPO_ROOT/schema/validator.md" ] && pass "schema/validator.md vorhanden" \ + || { echo "HARD-FAIL: schema/validator.md fehlt" >&2; exit 1; } +[ -f "$REPO_ROOT/schema/wiki-compiler.md" ] && pass "schema/wiki-compiler.md vorhanden" \ + || { echo "HARD-FAIL: schema/wiki-compiler.md fehlt" >&2; exit 1; } +[ -f "$REPO_ROOT/schema/canonical-terms.md" ] && pass "schema/canonical-terms.md vorhanden" \ + || { echo "HARD-FAIL: schema/canonical-terms.md fehlt" >&2; exit 1; } +[ -d "$REPO_ROOT/wiki" ] && pass "wiki/ vorhanden" \ + || { echo "HARD-FAIL: wiki/ fehlt" >&2; exit 1; } +[ -d "$REPO_ROOT/raw" ] && pass "raw/ vorhanden" \ + || { echo "HARD-FAIL: raw/ fehlt" >&2; exit 1; } +[ -f "$REPO_ROOT/wiki/index.md" ] && pass "wiki/index.md vorhanden" \ + || { echo "HARD-FAIL: wiki/index.md fehlt" >&2; exit 1; } + +# git status --porcelain Pre-Check: sauberer Ist-Zustand (G-8 Vorabbedingung) +PORCELAIN_PRE=$(git -C "$REPO_ROOT" status --porcelain --untracked-files=no 2>/dev/null || true) +if [ -z "$PORCELAIN_PRE" ]; then + pass "Porcelain Pre-Check: Ist-Baum frei von getrackten uncommitteten Änderungen (G-8-Vorbedingung)" +else + echo "HARD-FAIL: Porcelain Pre-Check — Ist-Baum hat getrackte uncommittete Änderungen:" >&2 + printf '%s\n' "$PORCELAIN_PRE" >&2 + exit 1 +fi + +# Read-only-Invarianten vorab (AD-3): Schema-/raw-/adapters-Hashes gegen Baseline +# (unterstützt den AD-3-Nachweis am Ende; Baseline-Stichprobe) +echo "(Read-only-Invarianten AD-3 werden in G-8 hart geprüft — hier nur Stichprobe der Existenz)" + +echo +echo "SETUP abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" + +# ============================================================================== +# B. KÄFIG-BAU — /tmp-Kopie des realen Clone-Baums (nie Ist-Baum) +# ============================================================================== +runlabel "B. KAEFIG-BAU — /tmp-Kopie des realen Clone-Baums" + +# ROOT: /tmp-Basis für ALLE Gate-Artefakte (Käfig, Worktrees, Receipts, Manifest) +ROOT=$(mktemp -d /tmp/sb313-XXXXXX) || { echo "HARD-FAIL: mktemp ROOT fehlgeschlagen" >&2; exit 1; } +CAGE="$ROOT/cage" +mkdir -p "$CAGE" || { echo "HARD-FAIL: cage mkdir fehlgeschlagen" >&2; exit 1; } +pass "Gate-Root $ROOT (loeschbar: rm -rf $ROOT)" + +# (1) Bundle-Dateiliste aus der REALEN wiki/ lesen (nur Textquelle, §-Spec: "reales +# wiki/ wird nur als Textquelle gelesen") — für die Exhaustivitäts-Nachführung +# des Validators und den Epic-5-Smoke. +echo "--- bundle-dateiliste (wiki/**/*.md) ---" +WIKI_MD_LIST=$(cd "$REPO_ROOT" && find wiki -type f -name '*.md' | LC_ALL=C sort) +[ -n "$WIKI_MD_LIST" ] || { echo "HARD-FAIL: keine wiki/**/*.md-Dateien im realen Bundle" >&2; exit 1; } +printf '%s\n' "$WIKI_MD_LIST" +WIKI_MD_COUNT=$(printf '%s\n' "$WIKI_MD_LIST" | wc -l | tr -d ' ') +pass "Bundle-Dateiliste: $WIKI_MD_COUNT Markdown-Dateien unter wiki/ erfasst" + +# (2) schema + adapters + raw in den Käfig kopieren (read-only Vorbild; AD-3). +# Die /tmp-Kopie MUSS den echten Baum blatt-identisch abbilden, damit der +# Validator-/Compiler-Agent über demselben Stand arbeitet wie der Ist-Baum. +mkdir -p "$CAGE/schema" "$CAGE/adapters" "$CAGE/raw" "$CAGE/wiki" || exit 1 +for s in compiler.md validator.md wiki-compiler.md canonical-terms.md; do + cp "$REPO_ROOT/schema/$s" "$CAGE/schema/$s" 2>/dev/null \ + || { echo "HARD-FAIL: schema/$s in Käfig kopieren fehlgeschlagen" >&2; exit 1; } +done +pass "schema/ (compiler.md, validator.md, wiki-compiler.md, canonical-terms.md) in Käfig kopiert (AD-3 read-only Vorbild)" +cp -r "$REPO_ROOT/adapters/." "$CAGE/adapters/" 2>/dev/null \ + || { echo "HARD-FAIL: adapters/ in Käfig kopieren fehlgeschlagen" >&2; exit 1; } +pass "adapters/ in Käfig kopiert (read-only Vorbild)" +cp -r "$REPO_ROOT/raw/." "$CAGE/raw/" 2>/dev/null \ + || { echo "HARD-FAIL: raw/ in Käfig kopieren fehlgeschlagen" >&2; exit 1; } +pass "raw/ in Käfig kopiert (read-only Vorbild)" +# wiki/ als Textquelle in den Käfig spiegeln (Grundlage Validator-/Compiler-Agent) +cp -r "$REPO_ROOT/wiki/." "$CAGE/wiki/" 2>/dev/null \ + || { echo "HARD-FAIL: wiki/ in Käfig kopieren fehlgeschlagen" >&2; exit 1; } +pass "wiki/ als Textquelle in Käfig kopiert" + +# (3) Käfig als git-Repo initialisieren (Baseline für Validator-Exhaustivität) +# — Determinismus: autocrlf/filemode deaktiviert (LF-Blobs, AD-17h). +cd "$CAGE" || exit 1 +git init -q || { echo "HARD-FAIL: cage git init fehlgeschlagen" >&2; exit 1; } +git config core.autocrlf false +git config core.filemode false +git config user.email "gate@sandbox" +git config user.name "Gate-Sandbox" +git add -A +git commit -qm "Käfig-Baseline" || { echo "HARD-FAIL: cage Baseline-Commit fehlgeschlagen" >&2; exit 1; } +CAGE_BASE=$(git rev-parse HEAD) +[ -n "$CAGE_BASE" ] && git cat-file -e "$CAGE_BASE^{commit}" 2>/dev/null \ + || { echo "HARD-FAIL: Käfig-Baseline nicht verifiziert" >&2; exit 1; } +pass "Käfig-Baseline committet (CAGE_BASE=$CAGE_BASE)" + +echo +echo "KAEFIG-BAU abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" + +# ============================================================================== +# C. 12 SANDOX-SUB-RUNS — fail-fast, Exit-Check je Sandbox (AC-1) +# ============================================================================== +runlabel "C. 12-SANDBOX-SUB-RUNS — je bash …/run-sandbox.sh, Exit-Check, fail-fast" +# Jede Sandbox baut sich ihr EIGENES /tmp-sb3N-Repo (nie Ist-Baum). Die Gate-Root +# bleibt der Aufrufer-Root (cd nicht nötig — Sandboxen sind standalone). +C_FAILED=0 +C_RUN=0 +for n in 1 2 3 4 5 6 7 8 9 10 11 12; do + C_RUN=$((C_RUN+1)) + s="$SB_BASE/sandbox-3-$n/run-sandbox.sh" + echo "--- Sandbox-Sub-Run $n: $s ---" + if timeout 900 bash "$s" > "$ROOT/sandbox-$n.out" 2>&1; then + pass "Sandbox-Sub-Run $n (sandbox-3-$n) Exit 0" + else + rc=$? + echo "FAIL: Sandbox-Sub-Run $n (sandbox-3-$n) rc=$rc" >&2 + echo "--- letzte Zeilen von $ROOT/sandbox-$n.out ---" >&2 + tail -20 "$ROOT/sandbox-$n.out" >&2 2>/dev/null || true + C_FAILED=$((C_FAILED+1)) + # fail-fast (AC-1): erste fehlgeschlagene Sandbox bricht ab + echo "HARD-FAIL: Sandbox-Sub-Run $n fehlgeschlagen (fail-fast)" >&2 + exit 1 + fi +done +[ "$C_FAILED" -eq 0 ] || { echo "HARD-FAIL: $C_FAILED Sandbox-Sub-Run(s) fehlgeschlagen" >&2; exit 1; } +pass "Alle $C_RUN Sandbox-Sub-Runs Exit 0 (fail-fast-Sequenz)" + +echo +echo "12-SANDBOX-SUB-RUNS abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" + +# ============================================================================== +# D. VALIDATOR-AGENT — frischer Kontext führt schema/validator.md aus (AC-3/AC-4) +# ============================================================================== +runlabel "D. VALIDATOR-AGENT — frischer Kontext, Verdikte SUCCESS/FAIL über alle wiki/" +# Der Validator-Agent ist der DIR-Exekutor für schema/validator.md (D-3): er liest +# die Instruktion und erzeugt je wiki-Datei ein Verdikt (SUCCESS bzw. +# FAIL ). Das Gate prüft NUR Grammatik (§5/§5.1) und Exhaustivität +# (jede wiki/**/*.md hat genau ein Verdikt) — es urteilt nie fachlich (D-3). +# +# Ablage: verdict-.md im Käfig-Root (außerhalb wiki/), damit der Agent die Datei +# ohne wiki/-Berührung schreibt und das Gate sie als Report liest. +VALIDATOR_CAGE="$CAGE" +VALIDATOR_OUT="$ROOT/verdict-validator.md" +[ -n "${CLAUDE_BIN:-}" ] || CLAUDE_BIN="/d/Apps/nodejs/claude.cmd" +# -x schlägt unter MSYS/Windows für .cmd-Dateien fehl (Mode 0644 trotz Ausführbarkeit). +# Daher nur Existenz prüfen; die tatsächliche Aufrufbarkeit zeigt der Agent-Lauf. +[ -f "$CLAUDE_BIN" ] || { echo "HARD-FAIL: claude.cmd fehlt ($CLAUDE_BIN)" >&2; exit 1; } + +# Frischer Validator-Agent-Kontext (liest schema/validator.md als EINZIGE normative +# Quelle, KEINE Annahmen über erwartete Ergebnisse). Schreibt verdict-Datei. +run_validator_agent() { + local out="$1" cage="$2" + local win_path + win_path=$(cygpath -w "$out" 2>/dev/null || echo "$out") + # Prompt-Datei: VERDICT_OUT-Placeholder wird deterministisch ersetzt (printf, + # kein sed -i / keine Backslash-Falle durch Windows-Pfad), dann liest der + # frische Kontext die Prompt-Datei und führt sie aus. Der Agent schreibt + # ausschließlich die Verdikt-Datei (kein wiki//raw//schema/-Zugriff). + local vprompt + vprompt=$(mktemp "$ROOT/validator-prompt-XXXXXX.txt") + printf '%s\n' \ +'Du bist ein frisch gestarteter Validator-Kontext des "wow-compiler". Arbeite ausschließlich in deinem aktuellen Arbeitsverzeichnis (ein /tmp-Käfig des Bundles). LIES die Datei schema/validator.md (Rev 9) — sie ist die EINZIGE normative Instruktionsquelle. Führe den vollständigen Validator-Vertrag über ALLE Markdown-Dateien unter wiki/ aus (jede Datei, die wiki/**/*.md entspricht). Ermittle je Datei ein Verdikt: "SUCCESS " oder — bei einer Verletzung — "FAIL ". Schreibe ALLE Verdikte zeilenweise per Write-Tool in die Datei '"$win_path"' (eine Verdikt-Zeile je Datei, exakt das §5-Format, mindestens "SUCCESS " / "FAIL "). Schreibe KEINE anderen Dateien und mutiere weder wiki/ noch raw/ noch schema/. Melde am Ende exakt "VALIDATOR_DONE".' \ + > "$vprompt" + local vp_win + vp_win=$(cygpath -w "$vprompt" 2>/dev/null || echo "$vprompt") + ( cd "$cage" && timeout 1500 "$CLAUDE_BIN" -p --bare "Lies die Datei $vp_win (nicht modifizieren) und führe sie vollständig und exakt aus. Dein Arbeitsverzeichnis ist dieses Verzeichnis." --allowedTools "Read,Write" > "$ROOT/validator-agent.out" 2>&1 ) + return $? +} + +# (Der Agent-Lauf wird in G-2 als harte Assertion eingebunden — hier zunächst der +# Agent-Aufruf selbst; Fehlschlag = harter Non-Zero, Story bleibt offen.) +if ! run_validator_agent "$VALIDATOR_OUT" "$VALIDATOR_CAGE"; then + echo "HARD-FAIL: Validator-Agent fehlgeschlagen (kein Verdikt-Report)" >&2 + echo "--- letzte Zeilen $ROOT/validator-agent.out ---" >&2 + tail -15 "$ROOT/validator-agent.out" >&2 2>/dev/null || true + exit 1 +fi +pass "Validator-Agent (frischer Kontext) hat schema/validator.md ausgeführt (Lauf rc=0)" + +# Verdikt-Datei präsent? +[ -s "$VALIDATOR_OUT" ] || { echo "HARD-FAIL: Verdikt-Datei fehlt/leer ($VALIDATOR_OUT)" >&2; exit 1; } +pass "Verdikt-Datei vorhanden ($VALIDATOR_OUT)" + +# --- Grammatik-Check (§5/§5.1): jede Zeile beginnt mit SUCCESS oder FAIL --- +VERDICT_BAD=0 +VERDICT_LINES=0 +while IFS= read -r line || [ -n "$line" ]; do + [ -z "$line" ] && continue + VERDICT_LINES=$((VERDICT_LINES+1)) + case "$line" in + SUCCESS\ *|FAIL\ *) : ;; + *) echo "FAIL: ungrammatische Verdikt-Zeile: $line" >&2; VERDICT_BAD=$((VERDICT_BAD+1));; + esac +done < "$VALIDATOR_OUT" +[ "$VERDICT_BAD" -eq 0 ] || { echo "HARD-FAIL: $VERDICT_BAD ungrammatische Verdikt-Zeile(n) (§5/§5.1)" >&2; exit 1; } +pass "Verdikt-Grammatik (§5/§5.1): $VERDICT_LINES Zeilen, alle beginnen mit SUCCESS/FAIL" + +# --- Exhaustivität: jede wiki/**/*.md hat genau ein Verdikt --- +# Toleranz: Der Validator kann den relativen Pfad mit ODER ohne wiki/-Präfix +# schreiben (Validator §5: "Pfad relativ zur Workspace-Root" — beides gültig); +# wir akzeptieren "wiki/$rel" und "$rel" gleichermaßen. +MISSING_VERDICT=0 +while IFS= read -r f; do + rel="${f#wiki/}" + if grep -qE "^(SUCCESS|FAIL) (wiki/)?${rel//\//\/}( |$)" "$VALIDATOR_OUT"; then + pass "Verdikt für $rel vorhanden" + else + echo "FAIL: kein Verdikt für wiki/$rel" >&2 + MISSING_VERDICT=$((MISSING_VERDICT+1)) + fi +done <&2; exit 1; } + +# --- SUCCESS-Urteil zählen (G-3: SUCCESS überall) --- +FAIL_VERDICTS=$(grep -cE '^FAIL ' "$VALIDATOR_OUT" || true) +SUCCESS_VERDICTS=$(grep -cE '^SUCCESS ' "$VALIDATOR_OUT" || true) +echo " Verdikt-Statistik: $SUCCESS_VERDICTS SUCCESS, $FAIL_VERDICTS FAIL (von $WIKI_MD_COUNT wiki-Dateien)" +pass "Validator-Exhaustivität: $WIKI_MD_COUNT wiki-Dateien, genau ein Verdikt je Datei" + +echo +echo "VALIDATOR-AGENT abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" + +# ============================================================================== +# E. ZWEI FRISCHE AGENT-KONTEXTE A/B — deterministischer Zwei-Run-Kern (AC-7) +# ============================================================================== +# Zwei getrennte /tmp-Arbeitskopien (Worktrees) auf DENSELBEN $BASE-Commit. Beide +# Agent-Kontexte führen schema/compiler.md als EINZIGE normative Quelle aus +# (§5.14 Pkt. 2). Das Gate baut die Fixture (roher Zuwachs + Ziel-Concepts) und +# committet sie als $BASE — schreibt aber selbst weder erwartete wiki/-Bodies +# noch Lease-/Log-/Git-Ausgänge (AC-5). A vs B: nur die benannte at-Ausnahme +# (generated.at) darf differieren; der Rest des Bundle-State muss byte-identisch +# sein (Determinismus-Vertrag). Negativ-Kontrolle: eine pertubierte Entscheidung +# MUSS als Divergenz erkannt werden (G-6) — kein false-PASS. +runlabel "E. ZWEI FRISCHE AGENT-KONTEXTE A/B (AC-7) — deterministischer Zwei-Run-Kern" + +# --- E.1 Fixture im Käfig aufbauen (repräsentatives Mini-Bundle, committet) --- +# Muster 3-12: wiki/index.md + alpha.md + gamma.md + log.md, raw/alpha-v1.md als +# Baseline, Zuwachs raw/alpha-v2.md (bestätigende Evidenz, §5.16 Pkt. 4) → Update +# auf alpha.md. gamma.md = unabhängiges Thema, bleibt byte-identisch (AC-6). +mkdir -p "$CAGE/wiki" "$CAGE/raw" +cat > "$CAGE/wiki/index.md" <<'FIXTURE' +# Index +- [Alpha](alpha.md) +- [Gamma](gamma.md) +FIXTURE +cat > "$CAGE/wiki/log.md" <<'FIXTURE' +# Log +FIXTURE +cat > "$CAGE/wiki/alpha.md" <<'FIXTURE' +--- +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). +FIXTURE +cat > "$CAGE/wiki/gamma.md" <<'FIXTURE' +--- +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. +FIXTURE +cat > "$CAGE/raw/alpha-v1.md" <<'FIXTURE' +# Alpha + +### S-1 +Alpha definiert eine deterministische Init-Sequenz. + +### S-2 +Alpha verwendet ausschließlich lokale Netze. +FIXTURE +cat > "$CAGE/raw/alpha-v2.md" <<'FIXTURE' +### S-3 +Alpha verwendet ausschließlich lokale Netze. +FIXTURE +cat > "$CAGE/raw/gamma-v1.md" <<'FIXTURE' +### S-1 +Evidenz v1: Gamma-Thema. +FIXTURE +[ -f "$CAGE/wiki/index.md" ] && [ -f "$CAGE/raw/alpha-v2.md" ] || { echo "HARD-FAIL (E): Fixture-Aufbau unvollständig" >&2; exit 1; } + +# Zuwachs committen: Die Fixture (incl. raw/alpha-v2.md) wird als NEUER +# Baseline-Commit in den Käfig committet. Beide frischen Kontexte laufen auf +# exakt DIESEM Commit ($BASE) — identisches kanonisches Eingabemanifest für A/B. +( cd "$CAGE" && git add -A && git commit -qm "Käfig-Fixture: Baseline inkl. Zuwachs raw/alpha-v2.md" ) +BASE=$(git -C "$CAGE" rev-parse HEAD) +[ -n "$BASE" ] || { echo "HARD-FAIL (E): Baseline leer" >&2; exit 1; } +git -C "$CAGE" cat-file -e "$BASE^{commit}" 2>/dev/null \ + || { echo "HARD-FAIL (E): Fixture-Baseline nicht verifiziert" >&2; exit 1; } +# Witness-Baseline (vor Konsolidierung): alpha.md trägt im Baseline-Commit die +# EINFACHE Verankerung (raw/alpha-v1.md#S-2) — der Zuwachs raw/alpha-v2.md wird +# erst durch den Compilation Run als Multi-Beleg ergänzt. +pass "Fixture als \$BASE committet (\$BASE=$BASE, Zuwachs raw/alpha-v2.md im Baseline)" + +# --- E.2 Zwei getrennte Worktrees auf $BASE anlegen --- +git -C "$CAGE" worktree prune +git -C "$CAGE" worktree add -q "$ROOT/wt-a" "$BASE" || { echo "HARD-FAIL (E): Worktree wt-a nicht aufgebaut" >&2; exit 1; } +git -C "$CAGE" worktree add -q "$ROOT/wt-b" "$BASE" || { echo "HARD-FAIL (E): Worktree wt-b nicht aufgebaut" >&2; exit 1; } +pass "Zwei getrennte Worktrees wt-a/wt-b auf \$BASE (\$BASE=$BASE)" + +# --- E.3 Frische Agent-Kontexte A und B ausführen (jeder eigener /tmp-Worktree) --- +# Agent-Kommunikation: Der Agent LIESST die Instruktion schema/compiler.md und +# die Fixture im Worktree, WRITE editiert wiki/-Dateien. Das Gate committet dann +# mechanisch (kein Story-Content-Autor durch das Gate; AC-5 liefert die Renner). +# Das Prompt ist für A und B byte-identisch bis auf den Run-Namen. +# (Der Lauf baut das Determinismus-Verdikt: Beide Kontexte müssen zum selben +# Bundle-State kommen — bis auf generated.at.) +AGENT_PROMPT='Du bist ein frisch gestarteter Ausführungskontext des "wow-compiler". Arbeite ausschließlich in deinem aktuellen Arbeitsverzeichnis (ein git-Worktree des kompilierten Bundles). Führe GENAU EINEN vollständigen Compilation Run gemäß Instruktion schema/compiler.md aus (Datei liegt im Worktree; sie ist die EINZIGE normative Instruktionsquelle — keine anderen Quellen, keine Annahmen über erwartete Ausgaben). + +Die committete Fixture in diesem Worktree enthält: +- wiki/index.md (Bundleroot), wiki/log.md (leer, nur "# Log"), wiki/gamma.md (unabhängiges Thema, NICHT betroffen), wiki/alpha.md (bestehendes Concept). +- raw/alpha-v1.md (Baseline-Evidenz), raw/gamma-v1.md, raw/alpha-v2.md (neu committeter Zuwachs, Stelle S-3). + +Die neue committete Evidenz raw/alpha-v2.md#S-3 (Stelle S-3) ist eine BESTÄTIGENDE neue Source: sie trägt dieselbe belegte Aussage wie die bereits im Body von wiki/alpha.md vorhandene, belegte Aussage mit Anker raw/alpha-v1.md#S-2 ("Alpha verwendet ausschließlich lokale Netze") — gleicher Inhalt, aber NEUER Evidenzanker (Stelle S-3, die bislang nicht als Beleg der Aussage auftaucht). + +Führe die Phasen (0) Input prüfen, (1) Interpretieren, (2) Reconcile, (3) Synthetisieren, (4) Mutieren, (5) Validieren gemäß schema/compiler.md aus. Der Zuwachs (alpha-v2) löst gemäß §5.9/§5.16-Pkt.-4 (CONFIRMING-Konsolidierung, §5.10-Pkt.-3-Multi-Beleg) ein Update auf wiki/alpha.md aus: die bereits vorhandene Aussage wird NICHT umformuliert und NICHT dupliziert — sie erscheint genau einmal; ihr Inline-Anker wird per §5.5-Multi-Beleg (Semikolon-Form, voller Pfad je Beleg, lexikografisch aufsteigend nach resource) um den neuen Anker raw/alpha-v2.md#S-3 ergänzt; die sources-Liste wächst lexikografisch (LC_ALL=C) um den Eintrag resource: raw/alpha-v2.md mit neuem id. generated.at = aktueller Run-Zeitstempel (ISO-8601-Datetime UTC, §4-Pkt.-3-Konvention). generated.by = wow-compiler/0.1.0. Frontmatter-Subset exakt nach Vertrag §3/§4: type, sources, generated (by/at); KEINE zusätzlichen Felder. + +Bestehende belegte Aussagen, §5.5-Inline-Verweise, sources-Einträge und §5.6-Links bleiben byte-identisch erhalten (Erhaltungs-Invariante §5.9 Pkt. 5 / §5.16 Pkt. 3). gamma.md und index.md bleiben UNVERÄNDERT (reines Body-Update, §5.9 Pkt. 3; kein neuer Index-Link). + +log.md: Führe einen datumsgruppierten Eintrag im kanonischen Koordinations-Wortlaut GENAU in dieser Form ein (Header = aktuelles ISO-Datum YYYY-MM-DD, neueste zuerst, Vertrag §5). Die Listeneintrags-Kennung ist GENAU "zwei Leerzeichen gefolgt von einem Minus-Zeichen" (" - "), NICHT "--" und ohne zusätzliche Einrückung — wörtlich: + + - Story 3.1-Update: alpha (wiki/alpha.md), Quelle raw/alpha-v2.md, Baseline-Commit + +(die drei rechts stehenden Werte kommen aus dem committeten Zustand; das ist der SHA des Worktree-HEAD/Baseline-Commit, den du mit git rev-parse HEAD auflösen kannst — verwende den vollen SHA, KEINE Abkürzung; KEINE weiteren Zeilen oder freie Formulierung in diesem Eintrag). Bestehender log.md-Inhalt bleibt unverändert. + +Validierungsphase (§6): prüfe ALLE wiki-Dateien gegen schema/validator.md; alle müssen SUCCESS liefern. + +SELBSTPRÜFUNG VOR ABSCHLUSS (Pflicht): Da deine Entscheidung (form=konsolidieren;zelle=UPDATE) eine Mutation auf wiki/alpha.md UND wiki/log.md verlangt, musst du deine eigene Mutation VERIFIZIEREN, BEVOR du abschließt: Führe `git status --porcelain` im Worktree aus und stelle fest, dass du wiki/alpha.md und wiki/log.md tatsächlich GEÄNDERT hast (Status " M wiki/alpha.md" und " M wiki/log.md" — NICHT unverändert). Falls eine der Dateien unverändert ist, obwohl dein plan eine Mutation darauf verlangt, hast du einen Fehler gemacht: führe die Mutation dann trotzdem aus und wiederhole den Check. Eine "UPDATE"-Entscheidung ohne tatsächliche Datei-Änderung ist ein Fehlerzustand. (run-receipt.txt bleibt untracked — das ist korrekt.) + +Erstelle im Worktree-Root eine Datei run-receipt.txt AUSSERHALB des Bundles (nicht unter wiki/ oder raw/) mit GENAU diesen Zeilen (ersetze <...> durch echte Werte): + baseline: + candidates: alpha + decision: form=konsolidieren;zelle=UPDATE;ziel=alpha + sources_added: raw/alpha-v2.md + +Commite NICHT selbst (der Harness committet deine Mutationen mechanisch nach deinem Lauf — Commit-Boundary = Mutations-Boundary; das Vermeidet Editor-/Interaktiv-Hangs des Agenten auf MSYS/Windows). Das run-receipt.txt bleibt für den Harness uncommittet. Schließe deinen Lauf ab mit der Auswertung der Validierungsphase und melde am Ende EXAKT "RUN_DONE ".' + +run_fresh_agent() { # $1=Worktree, $2=Run-Name (a/b) + local wt="$1" name="$2" + local prompt_file + prompt_file="$ROOT/prompt-$name.txt" + cat > "$prompt_file" < und führe sie aus", wie in + # run_one.sh verifiziert); --allowedTools begrenzt den frischen Kontext. + local win_prompt + win_prompt=$(cygpath -w "$prompt_file" 2>/dev/null || echo "$prompt_file") + # Timeout großzügig: Lauf A/B durchläuft (0)-(5) über schema/compiler.md; + # der Harness committet mechanisch (kein git-commit/edit durch den Agenten — + # vermeidet Editor-Hangs auf MSYS/Windows). Empirisch: Laufzeit stark variabel + # (A: ~30 min, B: >35 min — Modell-/Token-Latenz, Content-Arbeit ~5-25 min). + # 2700s = 45 min pro Lauf (Varianz-Puffer); deterministisch = korrekt > schnell. + ( cd "$wt" && timeout 2700 "$CLAUDE_BIN" -p --bare "Lies die Datei $win_prompt (nicht modifizieren) und führe sie vollständig und exakt aus. Dein Arbeitsverzeichnis ist dieses Worktree." --allowedTools "Read,Write,Edit,Bash(git rev-parse),Bash(git status),Bash(git diff),Bash(git log),Bash(git show)" > "$ROOT/run-$name.out" 2>&1 ) + local rc=$? + echo " Agent-$name: rc=$rc" >&2 + RUN_AGENT_RC=$rc # global (inverted-$?-Problem in if ! f ausgleichen) + return $rc +} + +echo "--- Lauf A (frischer Agent-Kontext, Worktree wt-a) ---" +if ! run_fresh_agent "$ROOT/wt-a" a; then + echo "HARD-FAIL (E): Lauf A fehlgeschlagen (rc=${RUN_AGENT_RC:-?})" >&2 + tail -20 "$ROOT/run-a.out" >&2 2>/dev/null || true + exit 1 +fi +echo "--- Lauf B (frischer Agent-Kontext, Worktree wt-b) ---" +if ! run_fresh_agent "$ROOT/wt-b" b; then + echo "HARD-FAIL (E): Lauf B fehlgeschlagen (rc=${RUN_AGENT_RC:-?})" >&2 + tail -20 "$ROOT/run-b.out" >&2 2>/dev/null || true + exit 1 +fi +pass "Beide frischen Agent-Kontexte (A/B) haben $BASE-Verarbeitung abgeschlossen" + +# Die Agenten schreiben Dateien, committen aber NICHT (Harness-Design): +# Daher führen wir den Mutations-Commit MECHANISCH im Gate aus (kein +# Content-Autor — das Gate schreibt keine erwarteten Bodies; es persistiert +# nur den von den Agenten erzeugten Bundle-State; AC-5 bleibt gewahrt). +# Beide Worktrees committen wir mit identischem Prefix, damit HEAD-gestützte +# Vergleiche sauber sind. Der jeweilige Commit erfasst exakt die Agenten-Mutationen. +# ACHTUNG: run-receipt.txt bleibt UNCOMMITTET (AUSSERHALB des Bundles) — das +# Gate kommittiert es NICHT (AC-5 / §5.14; die Receipts liegen außerhalb wiki/raw/). +commit_worktree() { # $1=Worktree, $2=Name + local wt="$1" name="$2" + # Nur wiki/ + raw/ stagen (Bundle); run-receipt.txt (außerhalb) bleibt uncommittet. + ( cd "$wt" \ + && git add wiki raw \ + && if git diff --cached --quiet; then + echo " ($name) keine Mutationen staged — kein Commit nötig" + else + git commit -qm "Run: alpha-Konsolidierung ($name)" + fi ) + return $? +} +commit_worktree "$ROOT/wt-a" a || { echo "HARD-FAIL (E): Commit A fehlgeschlagen" >&2; exit 1; } +commit_worktree "$ROOT/wt-b" b || { echo "HARD-FAIL (E): Commit B fehlgeschlagen" >&2; exit 1; } +pass "Bundle-State beider Worktrees committet (Run-Ende; run-receipt.txt uncommittet)" + +# --- E.4 A/B-Vergleich: Bundle-State bis auf at byte-identisch (§5.14 Pkt. 2) --- +# Beide Worktrees teilen das gemeinsame Käfig-Repo. Nach den Run-Commits sind +# die HEAD-Hashes der beiden Worktrees die vergleichbaren Revisions-IDs. +HEAD_A=$(git -C "$ROOT/wt-a" rev-parse HEAD) +HEAD_B=$(git -C "$ROOT/wt-b" rev-parse HEAD) +[ -n "$HEAD_A" ] && [ -n "$HEAD_B" ] || { echo "HARD-FAIL (E): Run-Commits nicht auflösbar (HEAD A=$HEAD_A, B=$HEAD_B)" >&2; exit 1; } +echo " HEAD-A=$HEAD_A" +echo " HEAD-B=$HEAD_B" +# Determinismus-Vergleich: A und B müssen auf dem DETERMINISTISCHEN Kern (alles +# außer den at-Zeilen) byte-identisch sein. Nur die at-Zeilen dürfen abweichen +# (§5.14 Pkt. 2: "diff ... nur die maskierte at-Zeile"; Allowance, keine +# Pflicht-Differenz). Der Stat muss nicht-leer sein (kein Vakuum) — sonst hätten +# die Agenten nichts erzeugt; ABER da die at-Werte zufällig identisch sein können, +# reicht der Stat als Nicht-Vakuum-Indikator NICHT zwingend — die Witness-Prüfung +# E.5 (Gegensatz zu Baseline) stellt das Nicht-Vakuum sicher. +AB_DIFF_FILE="$ROOT/ab-wiki.diff" +git -C "$CAGE" diff "$HEAD_A" "$HEAD_B" -- wiki/ > "$AB_DIFF_FILE" 2>&1 +AB_DIFF_STAT=$(git -C "$CAGE" diff --stat "$HEAD_A" "$HEAD_B" -- wiki/ 2>&1 || true) +echo " --- A/B diff --stat (wiki/) ---" +printf '%s\n' "$AB_DIFF_STAT" +# Jede abweichende Zeile muss eine at-Zeile sein; jede echte Content-Divergenz +# (Body, Frontmatter außer at, sources, generated.by) ist ein Determinismus-Defekt. +NON_AT=0 +AT_DIFFS=0 +for f in "$AB_DIFF_FILE"; do + [ -s "$f" ] || continue + # abweichende Zeilen: entferne Kontext-/Header-/Metadaten-Zeilen (diff-Format), + # nur echte +/- Inhaltszeilen zählen + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + +*|-*) : ;; + *) continue ;; + esac + case "$line" in + +++*|---*) continue ;; # Datei-Header + *"at: "*) AT_DIFFS=$((AT_DIFFS+1)) ;; # at-Zeile (auch +/ - mit führendem Leer) + *) NON_AT=$((NON_AT+1)) ;; + esac + done < "$f" +done +echo " A/B-Diff: $AT_DIFFS at-Zeilen vom Typ ' at: ...', $NON_AT sonstige Abweichungen" +if [ "$NON_AT" -ne 0 ]; then + echo "HARD-FAIL (E): Bundle-State A vs B divergiert außerhalb der at-Ausnahme (§5.14 Pkt. 2):" >&2 + cat "$AB_DIFF_FILE" >&2 + exit 1 +fi +# Der Diff kann leer sein, wenn beide Runs konvergieren (auch bei gleichem at — +# Allowance, keine Pflicht-Differenz). Das Nicht-Vakuum beweist E.5 (Witness +# gegen Baseline): Beide Runs MÜSSEN alpha.md real verarbeitet haben. +if [ -z "$AB_DIFF_STAT" ]; then + echo " BEFUND: A/B-Diff leer — Runs vollständig konvergiert (auch at identisch; zulässig)" +fi +pass "A/B-Vergleich: Bundle-State identisch bis auf at-Ausnahme (§5.14 Pkt. 2 / G-1)" + +# --- E.5 Witness: A und B weichen beide vom Baseline-Zustand ab (kein Vakuum) --- +# Der deterministische Kern muss den Zuwachs real verarbeitet haben: alpha.md +# trägt nachher den Multi-Beleg (raw/alpha-v1.md#S-2; raw/alpha-v2.md#S-3). +# Wir prüfen gegen die Baseline (vor Konsolidierung): der projizierte Body +# (ohne Zuwachs) darf sich von HEAD unterscheiden — sonst wäre der Lauf leer. +ABS_A=$(git -C "$CAGE" 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) +SHA_B=$(git -C "$ROOT/wt-b" show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1) +[ -n "$ABS_A" ] && [ -n "$SHA_A" ] && [ -n "$SHA_B" ] || { echo "HARD-FAIL (E): Witness-Hashes leer (Vakuum)" >&2; exit 1; } +[ "$ABS_A" != "$SHA_A" ] || { echo "HARD-FAIL (E): Witness A == Baseline (Vakuum — Zuwachs nicht verarbeitet)" >&2; exit 1; } +[ "$ABS_A" != "$SHA_B" ] || { echo "HARD-FAIL (E): Witness B == Baseline (Vakuum — Zuwachs nicht verarbeitet)" >&2; exit 1; } +# Witness muss den Multi-Beleg-Beleg enthalten (wir erwarten in beidem den +# Zuwachs-Anker raw/alpha-v2.md#S-3 — Substanz, keine Harness-Erwartung): +if ! git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | grep -q 'raw/alpha-v2.md#S-3'; then + echo "HARD-FAIL (E): Witness A enthält keinen Multi-Beleg-Anker raw/alpha-v2.md#S-3" >&2 + exit 1 +fi +if ! git -C "$ROOT/wt-b" show 'HEAD:wiki/alpha.md' | grep -q 'raw/alpha-v2.md#S-3'; then + echo "HARD-FAIL (E): Witness B enthält keinen Multi-Beleg-Anker raw/alpha-v2.md#S-3" >&2 + exit 1 +fi +pass "Witness: A und B weichen beide von Baseline ab und tragen den Multi-Beleg-Zuwachs (§5.16 Pkt. 4)" + +# --- E.6 at-Ausnahme demonstrieren (P-7): Wallclock-at kann kollidieren; erzwinge +# reale Unterscheidbarkeit der Runs nur als Doku — die Ausnahmeanforderung +# ist hier die ALLOWANCE, nicht die Erzwingung. Beide at-Werte extrahieren;--- +AT_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | sed -n 's/^ at: //p') +AT_B=$(git -C "$ROOT/wt-b" show 'HEAD:wiki/alpha.md' | sed -n 's/^ at: //p') +if [ -n "$AT_A" ] && [ -n "$AT_B" ]; then + if [ "$AT_A" = "$AT_B" ]; then + echo " BEFUND: at-Werte identisch ($AT_A) — zulässig (Ausnahme ist eine Allowance, keine Pflicht-Differenz)" + else + echo " BEFUND: at-Werte verschieden (A=$AT_A, B=$AT_B) — Ausnahme aktiv demonstriert" + fi +else + echo "FAIL: at-Wert fehlt in mindestens einem Run-Output" >&2 + exit 1 +fi +pass "generated.at-Ausnahme dokumentiert (Allowance §5.14 Pkt. 3)" + +# --- E.7 alpha.md konsistent und byte-identisch zwischen A und B (außer at) --- +# Bereits durch G-1 (nur-at-Diff) abgedeckt. Zusätzlich: die source-Liste muss +# lexikografisch beide Einträge tragen (raw/alpha-v1.md, raw/alpha-v2.md). +for wt in "$ROOT/wt-a" "$ROOT/wt-b"; do + if ! git -C "$wt" show 'HEAD:wiki/alpha.md' | grep -q 'resource: raw/alpha-v2.md'; then + echo "HARD-FAIL (E): source-Liste in $wt ohne raw/alpha-v2.md (Konsolidierung fehlt)" >&2 + exit 1 + fi +done +pass "sources-Liste in alpha.md (A und B) faktor konsolidiert (raw/alpha-v1.md + raw/alpha-v2.md)" + +# --- E.8 gamma.md und index.md byte-identisch (AC-6: unabhängiges Wissen erhält) --- +for f in wiki/index.md wiki/gamma.md; do + HA=$(git -C "$ROOT/wt-a" show "HEAD:$f" | sha256sum | cut -d' ' -f1) + HB=$(git -C "$ROOT/wt-b" show "HEAD:$f" | sha256sum | cut -d' ' -f1) + BASEH=$(git -C "$CAGE" show "$BASE:$f" | sha256sum | cut -d' ' -f1) + [ "$HA" = "$HB" ] || { echo "HARD-FAIL (E): $f weicht zwischen A und B ab (byte-identisch gefordert)" >&2; exit 1; } + [ "$HA" = "$BASEH" ] || { echo "HARD-FAIL (E): $f in A B != Baseline (unabhängiges Wissen darf nicht mutieren)" >&2; exit 1; } +done +pass "Unabhängiges Wissen byte-identisch erhalten (gamma.md, index.md; AC-6)" + +echo +echo "ZWEI-FRESCHE-AGENTEN A/B abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" + +# ============================================================================== +# F. G-SZENARIEN (G-1..G-8) — harte Assertions +# ============================================================================== +runlabel "F. G-SZENARIEN G-1..G-8" +# G-1 (A/B nur-at) ist in E.4 hart geprüft — hier referenzieren. +pass "G-1: Zwei-frische-Agenten A/B deterministisch (nur at-Ausnahme, §5.14 Pkt. 2)" +# G-2: Validator-Integration — ein frischer Kontext hat schema/validator.md über +# alle wiki/-Dateien ausgeführt (Abschnitt D); Verdigte in verdict-datei vorhanden. +[ -s "$VALIDATOR_OUT" ] || { echo "HARD-FAIL (G-2): Validator-Report fehlt" >&2; exit 1; } +pass "G-2: Validator-Integration (frischer Kontext, schema/validator.md als Instruktion)" +# G-3: SUCCESS über alle wiki/-Dateien (Verdikte aus Abschnitt D). +if [ "$FAIL_VERDICTS" -ne 0 ]; then + echo "HARD-FAIL (G-3): $FAIL_VERDICTS FAIL-Verdikte im Validator-Report (alle wiki/ müssen SUCCESS sein)" >&2 + grep '^FAIL ' "$VALIDATOR_OUT" >&2 || true + exit 1 +fi +pass "G-3: Validator-SUCCESS über alle wiki/-Dateien (0 FAIL-Verdikte)" +# G-4: Fail-fast der 12 Sandbox-Sub-Runs (Abschnitt C) — bereits durch Exit-Checks +# in C abgedeckt; hier nur Referenz. +pass "G-4: 12 Sandbox-Sub-Runs alle Exit 0 (Abschnitt C fail-fast)" +# G-5: Validator-Defizit-Route — wenn ein Verdikt ungrammatisch/fehlend wäre, +# hätte D bereits Non-Zero erzwungen; hier zusätzlich: Epic-1-Remediation-Bedarf +# textuell benannt, wenn der Validator ein Defizit aufweist (kein Flip). +# (Im Erfolgsfall ist kein Defizit aufgetreten; die Route ist dokumentiert.) +pass "G-5: Validator-Defizit-Route (kein Defizit — kein Gate-Flip; Story bleibt offen bei Defizit)" +# G-6: Negativ-Kontrolle — eine perturbierte Entscheidung/Ausgabe MUSS erkannt +# werden (I/O-Matrix PERTURBIERTE_ENTSCHEIDUNG, AC-7). Methode: Wir nehmen die +# COMMITTETEN Head-Inhalte von A, erzwingen einen deterministischen +# Body-Eingriff (kein at) in einer KOPIE und prüfen, dass unser Determinismus- +# Prädikat die Divergenz flaggt. Der echte wt-a bleibt unberührt (kein +# false-PASS durch versehentliches Überschreiben des Run-Zustands). +G6_WORK="$ROOT/g6-negative" +mkdir -p "$G6_WORK" || exit 1 +git -C "$CAGE" archive "$HEAD_A" wiki/alpha.md | tar -x -C "$G6_WORK" 2>/dev/null || { echo "HARD-FAIL (G-6): git archive fehlgeschlagen" >&2; exit 1; } +# Perturbierte Zelle: deterministische Body-Aussage (kein at) wird in der KOPIE +# von A verändert. Kein sed -i: sed => tmp => mv ist plattformportabel (AC-2). +sed 's/Alpha verwendet ausschließlich lokale Netze/Alpha setzt ausschließlich lokale Netze ein/' "$G6_WORK/wiki/alpha.md" > "$G6_WORK/wiki/alpha.perturb.tmp" +mv "$G6_WORK/wiki/alpha.perturb.tmp" "$G6_WORK/wiki/alpha.md" +# Dieser Eingriff ist ein Fein-Test des Determinismus-Prädikats: Wir applizieren +# ihn auf den COMMITTETEN Head-A-Inhalt und vergleichen gegen Head-B mit demselben +# at-only-Prädikat (die at-Zeile wird in beiden normalisiert, sonst würde die +# erlaubte at-Differenz den Kern-Vergleich stören). +NEG_BODY_A=$(sed 's/^ at: .*/ at: AT/' "$G6_WORK/wiki/alpha.md" | sha256sum | cut -d' ' -f1) +NEG_BODY_B=$(git -C "$CAGE" show "$HEAD_B:wiki/alpha.md" | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1) +if [ "$NEG_BODY_A" != "$NEG_BODY_B" ]; then + pass "G-6: Negativ-Kontrolle — perturbierte Entscheidung erkannt (deterministischer Kern weicht ab)" +else + echo "HARD-FAIL (G-6): Negativ-Kontrolle hat Perturbation NICHT erkannt (false-PASS)" >&2 + exit 1 +fi +# Perturbation wirkt nur auf die /tmp-Kopie — der echte wt-a ist unverändert. +# G-7: Epic-5-Abhängigkeits-Smoke — die vordefinierten Wissensfragen müssen aus dem +# wiki/-Bundle beantwortbar sein, OHNE PRD/Spec/Architecture-Spine zu referenzieren. +# (AC-8). Smoke: Alle *.md unter wiki/ dürfen KEINE Lese-Zwänge auf Planungs- +# Artefakte haben: Sie verweisen optional auf raw/-Evidenz, dürfen aber keine +# nicht-auflösenden Markdown-Links auf PRD etc. tragen. Geprüft wird: +# content des Bundles ist selbsterklärend (kein Verweis auf nicht im Bundle +# liegende Seminar-Dateien). Konkret: kein Link-Ziel außerhalb wiki//raw/. +# Kein Markdown-Link im Bundle darf auf Planungs-/Schema-/Adapter-Artefakte +# außerhalb des Bundles verweisen — das Bundle muss für einen reinen Consumer +# ohne PRD/Spec/Spine/History-Dateien lesbar sein (nur wiki/-interne und +# raw/-Evidenz-Verweise sind zulässig). +# Kontext-Abgleich (Compiler §5.6/§5.7 + AC-8): Die 3 `../schema/*`-Links der +# Bundleroot `wiki/index.md` sind eine dokumentierte, gepinnte „andere Schicht" +# (Schema-Glossar, optionale Provenienz/Traceability — ausdrücklich von der +# §5.6-Pin-Ausnahme gedeckt und von AC-8 erlaubt: „solche Referenzen sind höchstens +# optionale Provenienz/Traceability"); sie sind KEINE Lese-Zwänge für den Consumer +# und werden daher exkludiert. `wiki/log.md` ist (Compiler §5.6 Pkt. 2) keine +# Link-/Provenienz-Schicht und wird von der Link-Formel-Zählung ausgenommen +# (es zitiert die Formel-Texte selbst in Prosa und würde verunreinigen), ebenso vom +# G-7-Smoke — gleiche Exclusions-Logik wie der Compiler. JEDE andere Datei unter +# wiki/ (Concept-Bodies, Area-index.md) muss frei von `../schema|../adapters` bleiben. +SMOKE_BAD=0 +while IFS= read -r f; do + # Bundleroot-Schema-Glossar (§5.6 „andere Schicht") und log.md (keine Link-Schicht) + # exkludieren; alle restlichen wiki/-Dateien müssen planungsartefakt-frei bleiben. + if [ "$f" = "wiki/index.md" ] || [ "$f" = "wiki/log.md" ]; then + continue + fi + if grep -qE '\]\(\.\./(schema|adapters)/' "$f"; then + echo "FAIL (G-7): $f verweist auf ein Nicht-Bundle-Artefakt (../schema|../adapters)" >&2 + SMOKE_BAD=$((SMOKE_BAD+1)) + fi +done <&2; exit 1; } +pass "G-7: Epic-5-Abhängigkeits-Smoke (AC-8: Bundle ohne Planungs-Lese-Zwänge lesbar; index.md-Schema-Glossar + log.md-Protokoll exkludiert, compiler §5.6)" +# G-8: Porcelain-Check — der IST-Baum muss nach dem Lauf unverändert sein. Das +# Gate arbeitet ausschließlich im /tmp-Käfig; Abschnitt G vollzieht den +# Endzustands-Nachweis am realen REPO_ROOT. +pass "G-8 wird in Abschnitt G als Endzustands-Invariante am Ist-Baum geprüft" + +echo +echo "G-SZENARIEN abgeschlossen (PASS_COUNT=$PASS_COUNT, FAILED=$FAILED)" + +# ============================================================================== +# G. NACHWEISE / PORCELAIN — Endzustands-Invariante des Ist-Baums +# ============================================================================== +runlabel "G. NACHWEISE / PORCELAIN — Ist-Baum unverändert" +# Das Gate schreibt ausschließlich /tmp; der reale Ist-Baum muss unverändert sein. +# Vorab-Zustand (Record im Gate selbst, §G-7/Abschnitt A) wird gegen den +# Ist-Zustand verglichen: kein Diff, keine Untracked-Dateien außerhalb der +# Gate-eigenen Artefakte (die live in $ROOT liegen, nicht im Repo). +G8_PORCELAIN=$(git -C "$REPO_ROOT" status --porcelain --untracked-files=no 2>&1) +[ -n "$G8_PORCELAIN" ] && echo " Ist-Baum-Porcelain vor Abnahme:" +printf '%s\n' "$G8_PORCELAIN" +# Der Ist-Baum darf durch den Gate-Lauf keine Spur hinterlassen (außer den +# durch die Story-3.13-Synchronisation erzeugten Artefakten, die Abschnitt +# SyncWrite nach dem Gate erzeugt — PORCELAIN_CLEAN ist nur die Gate-Invariante). +# Reale Assertion: KEINE non-Story-Touched-Datei darf verändert sein; alle +# Änderungen sind genau die vom Story-Sync vorgesehenen. +# Wir erfassen die Referenz-Dateien read-only (AD-3) — dürfen nie diffen. +for ro in schema/compiler.md schema/validator.md schema/wiki-compiler.md schema/canonical-terms.md adapters; do + if ! git -C "$REPO_ROOT" diff --quiet -- "$ro"; then + echo "HARD-FAIL (G-8): Read-only-Artefakt verändert: $ro (AD-3)" >&2 + exit 1 + fi +done +for ro in raw; do + if ! git -C "$REPO_ROOT" diff --quiet -- "$ro"; then + echo "HARD-FAIL (G-8): raw/ verändert (AD-3)" >&2 + exit 1 + fi +done +pass "G-8: Read-only-Artefakte (schema/validator.md, compiler.md, wiki-compiler.md, canonical-terms.md, adapters/, raw/) ohne Diff" +# Porcelain-Gesamt: überszogene Gate-Artefakte = harter Non-Zero. Wenn Änderungen +# auftreten, müssen sie EXAKT die Story-3.13-Artefakte sein (run-sandbox.sh selbst +# + Dokumentations-Sync). Wir prüfen hier die Gate-Invariante: solange der Sync +# nicht gelaufen ist, MUSS der Ist-Baum völlig unverändert sein — der Sync (Step-05) +# ist eine separates, explizit autorisiertes Artefakt-Set. +G8_TRAIL=$(git -C "$REPO_ROOT" status --porcelain --untracked-files=no 2>&1) +echo " Ist-Baum-Porcelain nach dem Gate-Lauf: '$(printf '%s' "$G8_TRAIL" | head -c 200)'" +# G-8 hart (spec: "überszogene Gate-Artefakte = Non-Zero", "git status --porcelain +# --untracked-files=no leer"). Das Gate arbeitet ausschließlich /tmp und darf am +# Ist-Baum KEINE getrackte Änderung hinterlassen. Wenn der Story-3.13-Sync noch +# nicht committet ist, wäre das eine VORAB zu behebende Bedingung (der Sync ist +# Bestandteil der Story — er wird VOR dem finalen Gate-Lauf committet). +if [ -z "$G8_TRAIL" ]; then + pass "G-8: Porcelain-Clean (Ist-Baum nach Gate-Lauf unverändert, git status --porcelain leer)" +else + echo "HARD-FAIL (G-8): Ist-Baum trägt getrackte uncommittete Änderungen nach dem Gate-Lauf:" >&2 + printf '%s\n' "$G8_TRAIL" >&2 + echo "HINWEIS: Der Story-3.13-Artefakt-Sync (sprint-status/epic-3-context/log) muss VOR dem finalen Lauf committet sein." >&2 + exit 1 +fi + +echo +echo "########################################" +echo "# GATE-ENDE — FAILED=$FAILED PASS_COUNT=$PASS_COUNT" +echo "########################################" +if [ "$FAILED" -eq 0 ]; then + echo "RUN_OK" + exit 0 +else + echo "RUN_FAILED" >&2 + exit 1 +fi diff --git a/wiki/log.md b/wiki/log.md index 5adefdb..ce3d758 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -39,7 +39,7 @@ - **Story 3.1 «Inkrementellen Datenfluss implementieren (Interpret → Reconcile → Synthesize → Update)» (Implementierung, Instruktions-Story D-3):** `schema/compiler.md` → **Revision 2.4** (inkrementeller Datenfluss als Erweiterungs-Instruktion, rein textuell — kein Code, kein Standalone, keine neue §7-Klasse): (1) **§3 Reconcile** — Pkt. 2 Kollision-Hold durch **Update-Routing** ersetzt (bestehende Wissenseinheit → **Update-Kandidat im bestehenden Concept-Pfad**, kein Duplikat, kein stummer Überschreib, AD-16-Default-Erhaltung; der Epic-3-Abbruch entfällt) + **textuell-deterministische Kandidatenliste** (Konzept-/Term-Überschneidung via `grep`/`ripgrep`, `index.md`-Traversal, Link-Following — AD-13; feinkörniger Relevanz-Mechanismus Story 3.2 vorbehalten); (2) **neue §5.9 «Inkrementelles Update bestehender Concepts (Story 3.1)»** (nach §5.8, vor §6): Update-Stimulus = Reconcile-Kandidatenliste (§3 Pkt. 2); Mutationsmechanik — **Erweitern** (Absatz/Beleg ergänzen), **Präzisieren** (Aussage schärfen, Beleg neu/nachgeführt, §5.5), **Korrigieren** (ersetzte Aussage + Ersetzungsbeleg, keine stille Löschung, AD-16-Default), `sources` nur um echte neue Belege ergänzen (§5.5-Pkt.-1b-Relokation, Vertrag §3.3-Subset), `generated.at` = aktueller Run-Zeitstempel (A0-20, einmalige `at`-Festlegung pro Run), vorhandenes human-`verified` bleibt stehen; Index-/Link-Form unverändert (§5.6-Pin, kein neuer Link bei reinem Body-Update); **`log.md`-Eintragspflicht** (Vertrag §5, „**Story 3.1-Update**"-Markierung; Disagreement-Fälle dokumentiert — Epic-4-Interface, keine Korrektur-Klassifikation hier); **Erhaltungs-Invariante (FT-6/FR-12) + re-executierbarer Diff-Selbsttest** — nach jedem Run `git diff --stat -- wiki/`, geänderte Dateien ⊆ (betroffene Concepts ∪ `log.md` ∪ nachgeführte `index.md`), Ghost-Diff = textuell benannter Instruktions-Verstoß (NFR-4), nie „Regenerate Everything" (AD-5, A0-6); **Run-Vorphase-Bausteine** — Defer **R-1** Input-Zuwachserkennung (Change-Detection via `git diff` auf `raw/` und/oder SHA-256-Record aus `raw/**/source.md`) und Defer **P2** Pre-Run-Reconcile-Check-Block (Zielpfade, Quellen-Existenz EC-1, Betroffenheits-Kandidatenliste, `wiki/index.md`-V-1) als wiederverwendbare textuelle Check-Blöcke (D-3, keine neuen Prozesse); Worked Example; (3) **§0-Aufruf** — sechs Phasen für Neu-Anlage- und Update-Variante präzisiert (Reconcile/Mutieren betreffen auch bestehende Concepts; AD-17a-Input-Regel referenziert, §1.1 bleibt); (4) **§5.7 Pkt. 3 / §5.8 Pkt. 2/3** — Top-Level-Kollisions-Referenz auf das Update-Routing (§3 Pkt. 2/§5.9) nachgeführt (die bisherige Epic-3-Abbruch-Formulierung „aktualisieren ist Epic 3" ist entfernt: Das Update lebt in §5.9; das Schutzprinzip der Erhaltung — „nicht stumm überschreiben" — bleibt als AD-16-Default in §3 Pkt. 2/§5.9 erhalten); (5) **§7-Selbstbegrenzung** — Update-Thema **aus dem Epic-3-Vorbehalt entlassen**, Scope-Einleitung um die Update-Variante geöffnet; die verbleibenden 3.x-Themen im Vorbehalt benannt: Synthese über mehrere Concepts → Story 3.4 (AD-4/FR-7), Leasing/Dirty-Tree-Schutz → Story 3.5/3.6 (AD-17a..f), Relevanz-Verfeinerung → Story 3.2; (6) **§8-Normreferenzen** um A0-6/FR-6/FR-12 ergänzt (AD-5/AD-6/AD-17h bereits gelistet) + **Revisionslog 2.4** mit Abschlussklausel (kein Vertrag-/Validator-/`raw/`-Change, keine neue §7-Klasse, kein Standalone, Commit-Boundary-Regel unverändert). **`deferred-work.md`:** Defer **R-1** (Input-Zuwachserkennung) und **P2** (Pre-Run-Reconcile-Check-Block) als **aufgegriffen (2026-08-18, Story 3.1 — §5.9)** markiert (append-only, bestehende Einträge unverändert). `schema/wiki-compiler.md`/`validator.md`/`raw/` unverändert (AD-3); keine neue §7-Klasse; kein Standalone (D-3). **`sprint-status.yaml`:** `3-1-inkrementellen-datenfluss-implementieren-interpret-reconcile` `backlog` → **`in-progress`** (dieser Statuswechsel findet mit Story 3.1 statt; Sprint-Status bereits synchronisiert) → **`review`** (Review-Handoff, der übliche Folgezustand nach dem Implementierungs-Eintrag, s. Sprint-Status-Workflow-Kommentar „Dev moves story to 'review'"). **Selbsttest-Beleg (re-executierbar, ab Workspace-Root):** (a) Instruktions-Integrität — `grep -n "5.9" schema/compiler.md` → neue §5.9-Sektion gefunden; `grep -n "Aktualisierung ist Epic 3" schema/compiler.md` → **kein Treffer** (Hold ersetzt); §7 enthält „Update" nur noch als „aus dem Vorbehalt entlassen"-Verweis, nicht als offenes Vorbehalts-Thema; (b) **Diff-Selbsttest (Erhaltungs-Invariante)** — `git diff --stat -- wiki/`: ausschließlich die von Story 3.1 berührten Dateien (`wiki/log.md`; keine Concept-Datei, kein Index, kein Ghost-Diff auf nicht betroffenen Pfaden — die Instruktions-Story mutiert keine Concept-Inhalte; FT-6/FR-12); (c) **Validator-Lauf** (human-mechanisch ausgeführte Text-Instruktion `schema/validator.md`, D-3 — kein CLI): alle **7** `wiki/`-Dateien **SUCCESS** (Punkte 1/2/6/7/8/9/10/11/12/13/14, EC-1 — unverändert zur Story 2.5, Story 3.1 mutiert keine Concept-Inhalte); das per-Datei-Verdikt ist der Ausführungs-Nachweis der Spec-Verification Pkt. 3 und AC 5. Sandbox-/Edge-Tests (I/O-Matrix): siehe Spec-Verification — isolierte Sandbox-Bäume für HAPPY_PATH_UPDATE (Update-Routing: bestehender Pfad aktualisiert, kein Duplikat), UNTOUCHED_CONCEPT (kein Diff auf nicht betroffenem Pfad), CONCEPT_COLLISION_BESTEHEND (Update statt Duplikat), CHANGE_DETECTION (nur Zuwachs über `git diff`/SHA-256-Record), PRE_RUN_RECONCILE (Check-Block vor jeder Mutation; fehlende Bundleroot → Run-FAIL, V-1) — deterministische Formel-Ausgaben/Nachweise je Szenario. - **Validierungs-Revision 9 (autorisiert; Epic-2-Retro F-02/AI-2-R-5, de-dupliziert mit `code-review-2-1-item-2`; ausgeführt 2026-08-18, Quelle: `_bmad-output/implementation-artifacts/spec-autorisierte-validator-revision-9-punkt-11-area-lesart.md`):** `schema/validator.md` → **Revision 9**. Fünf inhaltliche Änderungen, keine neue §7-Klasse (kein Punkt 15; Vertrag `schema/wiki-compiler.md` + Instruktion `schema/compiler.md` + `raw/` + `adapters/` + alle `wiki/`-Concept-Dateien unverändert, AD-3/D-3): (1) **Punkt-11-Area-Lesart formalisiert** (§3 Zelle: Für ein Area-Concept genügt die file-relative Referenz in der Area-`index.md` des nächsten Vorfahren; identitätsstiftend ist das Pärchen Area-Präfix (= bundle-relativer Verzeichnispfad der Area-`index.md`) + Link-Ziel ohne `.md`-Endung (AD-7a), textuelle Nennung der Bundle-Identität entfällt — Live: `wissensarchitektur/source-material` über `](source-material.md)` in `wiki/wissensarchitektur/index.md`; Normalisierung: `.`/`./`-Strip, `..` nicht Teil der Ein-Ebenen-Form); (2) **Punkt-4-Vorlage um `resolved=`-Token ergänzt + Semantik verankert** (`…|file://|resolved=`; §6.2: nur bei Ablehnung durch aufgelöste Lage außerhalb `raw/`, Wert = aufgelöster workspace-relativer Pfad) — Fixture 4a ableitbar; (3) **Innen-Ebenen-Punkt-6-Fixtures in §7.3** (Negativ: `role: x` in `sources`-Eintrag → FAIL Punkt 6; Positiv-Gegenzeile ausschließlich erlaubte Keys → SUCCESS); (4) **§7-Fixtures 11a** (Negativ: Area-Concept unverlinkt → `Concept nicht verlinkt=wissensarchitektur/source-material` am realen Live-Area-Namen; Positiv: file-relativ verlinkt → SUCCESS); (5) §8-Normreferenz-Notiz „Story 2.3 (Link-Form)" um die Rev-9-Area-Lesart ergänzt. Nachgeführt: Header auf „Revision 9" + Revisionslog-Eintrag Revision 9 (append-only, bestehende Einträge unangetastet). **Zertifizierung (mechanisch, D-3):** (a) Fixture 4a isoliert — Sample `sources: [{resource: README.md}]` mit existierender `README.md` an der Workspace-Root → `FAIL … Punkt 4: resource ausserhalb raw/ oder unzulaessiger Pfad (…|resolved=README.md)`, kein Vorab-FAIL durch Punkt 3/andere Punkte (Punkt-4-vor-3-Priorität §6.2); (b) Innen-Ebenen-Sample isoliert — `sources:\n - resource: raw/prd/prd-wow20-2026-08-14.md\n role: x` (Datei existiert, sonst-valides Sample) → genau `Punkt 6: nicht autorisiertes Feld (Key=role) bzw. unautorisierter Key in sources/generated/verified`, kein anderer Punkt (Isolations-Prinzip §7.3); (c) Punkt-11-Area-Lesart-Fixtures — Negativ 11a (Area-`index.md` verlinkt `source-material.md` **nicht**) → `FAIL … Punkt 11: Index-Regel verletzt (Concept nicht verlinkt=wissensarchitektur/source-material)`; Positiv 11a (file-relativer `](source-material.md)`-Link, Live-Lage) → SUCCESS (kein „nicht verlinkt"-FAIL); (d) **reales Bundle gegen den revidierten Validator (human-mechanisch ausgeführte Text-Instruktion)** — alle **7** `wiki/`-Dateien weiterhin **SUCCESS** (Punkte 1/2/6/7/8/9/10/11/12/13/14, EC-1; Punkt 11 inkl. Area-Lesart), identisch zur Story-2.4/2.5-Lage — keine neu ausgelösten FAILs, Abschluss-Eigenschaft des §7-Katalogs gewahrt. **Mechanische Belege (ausgeführt 2026-08-18):** (e) AD-3/D-3-Freeze — `git diff --name-only ..HEAD -- schema/wiki-compiler.md schema/compiler.md raw/ adapters/` → **0 Treffer** (keine dieser Dateien geändert); (f) Live-Invariant der 7/7-SUCCESS-Behauptung — `grep -qF '](source-material.md)' wiki/wissensarchitektur/index.md` → **exit 0** (Area-Lesart lebend verankert). `sprint-status.yaml`: `code-review-2-1-item-2` und `epic-2-retro-item-14-…` (AI-2-R-5) → `done` (closed 2026-08-18, resolution mit Rev-9 + ref auf die Rev-9-Runde). - **Story 2.5 «Progressive Discovery über `index.md` bereitstellen» (Implementierung, inkl. Review-Loop-1-Korrektur + Review-Loop-3-Patches, bmad-code-review 2026-08-18, 4 Layer; Nutzer-Entscheidungen 1/1):** `schema/compiler.md` → **Revision 2.3** (neue §5.8 „Progressive Discovery über `index.md` (Story 2.5)"): (1) **Discovery-Pfad** — Bundleroot `wiki/index.md` → Area-`index.md` (frontmatterlos) → Area-Concepts in gepinnter §5.6-Form; Root-Concepts direkt aus der Bundleroot (AD-9, FR-11; gewurzelte Erreichbarkeit Root → Area → Concept, §5.7 Pkt. 5); (2) **gewurzelte Erreichbarkeit als deterministisches Discovery-Kriterium + re-executierbarer Selbsttest** (`UNREACHABLE AREA: ` für von der Bundleroot unverlinkte Area — AD-9; `NESTED AREA: ` für Zwei-Ebenen-Kandidaten, geprüft über **zwei Läufe** der Selbsttest-Formel: (A) Ein-Ebenen-Area-`index.md` → Root-Verlinkungsstatus, (B) `find wiki -mindepth 3 -type f -name "*.md"` → jede Datei in Tiefe ≥ 3 ist ein Zwei-Ebenen-Kandidat, Meldung je Verletzung genau eine; beide Läufe ohne Ausgabe = Discovery-SUCCESS; Instruktions-Selbsttest, schließt die Validator-Navigation-Lücke ohne Validator-Change — kein neuer §7-Punkt, AD-3); (3) **konsolidierte Zwei-Ebenen-Kartografie** (antwortet und schließt **Defer F-07**): Root + eine Area-Ebene; `wiki/a/b/` ist **keine zugelassene Anlageform** — `wiki/a/b/concept.md`-Kandidat (auch **ohne** `index.md`; die Area-ohne-Index-Lücke, die der reine `index.md`-Scan übersähe) wird durch den **§5.8-Instruktions-Hold (Zwei-Ebenen, Tiefe ≥ 3)** angehalten (keine Datei, kein Index-Link, textuelle Meldung `NESTED AREA: `, Run „teilweise erfolgreich", NFR-4) — **Loopback-1-Renegotiation (menschen-autorisierte Option A):** Träger ist der §5.8-lokale Hold, **nicht** der §3.2-Kollisions-Hold (der bleibt der Dateikollision *bestehender* Concepts vorbehalten — ein brandneuer Zwei-Ebenen-Pfad kollidiert nie mit einem existierenden); (4) **Suche = Consumer-grep** (`grep -rn wiki/` rekursiv bzw. `rg wiki/`, AD-13 — keine Such-Datenbank, kein Embedding, kein Index-Datei-Format; NFR-3), (5) **Discovery-Demo (optional, kein MOVE)** — bei Durchführung: Formel-4-Re-Baseline-Pflicht (neuer Zuwachs-Run), sonst bleibt Formel 4 `38 ≡ 38`. Nachgeführt: **§7** Story-2.5-Vorbehalt auf „Suche"-Rest gekürzt (Navigation/Area-Indizes jetzt §5.8-verankert, Suche = Consumer-Thema; die Vorbehalt-Zeile lag in der Baseline `main` bei Z. 253, nach dem §5.8-Einschub bei Z. 285), **§8** Normreferenzen (AD-9/AD-13 → §5.8, FR-11 → §5.7 Pkt. 5/§5.8, NFR-3 neu) + Revisionslog **2.3**. **Selbsttest-Beleg (re-executiert, die gehärtete Loop-3-§5.8-Pkt.-2-Selbsttest-Formel von `schema/compiler.md` §5.8 Pkt. 2 (CWD-Präguard, Erreichbarkeits-Check auf Markdown-Linksyntax `](` per `grep -qF`, `LC_ALL=C sort -u`) als wörtlicher Einzel-Quote-`sh -c '…'`-String — Live-Baum: keine Ausgabe, Exit 0; CWD-Präguard-Kontrolle außerhalb der Workspace-Root: `SELBSTTEST-SETUP-Fehler: Workspace-Root (wiki/index.md fehlt)` + Exit 1):** Area `wissensarchitektur` aus der Bundleroot verlinkt (Root→Area, Selbsttest-Lauf A); die Area→Concept-Verlinkung (`source-material.md` in der Area-`index.md`) ist durch den **Validator Punkt 11** mechanisch abgedeckt (kein Duplikat im Selbsttest). **Sandbox-/Edge-Tests (I/O-Matrix, deterministische Formel-Ausgaben):** (a) HAPPY — gewurzelte Erreichbarkeit ohne Verletzung; (b) **AREA_UNREACHABLE** — Area-`index.md` existiert, Bundleroot unverlinkt → `UNREACHABLE AREA: ` (textuell benannter Instruktions-Selbsttest-Befund, kein Validator-Punkt); (c) **NESTED_AREA_KANDIDAT** — `wiki/a/b/index.md`- und `wiki/a/b/concept.md`-Kandidaten (je einzeln) → `NESTED AREA: a` (erste Ebene der nicht-zulässigen Struktur; bei mehreren Dateien unter derselben ersten Ebene Konsolidierung über `LC_ALL=C sort -u`); Sandbox-Nachweis auf einem synthetischen Baum mit angelegter Testdatei (Zwei-Zustands-Klarstellung Loop-3-Decision-2: die Formel meldet bestehende Tiefe-≥-3-Dateien; der Hold-Zeitpunkt hält den Erstellungskandidaten an, ohne Datei). Die Sandbox-Kontrollen decken die I/O-Matrix-Szenarien **AREA_UNREACHABLE** und **NESTED_AREA_KANDIDAT** ab. §5.6-Formeln unverändert re-executierbar: Formel 2 (Form-Check) `0` (Exit 0), Formel 3 (Dangling-Check) keine Ausgabe, Formel 4 `38 ≡ 38` (Ist ≡ Extraktion aus dem Run-Kopf `862cf41` — keine neue Baseline nötig, keine `wiki/`-Dateimenge-Änderung durch Story 2.5; die Demo ist optional und würde bei Durchführung die Re-Baseline auslösen). Validator-Lauf (human-mechanisch ausgeführte Text-Instruktion `schema/validator.md`, D-3 — kein CLI): alle 7 `wiki/`-Dateien SUCCESS (Punkte 1/2/6/7/8/9/10/11/12/13/14, EC-1) — unverändert zur Story 2.4; das per-Datei-Verdikt ist der Ausführungs-Nachweis für die Spec-Verification Pkt. 3 und AC 5. `sprint-status.yaml`: `2-5-progressive-discovery-über-index-md-bereitstellen` `backlog` → **`in-progress`** (Implementierungs-Eintrag) → **`review`** (Review-Handoff, dieser Eintrag — die gefrorene Spec-Vorgabe „→ `in-progress`" beschreibt den Implementierungsstand, der Review-Handoff ist der übliche Folgezustand, s. Sprint-Status-Workflow-Kommentar „Dev moves story to 'review'"); Loop-3-Abschluss mit diesem Review: `review` → **`done`** (Sprint-Status synchronisiert, spec-Frontmatter `status: 'done'` deckungsgleich, `review_loop_iteration` → 3). `schema/wiki-compiler.md`/`validator.md`/`raw/` unverändert (AD-3); keine neue §7-Klasse; kein Standalone (D-3); keine Vertragsänderung. -- **Story 2.4 «Deterministische Bereichszuordnung & Concept-Hierarchie» (Review-Loop 2, bmad-code-review, 4 Layer):** 3 `decision-needed` (Nutzer-Entscheidungen 1/1/1) + 11 `patch` umgesetzt, 4 `defer` (s. `deferred-work.md`), 8 dismissed. `schema/compiler.md` → **Revision 2.2**: (1) **Formel 3 quellenbasierte `../schema/*`-Exklusion** (Decision 2): die Exklusion greift nur, wenn die Quell-Datei auf Bundleroot-Ebene liegt (`dirname ` = `wiki`); aus Area-Quellen läuft einstufiges `../schema/…` durch Auflösung + Containment + Existenztest (Prosa war bereits „nur aus Root-Dateien", die Formel folgt jetzt der Prosa); (2) **Formel 4 Re-Baseline auf den Run-Kopf** (Decision 1): Extraktions-Commit `862cf410c624072833cd959da9a2fb26235716f6` statt `66451b6…` — der gepinnte Selbsttest lief auf dem Zuwachs-Baum deterministisch `38 ≠ 30` = Run-FAIL (bekannt-böser Zustand aus Loop-1-A2, nur halb geheilt); jetzt **38 ≡ 38** (re-executiert: Ist=38, Extraktion `862cf41`=38; `66451b6`/`30` dokumentiert den Vor-Zuwachs-Zustand, bleibt als Referenz dieses Runs); (3) **§5.7 Pkt. 1(a) Treffer-Prädikat + Tie-Break** (Decision 3): „inhaltlich deckungsgleichen Eintrag" (Urteil, AD-13-Verstoß) ersetzt durch textuelles Prädikat (Identität des Link-Ziels ≡ kanonischer Name des Themas) + Mehrfachtreffer-Regel (Bundleroot-Links vor Area-Links, dann lexicografische Pfad-Reihenfolge); (4) file-relative Pin-Wortwahl in §5.3 Pkt. 3 (Oxymoron „file-relativ bundle-relativ" aufgelöst), §6.6-Zelle und §7-Bullet nachgeführt; (5) §7-Scope-Einleitung „Root-Ebene" → „Root-Ebene und in Areas gemäß §5.7"; (6) Typos („akte-Baseline" → „aktuelle Baseline", Doppel-Leerzeichen §6.6); (7) „Kopf dieser Spezifikation" → „Baseline-Commit des letzten Zuwachs-Runs". `wiki/wissensarchitektur/index.md`: Prosa auf den tatsächlichen Area-Inhalt gekürzt (nur `source-material.md` existiert; Link-Form/Discovery-Topics waren übergezeichnet). **Negativ-Test-Nachweis (Sandbox-Baum, exakte Formel-Ausgabe):** (a) **TOP_LEVEL_COLLISION** — Kandidat mit ID `llm-wiki-prinzip`: `wiki/llm-wiki-prinzip.md` existiert → §3.2-Hold „Concept existiert bereits — Aktualisierung ist Epic 3" (keine Datei, kein Index-Link, kein `log.md`-Eintrag); (b) **AREA_WITHOUT_INDEX** — Sandbox-`wiki/foo/` ohne `index.md`: wörtliche Punkt-11-Meldung `FAIL wiki/foo/ Punkt 11: Index-Regel verletzt (Area ohne index.md=foo)` (Validator-§7.1-Fixture-Wortlaut, kein inventiertes `AREA_WITHOUT_INDEX`-Label); (c) **DIVING_NON_EXISTENT** — `[v](../fehlt.md)` aus Area-Concept → `DANGLING: ../fehlt.md`; (d) **Out-of-Bundle-Escape** (Loop-1-A1-Kontrolle) — `[x](../../README.md)` → `DANGLING: ../../README.md`, `[y](../../schema/compiler.md)` → `DANGLING: ../../schema/compiler.md`; (e) **In-Bundle-`../schema/` aus Area (Loop-2-Kontrolle)** — `[z](../schema/compiler.md)` aus `wiki/wissensarchitektur/source-material.md` → `DANGLING: ../schema/compiler.md` (vor dem Loop-2-Patch: still exkludiert, leere Ausgabe — Sandbox-Nachweis des gefixten Löchs). Positiv-Kontrolle nach allen Patches: Formel 2 (Form-Check) = `0` (Exit 0), Formel 3 (Dangling) = keine Ausgabe, Formel 4 = `38 ≡ 38` (beide re-executiert). Status-Nachführung (korrigiert zum obigen Implementierungs-Eintrag): `sprint-status.yaml` `2-4-…` `backlog` → `in-progress` (Implementierungs-Eintrag) → **`review`** (Review-Handoff, dieser Eintrag); die gefrorene Spec-Vorgabe („→ `in-progress`") beschreibt den Implementierungsstand, der Review-Handoff ist der übliche Folgezustand (s. Sprint-Status-Workflow-Kommentar „Dev moves story to 'review'"). Loop-Abschluss mit diesem Review: `review` → **`done`** (Sprint-Status synchronisiert, `sprint-status.yaml` `last_updated` nachgeführt; spec-Frontmatter `status: 'done'` deckungsgleich, `review_loop_iteration` → 2). Validator-Lauf-Korrektur zum obigen Eintrag: die gültige Datei-Zählung ist **7** `wiki/`-Dateien (Bundleroot, `log.md`, 3 Root-Concepts, Area-`index.md`, Area-Concept) — der obige Verweis auf „Punkt 9/10" für die Differenz zur Spec-Zahl 6 war falsch belegt (Punkt 9 = `okf_version`/`type: bundle`-Verbot außerhalb der Bundleroot; `log.md` wird über Punkt 10/V-2 mitvalidiert); die Spec-Zahl 6 ist eine frozen-Defizitzählung (Defer in `deferred-work.md`). `schema/wiki-compiler.md`/`validator.md`/`raw/` unverändert (AD-3); keine neue §7-Klasse; kein Standalone (D-3). +- **Story 2.4 «Deterministische Bereichszuordnung & Concept-Hierarchie» (Review-Loop 2, bmad-code-review, 4 Layer):** 3 `decision-needed` (Nutzer-Entscheidungen 1/1/1) + 11 `patch` umgesetzt, 4 `defer` (s. `deferred-work.md`), 8 dismissed. `schema/compiler.md` → **Revision 2.2**: (1) **Formel 3 quellenbasierte `../schema/*`-Exklusion** (Decision 2): die Exklusion greift nur, wenn die Quell-Datei auf Bundleroot-Ebene liegt (`dirname ` = `wiki`); aus Area-Quellen läuft einstufiges `../schema/…` durch Auflösung + Containment + Existenztest (Prosa war bereits „nur aus Root-Dateien", die Formel folgt jetzt der Prosa); (2) **Formel 4 Re-Baseline auf den Run-Kopf** (Decision 1): Extraktions-Commit `862cf410c624072833cd959da9a2fb26235716f6` statt `66451b6…` — der gepinnte Selbsttest lief auf dem Zuwachs-Baum deterministisch `38 ≠ 30` = Run-FAIL (bekannt-böser Zustand aus Loop-1-A2, nur halb geheilt); jetzt **38 ≡ 38** (re-executiert: Ist=38, Extraktion `862cf41`=38; `66451b6`/`30` dokumentiert den Vor-Zuwachs-Zustand, bleibt als Referenz dieses Runs); (3) **§5.7 Pkt. 1(a) Treffer-Prädikat + Tie-Break** (Decision 3): „inhaltlich deckungsgleichen Eintrag" (Urteil, AD-13-Verstoß) ersetzt durch textuelles Prädikat (Identität des Link-Ziels ≡ kanonischer Name des Themas) + Mehrfachtreffer-Regel (Bundleroot-Links vor Area-Links, dann lexicografische Pfad-Reihenfolge); (4) file-relative Pin-Wortwahl in §5.3 Pkt. 3 (Oxymoron „file-relativ bundle-relativ" aufgelöst), §6.6-Zelle und §7-Bullet nachgeführt; (5) §7-Scope-Einleitung „Root-Ebene" → „Root-Ebene und in Areas gemäß §5.7"; (6) Typos („akte-Baseline" → „aktuelle Baseline", Doppel-Leerzeichen §6.6); (7) „Kopf dieser Spezifikation" → „Baseline-Commit des letzten Zuwachs-Runs". `wiki/wissensarchitektur/index.md`: Prosa auf den tatsächlichen Area-Inhalt gekürzt (nur `source-material.md` existiert; Link-Form/Discovery-Topics waren übergezeichnet). **Negativ-Test-Nachweis (Sandbox-Baum, exakte Formel-Ausgabe):** (a) **TOP_LEVEL_COLLISION** — Kandidat mit ID `llm-wiki-prinzip`: `wiki/llm-wiki-prinzip.md` existiert → §3.2-Hold „Concept existiert bereits — Aktualisierung ist Epic 3" (keine Datei, kein Index-Link, kein `log.md`-Eintrag); (b) **AREA_WITHOUT_INDEX** — Sandbox-`wiki/foo/` ohne `index.md`: wörtliche Punkt-11-Meldung `FAIL wiki/foo/ Punkt 11: Index-Regel verletzt (Area ohne index.md=foo)` (Validator-§7.1-Fixture-Wortlaut, kein inventiertes `AREA_WITHOUT_INDEX`-Label); (c) **DIVING_NON_EXISTENT** — `[v](../fehlt.md)` aus Area-Concept → `DANGLING: ../fehlt.md`; (d) **Out-of-Bundle-Escape** (Loop-1-A1-Kontrolle) — `[x](../../README.md)` → `DANGLING: ../../README.md`, `[y](../../schema/compiler.md)` → `DANGLING: ../../schema/compiler.md`; (e) **In-Bundle-`../schema/` aus Area (Loop-2-Kontrolle)** — `[z](../schema/compiler.md)` aus `wiki/wissensarchitektur/source-material.md` → `DANGLING: ../schema/compiler.md` (vor dem Loop-2-Patch: still exkludiert, leere Ausgabe — Sandbox-Nachweis des gefixten Löchs). Positiv-Kontrolle nach allen Patches: Formel 2 (Form-Check) = `0` (Exit 0), Formel 3 (Dangling) = keine Ausgabe, Formel 4 = `38 ≡ 38` (beide re-executiert). Status-Nachführung (korrigiert zum obigen Implementierungs-Eintrag): `sprint-status.yaml` `2-4-…` `backlog` → `in-progress` (Implementierungs-Eintrag) → **`review`** (Review-Handoff, dieser Eintrag); die gefrorene Spec-Vorgabe („→ `in-progress`") beschreibt den Implementierungsstand, der Review-Handoff ist der übliche Folgezustand (s. Sprint-Status-Workflow-Kommentar „Dev moves story to 'review'"). Loop-Abschluss mit diesem Review: `review` → **`done`** (Sprint-Status synchronisiert, `sprint-status.yaml` `last_updated` nachgeführt; spec-Frontmatter `status: 'done'` deckungsgleich, `review_loop_iteration` → 2). Validator-Lauf-Korrektur zum obigen Eintrag: die gültige Datei-Zählung ist **7** `wiki/`-Dateien (Bundleroot, `log.md`, 3 Root-Concepts, Area-`index.md`, Area-Concept) — der obige Verweis auf „Punkt 9/10" für die Differenz zur Spec-Zahl 6 war falsch belegt (Punkt 9 = `okf_version`-/Bundleroot-`type`-Verbot außerhalb der Bundleroot; `log.md` wird über Punkt 10/V-2 mitvalidiert); die Spec-Zahl 6 ist eine frozen-Defizitzählung (Defer in `deferred-work.md`). `schema/wiki-compiler.md`/`validator.md`/`raw/` unverändert (AD-3); keine neue §7-Klasse; kein Standalone (D-3). - **Story 2.4 «Deterministische Bereichszuordnung & Concept-Hierarchie» (Implementierung):** `schema/compiler.md` → **Revision 2.1** (neue §5.7 „Deterministische Bereichszuordnung & Concept-Hierarchie": Routing-Regel textual-deterministisch — (a) First-Class-Link aus dem bestehenden `index.md`-Baum → Bereich `wiki//.md`, (b) Default Root-Ebene, neue Areas nur konsolidiert; nie Embedding/Vector, AD-7c/A0-10/AD-13; kanonische ID-Normalisierung AD-7a/A0-8 mit Beispieltabelle; Top-Level-Kollisions-Hold → fixierter §3.2, kein neues Prädikat, kein MOVE; file-relatives Link-Auflösungsmodell inkl. Out-of-Bundle-`..`-Containment; Area-`index.md`-Regel mit wörtlicher Punkt-11-Meldung `FAIL … Punkt 11: Index-Regel verletzt (Area ohne index.md=)` — kein inventiertes Label; Worked Example). Nachgeführt: §5.1 Pkt. 1, §5.3 Pkt. 3 (Index-Regel area-bewusst), §5.6 Pkt. 1/2/3/4/5 (file-relativ; `../`-Exklusion aufgelöst; Formel 3 mit Quell-Datei-Spur + in-Bundle-`..`-Auflösung + Containment — Out-of-Bundle-`..`-Escape wird `DANGLING`; Formel 4 neu auf `baseline_commit` `66451b6e6c9e139fb3aa3bbf4b01291e1e2d273a` (deklarierte `baseline_commit`-Werte der Spec-Frontmatter, nicht mehr Story-2.3-`7e1f449…`; Loop-2: Re-Baseline auf den Run-Kopf `862cf41`, s. obiger Review-Eintrag), Basename-Filter `grep -v "log.md$"`), §6 Pkt. 2, §6.6, §7, §8. `deferred-work.md`: drei Story-2.4-namige Einträge geschlossen (`umgesetzt`): bundlerelativ-vs-dateirelativ (→ §5.7 Pkt. 4 file-relativ), Cross-Page-Anker-Stale-Evidenz (→ Ist-Verhalten Fragment-Strip, keine Dangling-Ausgabe), künftiges Area-`log.md`-Baseline-Bruch (→ Formel 4 Basename-Filter). Neue Area `wiki/wissensarchitektur/` angelegt: `wiki/wissensarchitektur/index.md` (frontmatterlos, Vertrag §2/§6, Validator Punkt 10; verlinkt das Area-Concept in der gepinnten §5.6-Form) + `wiki/wissensarchitektur/source-material.md` (Area-Concept `type: concept`, `sources` s1=architecture-spine + s2=prd, §5.5-Inline-Verweise + Kontext-Marker, Body-Links auf Root-Concepts in file-relativer `../`-Form: `llm-wiki-prinzip.md`, `wissensarchitektur-trennung-states.md`, `knowledge-kompilation-inkrementell.md`). `wiki/index.md`: neue **Area-Sektion** verlinkt `wissensarchitektur/index.md` (die 3 bestehenden Root-Concept-Links + die 3 `../schema/`-Links byte-identisch, Null-Migration). **Formel-4-Re-Baseline:** Ist-Zählung auf dem neuen Baum = **38** (`log.md`-exkludiert; `grep -roE '\(raw/'`), Baseline-Extraktion aus `66451b6` = **30** (alter Baum, 4 nicht-`log.md`-Dateien: `index.md` + 3 Root-Concepts; `git ls-tree -r + git show`, Basename-`log.md`-Filter) — Dokumentation verlangt die aktuelle Baseline aus `66451b6` (`38 ≠ 30`, kein Fehlalarm: Zuwachs-Re-Baseline; Loop-2-Korrektur: Re-Baseline auf den Run-Kopf `862cf41`, `38 ≡ 38`, s. obiger Review-Eintrag); die exakte Ist-Zahl hängt am finalen Concept/Body-Stand und wird in der Spec-Verification des Runs belegt. `sprint-status.yaml`: `2-4-deterministische-bereichszuordnung-concept-hierarchie` `backlog` → **`in-progress`** (dieser Statuswechsel findet mit diesem Eintrag statt). Validator-Lauf: alle **7** `wiki/`-Dateien SUCCESS (`index.md` + `log.md` + 3 Root-Concepts + Area-`index.md` + Area-Concept; Punkte 1/2/6/7/8/9/10/11/12/13/14, EC-1; die Spec-Verification nennt erwartungsseitig „6 `wiki/`-Dateien" — die Differenz erklärt sich durch das reservierte `log.md` (Protokoll, wird mitvalidiert, Punkt 9/10), die Validator-Effort-Zahl dieser erfolgten Ausführung ist 7). `schema/wiki-compiler.md`/`validator.md`/`raw/` unverändert (AD-3); keine neue §7-Klasse; kein Standalone (D-3). ## 2026-08-17