fix: Story 3.12 Review-Loop-1-Patches (atomarer Ownership-CAS scopelock_takeover, AK-2->AC-2, L-2-Negativ-CAS, Sandbox-Härtung; L-1..L-9 9/9 harte PASS/Exit 0)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,7 @@ isolate() {
|
||||
SCOPELOCK="refs/leases/wiki" # exklusiver scope-bezogener Lock (AC-a)
|
||||
ZERO=$(printf '%040d' 0) # $ZERO_SHA für create-only
|
||||
scopelock_acquire() { # $1 = Run-ID (Lock-Inhalt; PRODUCER sichtbar) — create-only
|
||||
[ -n "${1:-}" ] || return 2 # PATCH 4a: keine Akquise mit leerer Run-ID / leerem Lock-Inhalt
|
||||
local runid="$1" val
|
||||
val=$(printf '%s' "$runid" | git hash-object -w --stdin) || return 2
|
||||
{ git update-ref "$SCOPELOCK" "$val" "$ZERO"; } 2>/dev/null
|
||||
@@ -121,18 +122,28 @@ scopelock_release() { # $1 = erwartete Inhaber-Run-ID (Ownership-Pruefung)
|
||||
scopelock_content() {
|
||||
local val
|
||||
val=$(git rev-parse -q --verify "$SCOPELOCK" 2>/dev/null) || { echo ""; return 0; }
|
||||
git cat-file -p "$val" 2>/dev/null || echo ""
|
||||
# PATCH 4c: Ref existiert, aber cat-file scheitert = korrupter/fehlender Blob -> harter
|
||||
# LOCK_READ_ERROR statt stiller leerer Rueckgabe (leer ist nur "Ref nicht vorhanden").
|
||||
git cat-file -p "$val" 2>/dev/null || { echo "HARD-FAIL (LOCK_READ_ERROR): Lock-Ref '$SCOPELOCK' zeigt auf '$val', aber Blob nicht lesbar (korrupt/fehlend)" >&2; exit 1; }
|
||||
}
|
||||
# Ownership-CAS: Uebernahme nur, wenn der Lock (noch) vom erwarteten Inhaber gehalten wird
|
||||
# (atomare Ownership-Pruefung, §5.18 Pkt. 1/2). Die alte Inhaber-Run-ID im Lock-Inhalt ist
|
||||
# die Ownership-Bedingung; der Ref-Write laeuft atomar auf der gitschen Ref-Sperre.
|
||||
scopelock_takeover() { # $1 = erwartete alte Run-ID (Lock-Inhalt) $2 = neue Run-ID
|
||||
local old="$1" new="$2" newval
|
||||
local old="$1" new="$2" newval oldblob
|
||||
# Schnelle Fehldiagnose-Hilfe (bleibt, PATCH 1): Ownership-Mismatch klar beschrifteter
|
||||
# HARD-FAIL. Der entscheidende Guard ist danach der ATOMARE Old-Value-Write (CAS) — die
|
||||
# atomare Abweisung selbst wird im negativen L-2-Test durch direkten atomaren Ref-Write
|
||||
# geprueft (PATCH 2), der schnelle Diagnose-Zweig bleibt fuer echte Aufrufer fatal.
|
||||
[ "$(scopelock_content)" = "$old" ] \
|
||||
|| { echo "HARD-FAIL (Ownership): Lock-Inhalt '$old' erwartet, tatsaechlich '$(scopelock_content)'" >&2; exit 1; }
|
||||
newval=$(printf '%s' "$new" | git hash-object -w --stdin) || return 2
|
||||
git update-ref "$SCOPELOCK" "$newval" 2>/dev/null \
|
||||
|| { echo "HARD-FAIL (Takeover): Ref-Write fehlgeschlagen" >&2; exit 1; }
|
||||
# Atomarer Ownership-CAS (PATCH 1, §5.18 Pkt. 2): der Ref-Write traegt den Old-Value
|
||||
# (Blob der erwarteten alten Inhaber-Run-ID). Schlaegt atomar fehl (Exit != 0) und laesst den
|
||||
# Lock UNVERAENDERT, wenn der Lock nicht mehr exakt diesen Inhalt traegt.
|
||||
oldblob=$(printf '%s' "$old" | git hash-object -w --stdin) || return 2
|
||||
git update-ref "$SCOPELOCK" "$newval" "$oldblob" 2>/dev/null \
|
||||
|| { echo "HARD-FAIL (Takeover): atomarer Ownership-CAS fehlgeschlagen (Lock traegt nicht mehr exakt den erwarteten Inhalt — kein Clobber)" >&2; exit 1; }
|
||||
}
|
||||
|
||||
# ---------- Registry / Gen / Liveness (§5.12, uebernommen aus sandbox-3-6) ----------
|
||||
@@ -199,6 +210,7 @@ lease_stale() { # $1=area $2=id $3=erzeugungs_gen: true (0) wenn Erzeugungs-Ge
|
||||
# $1=area $2=id $3=erzeugungs_gen $4=erwartete-halter-runid (Lock-Inhalt)
|
||||
lease_liveness_stale() {
|
||||
local area="$1" id="$2" gen="$3" holder_runid="$4"
|
||||
[ -n "${4:-}" ] || return 1 # PATCH 4b: leere holder_runid -> keine vacuous-stale-Klassifikation
|
||||
if ! lease_stale "$area" "$id" "$gen"; then
|
||||
return 1 # nicht generationen-abgelaufen -> nicht stale
|
||||
fi
|
||||
@@ -328,7 +340,27 @@ lease_stale wiki run-l2 1 || fail "L-2: Run A nicht als stale klassifiziert (Erz
|
||||
lease_liveness_stale wiki run-l2 1 "RUN-L2-alt" || fail "L-2: bestaetigt abgebrochene Lease nicht als stale klassifiziert (AC-1/AC-2)"
|
||||
[ "$(scopelock_content)" = "RUN-L2-alt" ] || fail "L-2: Lock-Inhalt vor Uebernahme veraendert"
|
||||
# Uebernahme GENAU EINMAL: Ownership-CAS ersetzt den Lock-Inhalt (RUN-L2-alt -> RUN-L2-neu).
|
||||
# PATCH 2 — atomarer Ownership-CAS: POSITIVE genau-einmal-Kontrolle und NEGATIVE Kontrolle.
|
||||
# Der korrekte Old-Value flippt (CAS) genau einmal und hinterlaesst den neuen Inhaber:
|
||||
scopelock_takeover "RUN-L2-alt" "RUN-L2-neu"
|
||||
[ "$(scopelock_content)" = "RUN-L2-neu" ] || fail "L-2: CAS mit korrektem Old-Value flippte den Lock-Inhalt nicht (PATCH 2, AC-2)"
|
||||
# NEGATIV-Kontrolle (PATCH 2): Takeover-Aufruf mit FALSCHEM Old-Value (die alte Holder-ID ist
|
||||
# nach der Uebernahme nicht mehr Inhaber) muss atomar FEHLSCHLAGEN und den Lock UNVERAENDERT
|
||||
# lassen — kein Clobber. Um die ATOMARE Abweisung zu beweisen (nicht nur die schnelle Diagnose),
|
||||
# wird der atomare Old-Value-Ref-Write direkt ausgefuehrt: der Lock traegt Blob('RUN-L2-neu'),
|
||||
# der falsche Old-Value Blob('RUN-L2-alt') ist NICHT Old-Value -> git update-ref schlaegt atomar
|
||||
# fehl (Exit != 0, kein Clobber, Lock unveraendert) — identisch zu dem, was ein um die
|
||||
# Diagnose herumlaufender Aufrufer treffen wuerde.
|
||||
alt_blob=$(printf '%s' 'RUN-L2-alt' | git hash-object -w --stdin) || fail "L-2: alt_blob nicht erzeugt (PATCH 2)"
|
||||
falsch_blob=$(printf '%s' 'RUN-L2-falsch' | git hash-object -w --stdin) || fail "L-2: falsch_blob nicht erzeugt (PATCH 2)"
|
||||
if git update-ref "$SCOPELOCK" "$falsch_blob" "$alt_blob" 2>/dev/null; then
|
||||
fail "L-2: atomarer Old-Value-Write mit falschem Old-Value gelang (PATCH 2, AC-2: kein Clobber)"
|
||||
fi
|
||||
# Lock-Ref existiert unveraendert (eine aktive Root-Lease, kein Clobber):
|
||||
n2b=$(git for-each-ref --format='%(refname)' | grep -cF "$SCOPELOCK")
|
||||
[ "$n2b" -eq 1 ] || fail "L-2: Lock-Ref nach negativem atomaren CAS veraendert (PATCH 2)"
|
||||
# Lock-Inhalt unveraendert (Inhalt danach == aktuelle Inhaber-Run-ID, kein Clobber):
|
||||
[ "$(scopelock_content)" = "RUN-L2-neu" ] || fail "L-2: negativer atomarer CAS hat den Lock-Inhalt veraendert (Clobber — PATCH 2, AC-2)"
|
||||
# Genau eine aktive Root-Lease: genau ein scope-bezogener Lock, Inhalt = neuer Inhaber.
|
||||
n2=$(git for-each-ref --format='%(refname)' | grep -cF "$SCOPELOCK")
|
||||
[ "$n2" -eq 1 ] || fail "L-2: nach Uebernahme erwartet genau einen Scope-Lock, tatsaechlich $n2 (AC-2: genau eine aktive Root-Lease)"
|
||||
@@ -607,9 +639,19 @@ printf '\n### 2026-08-21 — Freigabe: wiki Root-Scope durch RUN-L8 (Ref-Delete,
|
||||
mkdir -p registry
|
||||
echo "gen: 1" > registry/wiki
|
||||
echo "hold: run-l8 (Gen 1)" >> registry/wiki
|
||||
# KERN (AC-6): log.md enthaelt KEINE Build-/Review-/Sandbox-Texte — nur die zwei
|
||||
# Koordinations-Eintraege; kein Ghost-Diff im Mutationsbereich (nur log.md, Registry ausserhalb).
|
||||
grep -qiE 'sandbox|review|build|_bmad|run-sandbox' wiki/log.md && fail "L-8: log.md traegt Build-/Review-/Sandbox-Historie (AC-6 verletzt)"
|
||||
# KERN (AC-6): log.md traegt KEINE Build-/Review-/Story-/Sandbox-Historie — weder als
|
||||
# Kategorie-benennende Eintraege (z. B. 'Build-Historie', 'Sandbox-Protokoll', 'Story-Log')
|
||||
# noch als Sandbox-Artefakt-/Pfad-Referenz (_bmad-output, run-sandbox, sandbox-3-12);
|
||||
# legitime Fach-/Koordinationswoerter im Fliesstext (z. B. einzelnes 'Build'/'Review' ohne
|
||||
# Kategorie-Hyphenat; Story-Nummern wie 'Story-3.12-L6' ohne '-Historie') werden NICHT
|
||||
# getroffen (PATCH 4d — Edge-Case-Hunter).
|
||||
grep -qiE '(Build|Review|Story|Sandbox)-(Histor|Log|Protokoll|Bericht|Nachweis)|_bmad-output|run-sandbox|sandbox-3-12' wiki/log.md \
|
||||
&& fail "L-8: log.md traegt Build-/Review-/Story-/Sandbox-Historie (AC-6 verletzt; PATCH 4d)"
|
||||
# Positiv-Kontrolle (PATCH 4d): die beiden vertragskonformen Koordinations-Eintraege sind
|
||||
# vorhanden und loesen den Kategorie-Check NICHT aus — kein false-HARD-FAIL auf legitime
|
||||
# Koordinations-Eintraege im Fliesstext.
|
||||
grep -qF 'Lease-Akquise: wiki Root-Scope durch RUN-L8' wiki/log.md || fail "L-8: Koordinations-Eintrag Akquise fehlt (PATCH 4d Positiv-Kontrolle)"
|
||||
grep -qF 'Freigabe: wiki Root-Scope durch RUN-L8' wiki/log.md || fail "L-8: Koordinations-Eintrag Freigabe fehlt (PATCH 4d Positiv-Kontrolle)"
|
||||
# Der einzige wiki/-Unterschied ist log.md (Registry/Lock ausserhalb, kein Ghost-Diff):
|
||||
git add wiki/log.md registry/wiki
|
||||
git commit -qm "L8: canonisches Log (2 Koordinations-Eintraege) + Registry (ausserhalb wiki/)"
|
||||
|
||||
Reference in New Issue
Block a user