feat: Story 3.8 Review-Loop-3-Abschluss (bmad-code-review Re-Run, 4 Layer; kein Loopback)
Triage: 4 decision-needed / 13 patch / 6 defer / 7 dismissed (u. a. Blind-Hunter "alle Sandbox-SRO-Anker stale" widerlegt — alle 6 exakt vor den Loop-3-Edits). Entscheidungen D-1..D-4 (alle Option 1): D-1 Schliessungs-Bullet macht den "Bekannte Determinismus-Luecke"-Alt-Bullet superseded (append-only-Hinweis, kein Umbruch); D-2 Story-Status-Flip auf done (Change-Proposal-Sequenzierung betrafft das Epic-3-Abnahmegate 3.13, nicht die 3.8-Verankerung); D-3 autorisierte Neu-Verhandlung in geringfuegischem Umfang (Typo "documenthuman" -> "document-human", Spec Re-Open-Delta-Zeile); D-4 Hold-Home = Story 3.10 (Epic 3), Korrektur-/Erweiterungs-Klassifikation = Epic-4-Interface/Story 4.1 (S5.10 Pkt. 8 praezisiert). Patches P-1..P-13: SRO-/Code-Map-Anker auf IST-Zeilen (compiler.md 375/376/377/477, Code Map 365-377/424ff/445/477, Sandbox 245/265/442/275/447/531); symmetrische Term-/Body-Normalisierung (S3.2 Pkt. 2a + match_stufe_a, kein host-abhaengiges -i); Receipt-Hashvergleich als re-executierbare Zwei-Run-Formel (S5.14 Pkt. 2); Sandbox-Fresh-Kontext-Restluecke benannt (mechanisch simuliert; Nachweis = 3.13-Abnahmegate); Manifest generated_by wird konsumiert; at-Ausnahme hart assertiert (Wiederholungs-Schleife bei Sekundenkollision, HARD-FAIL statt stiller PASS); "voellig pinbar"/"keine offene Frage" ge scopet (Umlaut-Defer + 3.9-ACs bewusst offen); epics.md AC-2 AD-16-Kopplung nachgefuehrt; review_loop_iteration 0 -> 2; epic-3-context.md 3.9/3.11/3.12-Entscheidungen als Zielzustand markiert; AD-17-Enum verifiziert (kein Edit noetig); Review-Loop-3-Change-Log-Eintrag. Sandbox re-executiert: DET-1..DET-8 harte PASS, Exit 0; at-Ausnahme aktiv (assertiert, real unterschiedliche Wanduhr-at-Werte); Witness nicht-vakuum. Spec status: done, review_loop_iteration: 2; sprint-status 3-8 -> done (Sequenzierungs-Hinweis: 3.9-3.12-Verankerungen + 3.13-Abnahme bleiben offen); wiki/log.md Loop-3-Abschluss-Bullet; deferred-work.md 6 neue Defers. AD-3 gewahrt: validator.md/wiki-compiler.md/adapters/raw/ unveraendert. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -125,17 +125,24 @@ norm() { # $1 = Begriff (Kollaps-Form, NICHT registry-aufloesend — Kollaps-Sco
|
||||
# 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
|
||||
# Symmetrische Normalisierung (Review-Loop-3, P-4; §3.2 Pkt. 2a): der Term wird über
|
||||
# norm() (Lowercasing + Kollaps-Klasse [-–— _] -> -, §3.2 Pkt. 1b) normalisiert und der
|
||||
# Body entsprechend lowercased — der Match läuft auf den beidseitig normalisierten Formen
|
||||
# (kein host-abhängiges -i-Flag; Groß-/Kleinschreibung deterministisch über beide Seiten).
|
||||
local term_lc
|
||||
term_lc=$(norm "$term")
|
||||
# Regex-Metazeichen im Begriff literal-escaped (deterministischer, literal-sicherer Vergleich,
|
||||
# §3.2-Pkt.-2a/§5.14 Pkt. 5): ein Term wie "a+b" oder "x.y" darf nie als Muster umgedeutet werden.
|
||||
local esc
|
||||
esc=$(printf '%s' "$term" | sed 's|[][\\.*^$+?(){}|]|\\&|g')
|
||||
esc=$(printf '%s' "$term_lc" | sed 's|[][\\.*^$+?(){}|]|\\&|g')
|
||||
for file in "${@:2}"; do
|
||||
# Body = alles nach dem zweiten "---" (Frontmatter exkludiert, deterministisch);
|
||||
# CRLF-tolerant (Host-Checkout-Form normalisiert), sonst wäre der Wortgrenzen-Match unbestimmt.
|
||||
body=$(tr -d '\r' < "$file" | awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}')
|
||||
# Der Body wird wie der Term lowercased (Symmetrie); die Kollaps-Äquivalenz des Em-Dash
|
||||
# ist damit im Match-Pfad real ausgeübt (norm-then-match, Review-Loop-3, P-4).
|
||||
body=$(tr -d '\r' < "$file" | tr 'A-Z' 'a-z' | awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}')
|
||||
# Wortgrenzen-Match: ganze Wörter — Substring-/Frontmatter-Treffer liefern nichts.
|
||||
# Term-Normalisierung (§3.2 Pkt. 1b) ist lowercasing — der Match ist daher case-insensitiv.
|
||||
if printf '%s' "$body" | grep -Eiq "(^|[^A-Za-z0-9])${esc}([^A-Za-z0-9]|$)"; then
|
||||
if printf '%s' "$body" | grep -Eq "(^|[^A-Za-z0-9])${esc}([^A-Za-z0-9]|$)"; then
|
||||
basename "$file" .md
|
||||
fi
|
||||
done
|
||||
@@ -247,6 +254,7 @@ git worktree add -q "$ROOT/wt-b" "$BASE" || { echo "HARD-FAIL (DET-2): Worktree
|
||||
# Das Manifest wird unten in run2_worktree VOR jedem Lauf konsumiert/validiert (nicht dekorativ).
|
||||
cat > "$ROOT/manifest.yaml" <<EOF
|
||||
baseline: $BASE
|
||||
generated_by: wow-compiler/0.1.0
|
||||
sources:
|
||||
- raw/alpha-v1.md
|
||||
- raw/alpha-v2.md
|
||||
@@ -278,6 +286,11 @@ run2_worktree() { # $1 = Worktree-Pfad ; $2 = Run-Name ; führt die Instruktion
|
||||
[ -n "$m_src" ] || { echo "HARD-FAIL (DET-2): Manifest ohne geordnete Source-Eingaben" >&2; exit 9; }
|
||||
[ -n "$m_identity" ] || { echo "HARD-FAIL (DET-2): Manifest ohne output-sichtbare Identitätswerte" >&2; exit 9; }
|
||||
case "$m_identity" in *generated.at*) ;; *) { echo "HARD-FAIL (DET-2): Manifest muss generated.at als Identitätswert führen (A0-20-Ausnahme)" >&2; exit 9; };; esac
|
||||
case "$m_identity" in *generated.by*) ;; *) { echo "HARD-FAIL (DET-2): Manifest muss generated.by als Identitätswert führen" >&2; exit 9; };; esac
|
||||
# generated.by wird aus dem Manifest KONSUMIERT (nicht hart codiert; Review-Loop-3, P-6):
|
||||
# der output-sichtbare Identitätswert des Laufs ist Manifest-Inhalt, nicht Skript-Konstante.
|
||||
m_by=$(sed -n 's/^generated_by: //p' "$ROOT/manifest.yaml")
|
||||
[ -n "$m_by" ] || { echo "HARD-FAIL (DET-2): Manifest ohne generated_by-Wert" >&2; exit 9; }
|
||||
# Sicherstellen: Worktree am Baseline-Commit, sauber (Q-6: getrennt aufgebaut, sauber).
|
||||
git checkout -qf "$BASE"
|
||||
git clean -qfd wiki raw
|
||||
@@ -307,7 +320,7 @@ sources:
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s3
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
by: $m_by
|
||||
at: $at_now
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
@@ -365,6 +378,24 @@ cat "$ROOT/receipts/run2a.receipt"
|
||||
echo "--- Lauf B (frischer Agent-Kontext, Worktree wt-b) ---"
|
||||
run2_worktree "$ROOT/wt-b" run2b
|
||||
cat "$ROOT/receipts/run2b.receipt"
|
||||
# at-Ausnahme-Demo (Review-Loop-3, P-7): die aktive Ausnahme (real unterschiedliche
|
||||
# Wanduhr-at-Werte) wird ERZWUNGEN, nicht nur gedruckt — bei einer Wanduhr-Sekunden-
|
||||
# kollision wird Lauf B deterministisch wiederholt (frischer Kontext, derselbe Baum),
|
||||
# bis die at-Zellen differieren (begrenzte Wiederholungen; jede Kollision ist ein
|
||||
# Host-Timing-Artefakt, keine Eigenschaft des committeten Zustands). Damit ist der
|
||||
# Change-Log-Beleg „zwei Läufe mit real unterschiedlichen at_cell-Werten" eine
|
||||
# Eigenschaft jeder Re-Execution.
|
||||
at_a=$(grep '^at_cell=' "$ROOT/receipts/run2a.receipt" | cut -d= -f2)
|
||||
for try in 1 2 3 4 5; do
|
||||
at_b=$(grep '^at_cell=' "$ROOT/receipts/run2b.receipt" | cut -d= -f2)
|
||||
[ "$at_a" != "$at_b" ] && break
|
||||
if [ "$try" -eq 5 ]; then
|
||||
echo "HARD-FAIL (DET-2): at-Ausnahme nicht demonstrierbar (5x Sekundenkollision — Host-Timing-Anomalie)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo " at-Kollision (Wanduhr-Sekunde) — Lauf B wird im frischen Kontext wiederholt (Versuch $try)"
|
||||
run2_worktree "$ROOT/wt-b" run2b
|
||||
done
|
||||
# Receipt-Gerüst-Nicht-Vakuum: beide Receipts müssen den deterministischen Zeilenblock tragen
|
||||
# (diff könnte sonst zweifach-leere Dateien als "identisch" PASSen lassen).
|
||||
for r in "$ROOT/receipts/run2a.receipt" "$ROOT/receipts/run2b.receipt"; do
|
||||
@@ -391,8 +422,10 @@ fi
|
||||
[ -n "$at_a" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme-Cell fehlt in receipt A" >&2; exit 1; }
|
||||
[ -n "$at_b" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme-Cell fehlt in receipt B" >&2; exit 1; }
|
||||
[ -n "$tree_a" ] && [ -n "$tree_b" ] || { echo "HARD-FAIL (DET-2): tree-Cell fehlt in einem Receipt" >&2; exit 1; }
|
||||
[ "$at_a" = "$at_b" ] && [ "$tree_a" = "$tree_b" ] && echo " BEFUND: at-Grenzfall — beide Läufe tragen identischen at-Wert (Wanduhr-Sekundenkollision); die Ausnahme (A0-20) erlaubt Abweichung, verlangt sie aber nicht (deterministischer Vergleich bleibt gültig)"
|
||||
[ "$at_a" != "$at_b" ] && echo " BEFUND: at-Ausnahme aktiv — Lauf A at=$at_a, Lauf B at=$at_b (benannte Ausnahme, §5.14 Pkt. 3: übrige Bestandteile byte-identisch)"
|
||||
# P-7-Assertion: die aktive Ausnahme ist jetzt hart belegt (Wiederholungs-Schleife oben
|
||||
# hat at_a != at_b erzwungen) — kein stilles „BEFUND: at-Grenzfall" mehr.
|
||||
[ "$at_a" != "$at_b" ] || { echo "HARD-FAIL (DET-2): at-Ausnahme nicht demonstriert — beide Läufe tragen identischen at-Wert (P-7: aktive Ausnahme-Assertion)" >&2; exit 1; }
|
||||
echo " BEFUND: at-Ausnahme aktiv (assertiert) — Lauf A at=$at_a, Lauf B at=$at_b (benannte Ausnahme, §5.14 Pkt. 3: übrige Bestandteile byte-identisch)"
|
||||
# (d) Der deterministische Content weicht als Ganzes von der Baseline ab (Witness, echte Hashes).
|
||||
ABS_A=$(git -C "$ROOT/wt-a" show "$BASE:wiki/alpha.md" | sha256sum | cut -d' ' -f1)
|
||||
SHA_A=$(git -C "$ROOT/wt-a" show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)
|
||||
|
||||
Reference in New Issue
Block a user