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:
@@ -623,3 +623,7 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
|
|||||||
- Umsetzung: Die Story-3.12-Sandbox übernimmt die Banner-Konvention und ersetzt den leeren Platzhalter durch eine dokumentierende Funktion (`scopelock_header_banner() { echo "--- Lifecycle-Run: $1 (Root-Scope wiki/, Lock refs/leases/wiki) ---"; }`, Z. 178-180) — je Lifecycle-Szenario wird der Banner als Run-Kopf ausgegeben (L-1, L-3..L-9). Die Story-3.11-Sandbox selbst bleibt — wo es keinen Platzhalter-Aufruf gibt — unverändert (Fugen-Identität).
|
- Umsetzung: Die Story-3.12-Sandbox übernimmt die Banner-Konvention und ersetzt den leeren Platzhalter durch eine dokumentierende Funktion (`scopelock_header_banner() { echo "--- Lifecycle-Run: $1 (Root-Scope wiki/, Lock refs/leases/wiki) ---"; }`, Z. 178-180) — je Lifecycle-Szenario wird der Banner als Run-Kopf ausgegeben (L-1, L-3..L-9). Die Story-3.11-Sandbox selbst bleibt — wo es keinen Platzhalter-Aufruf gibt — unverändert (Fugen-Identität).
|
||||||
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` L-1/L-3..L-9 (Banner-Ausgabe je Run) — Exit 0.
|
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-12/run-sandbox.sh` L-1/L-3..L-9 (Banner-Ausgabe je Run) — Exit 0.
|
||||||
- status: aufgegriffen (Home erledigt in sandbox-3-12, Banner-Konvention umgesetzt)
|
- status: aufgegriffen (Home erledigt in sandbox-3-12, Banner-Konvention umgesetzt)
|
||||||
|
|
||||||
|
- source_spec: `_bmad-output/implementation-artifacts/spec-3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen.md`
|
||||||
|
summary: Echte Zwei-Worktree-/Zwei-Prozess-Übernahme des Root-Scope-Locks mit Synchronisations-Barriere üben — die §5.18-Pkt.-2-Takeover-Atomarität (AC-2) ist in sandbox-3-12 nur sequenziell (ein Worktree) geprüft.
|
||||||
|
evidence: Verification-Gap-Review Story 3.12 (Loop 1): sandbox-3-12 läuft komplett in einem `git init`-Worktree; die §5.17-geteilte-Ref-Namespace-Sichtbarkeit und die Echt-Überlappung (Zwei Producer gleichzeitig, eine Entscheidung) ist nicht beobachtet; die vorhandene Rest-Vertiefung in der Story-3.11-Aufgegriffen-Notiz (Home §5.18 Pkt. 1/2/5) und der deferred-work-Eintrag zur Zwei-Worktree-Barriere (Sandbox-Härtung / Story-3.13-Abnahme) scopen dasselbe Restziel.
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ isolate() {
|
|||||||
SCOPELOCK="refs/leases/wiki" # exklusiver scope-bezogener Lock (AC-a)
|
SCOPELOCK="refs/leases/wiki" # exklusiver scope-bezogener Lock (AC-a)
|
||||||
ZERO=$(printf '%040d' 0) # $ZERO_SHA für create-only
|
ZERO=$(printf '%040d' 0) # $ZERO_SHA für create-only
|
||||||
scopelock_acquire() { # $1 = Run-ID (Lock-Inhalt; PRODUCER sichtbar) — 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
|
local runid="$1" val
|
||||||
val=$(printf '%s' "$runid" | git hash-object -w --stdin) || return 2
|
val=$(printf '%s' "$runid" | git hash-object -w --stdin) || return 2
|
||||||
{ git update-ref "$SCOPELOCK" "$val" "$ZERO"; } 2>/dev/null
|
{ git update-ref "$SCOPELOCK" "$val" "$ZERO"; } 2>/dev/null
|
||||||
@@ -121,18 +122,28 @@ scopelock_release() { # $1 = erwartete Inhaber-Run-ID (Ownership-Pruefung)
|
|||||||
scopelock_content() {
|
scopelock_content() {
|
||||||
local val
|
local val
|
||||||
val=$(git rev-parse -q --verify "$SCOPELOCK" 2>/dev/null) || { echo ""; return 0; }
|
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
|
# 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
|
# (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.
|
# 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
|
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" ] \
|
[ "$(scopelock_content)" = "$old" ] \
|
||||||
|| { echo "HARD-FAIL (Ownership): Lock-Inhalt '$old' erwartet, tatsaechlich '$(scopelock_content)'" >&2; exit 1; }
|
|| { 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
|
newval=$(printf '%s' "$new" | git hash-object -w --stdin) || return 2
|
||||||
git update-ref "$SCOPELOCK" "$newval" 2>/dev/null \
|
# Atomarer Ownership-CAS (PATCH 1, §5.18 Pkt. 2): der Ref-Write traegt den Old-Value
|
||||||
|| { echo "HARD-FAIL (Takeover): Ref-Write fehlgeschlagen" >&2; exit 1; }
|
# (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) ----------
|
# ---------- 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)
|
# $1=area $2=id $3=erzeugungs_gen $4=erwartete-halter-runid (Lock-Inhalt)
|
||||||
lease_liveness_stale() {
|
lease_liveness_stale() {
|
||||||
local area="$1" id="$2" gen="$3" holder_runid="$4"
|
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
|
if ! lease_stale "$area" "$id" "$gen"; then
|
||||||
return 1 # nicht generationen-abgelaufen -> nicht stale
|
return 1 # nicht generationen-abgelaufen -> nicht stale
|
||||||
fi
|
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)"
|
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"
|
[ "$(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).
|
# 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_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.
|
# Genau eine aktive Root-Lease: genau ein scope-bezogener Lock, Inhalt = neuer Inhaber.
|
||||||
n2=$(git for-each-ref --format='%(refname)' | grep -cF "$SCOPELOCK")
|
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)"
|
[ "$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
|
mkdir -p registry
|
||||||
echo "gen: 1" > registry/wiki
|
echo "gen: 1" > registry/wiki
|
||||||
echo "hold: run-l8 (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
|
# KERN (AC-6): log.md traegt KEINE Build-/Review-/Story-/Sandbox-Historie — weder als
|
||||||
# Koordinations-Eintraege; kein Ghost-Diff im Mutationsbereich (nur log.md, Registry ausserhalb).
|
# Kategorie-benennende Eintraege (z. B. 'Build-Historie', 'Sandbox-Protokoll', 'Story-Log')
|
||||||
grep -qiE 'sandbox|review|build|_bmad|run-sandbox' wiki/log.md && fail "L-8: log.md traegt Build-/Review-/Sandbox-Historie (AC-6 verletzt)"
|
# 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):
|
# Der einzige wiki/-Unterschied ist log.md (Registry/Lock ausserhalb, kein Ghost-Diff):
|
||||||
git add wiki/log.md registry/wiki
|
git add wiki/log.md registry/wiki
|
||||||
git commit -qm "L8: canonisches Log (2 Koordinations-Eintraege) + Registry (ausserhalb wiki/)"
|
git commit -qm "L8: canonisches Log (2 Koordinations-Eintraege) + Registry (ausserhalb wiki/)"
|
||||||
|
|||||||
+12
-2
@@ -2,7 +2,7 @@
|
|||||||
title: 'Story 3.12 — Lease-Lifecycle und Commit-Abschluss transaktional schließen'
|
title: 'Story 3.12 — Lease-Lifecycle und Commit-Abschluss transaktional schließen'
|
||||||
type: 'feature'
|
type: 'feature'
|
||||||
created: '2026-08-21'
|
created: '2026-08-21'
|
||||||
status: 'in-progress'
|
status: 'in-review'
|
||||||
baseline_commit: '80480af3ec910b6a00d10f4fe820131bbf79c6a5'
|
baseline_commit: '80480af3ec910b6a00d10f4fe820131bbf79c6a5'
|
||||||
review_loop_iteration: 0
|
review_loop_iteration: 0
|
||||||
context:
|
context:
|
||||||
@@ -127,5 +127,15 @@ Der Lifecycle ist der **geschlossene transaktionale Rahmen** der Koordinations-D
|
|||||||
|
|
||||||
### Review Findings
|
### Review Findings
|
||||||
|
|
||||||
_Bmad-code-review Loop 1 (2026-08-21), 4 Layer (blind-hunter/edge-case-hunter/verification-gap/acceptance-auditor). Triage: **noch offen** — Findings werden nach dem Loop im Spec-Change-Log festgehalten._
|
_Bmad-code-review Loop 1 (2026-08-21), 4 Layer (blind-hunter/edge-case-hunter/verification-gap/acceptance-auditor). Triage: **konvergiert** — kein intent_gap, kein bad_spec; 4 Patch-Kategorien auto-fixiert, 1 Defer, Rest begründet abgewiesen. Siehe `## Spec Change Log` unten für die vollständige Triage-Struktur._
|
||||||
|
|
||||||
|
## Spec Change Log
|
||||||
|
|
||||||
|
- **2026-08-21, Loop 1, Triage & Patches (kein Loopback — keine intent_gap/bad_spec):**
|
||||||
|
- **PATCH (Kern, Verification-Gap V1):** `scopelock_takeover` in sandbox-3-12 Z. 129–146 auf **atomaren Ownership-CAS** umgestellt (`git update-ref <ref> <neu> <oldblob>` statt read-then-write ohne Old-Value) — §5.18 Pkt. 2 („Ersetzung als Ref-Schreibvorgang … nach atomarer Ownership-Prüfung") correct realisiert; L-2 übt jetzt die **negative atomare Abweisung** (falscher Old-Value-Blob → Ref-Write schlägt atomar fehl, Lock und genau-eine-aktive-Ref unverändert, kein Clobber) plus positiven Exactly-once-CAS.
|
||||||
|
- **PATCH:** §5.18 Pkt. 2 Z. 431 Tippfehler `AK-2:` → `AC-2:`.
|
||||||
|
- **PATCH (Härtung, Edge-Case-Hunter E2/E3/E4/E5):** `scopelock_acquire` lehnt leere Run-ID ab; `lease_liveness_stale` lehnt leeres `holder_runid` ab (keine vacuous-stale-Klassifikation); `scopelock_content` unterscheidet korrupten/fehlenden Blob (harter `LOCK_READ_ERROR`) von „Ref nicht vorhanden"; L-8-Kanonikalitätsprüfung auf verboțene Kategorie-Marker + Artefakt-Pfade präzisiert (kein false-HARD-FAIL auf legitime Fließtext-Wörter) mit Positiv-Kontrolle.
|
||||||
|
- **DEFER:** echte Zwei-Worktree-/Zwei-Prozess-Übernahme mit Synchronisations-Barriere bleibt Story 3.13-Abnahme (`deferred-work.md`-Append mit `source_spec:`-Format).
|
||||||
|
- **REJECT (begründet):** L-6-„Rollback nach Commit" (Commit immutabel per AD-17f; Matrix-„oder"-Semantik `Baseline-oder-valide-committet` korrekt erfüllt, L-5 übt den echten Rollback); in-review-Flip ist Workflow-Folge; banner in sandbox-3-12 ersetzt (sandbox-3-11 unverändert korrekt); `generated`-Staleness-Konvention; `baseline_commit` korrekt = Vor-Implementierungs-HEAD; `generated.at`-Wanduhr-Gap §5.14 Pkt. 3 dokumentiert; Review-Findings-Sektion leer weil Loop 1 eben läuft; ein-Worktree-Sandbox bewusst (Defer Zwei-Worktree→3.13); epic-3-context kein Doppelsatz (geprüft); „12-vs-13-I/O-Matrix-Zeilen" Missverständnis (Story-3.11-Matrix 4-vs-8, nicht Story-3.12-12-Zeilen).
|
||||||
|
- **Nachweis:** Sandbox-3-12 re-executiert nach Patches: **L-1..L-9, 9/9 harte PASS, Exit 0**; `grep -n "AK-2" schema/compiler.md` leer; AD-3-Files ohne Diff.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -428,7 +428,7 @@ Die §5.11-Lease-Akquise (Pkt. 1 — Lockfile `lease/<area>/<id>.lock`, Merge-Ba
|
|||||||
Die Koordinations-Dimension ist über §5.11 (Leasing/Dirty-Tree, Story 3.5), §5.12 (Staleness/Recovery, Story 3.6), §5.13 (Phasen-Trennung, Story 3.7) und §5.17 (atomare Root-Scope-Lease-Akquise, Story 3.11) verankert — diese Sektion ist die **geschlossene, transaktionale Lifecycle-Klammer** darüber (D-3, Story 3.12; AC-1..AC-7; A0-20 — keine Wanduhr-/Systemzeit-Steuerung, Lifecycle deterministisch aus dem committeten Git-State, AD-17h/A0-19): sie ordnet Akquise (§5.11/§5.17), Preflight/Protect (§5.11 Pkt. 3), Mutation (§5.9/§5.10/§5.16), Rollback (§5.13) und durable Release (§5.11 Pkt. 1/§5.17 Pkt. 1) in eine **eindeutige Zustandsmaschine**, so dass SUCCESS und FAIL je einen sauberen, wiederanlaufbaren Zustand hinterlassen. **Fugen-Identität:** §5.11 (Pkt. 1–7), §5.12 (Pkt. 1–7), §5.13 und §5.17 bleiben **textuell unverändert** — die Lifecycle-Regie ist die von §5.17 Pkt. 1/6 benannte Fortsetzung („Lifecycle-Regie Story 3.12"). Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`adapters/`-/`raw/`-Change (AD-3), **keinen** neuen Frontmatter-/Format-Key (Vertrag §3.1–§3.7 unverändert), **keinen** Standalone (D-3) und **keine** Wanduhr-/Systemzeit hinzu (A0-20).
|
Die Koordinations-Dimension ist über §5.11 (Leasing/Dirty-Tree, Story 3.5), §5.12 (Staleness/Recovery, Story 3.6), §5.13 (Phasen-Trennung, Story 3.7) und §5.17 (atomare Root-Scope-Lease-Akquise, Story 3.11) verankert — diese Sektion ist die **geschlossene, transaktionale Lifecycle-Klammer** darüber (D-3, Story 3.12; AC-1..AC-7; A0-20 — keine Wanduhr-/Systemzeit-Steuerung, Lifecycle deterministisch aus dem committeten Git-State, AD-17h/A0-19): sie ordnet Akquise (§5.11/§5.17), Preflight/Protect (§5.11 Pkt. 3), Mutation (§5.9/§5.10/§5.16), Rollback (§5.13) und durable Release (§5.11 Pkt. 1/§5.17 Pkt. 1) in eine **eindeutige Zustandsmaschine**, so dass SUCCESS und FAIL je einen sauberen, wiederanlaufbaren Zustand hinterlassen. **Fugen-Identität:** §5.11 (Pkt. 1–7), §5.12 (Pkt. 1–7), §5.13 und §5.17 bleiben **textuell unverändert** — die Lifecycle-Regie ist die von §5.17 Pkt. 1/6 benannte Fortsetzung („Lifecycle-Regie Story 3.12"). Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`adapters/`-/`raw/`-Change (AD-3), **keinen** neuen Frontmatter-/Format-Key (Vertrag §3.1–§3.7 unverändert), **keinen** Standalone (D-3) und **keine** Wanduhr-/Systemzeit hinzu (A0-20).
|
||||||
|
|
||||||
1. **Liveness & Ownership — Staleness verlangt bestätigten Abbruch/abgelaufene Liveness plus atomare Ownership-Prüfung (AC-1):** Eine lebende Lease wird durch eine **höhere sichtbare Generation allein nicht stale** — Staleness folgt nie aus Kalenderzeit (A0-20: keine Time-To-Live über Wanduhr) und nie allein aus der Gen-Registry (§5.12), sondern aus **committeten Zuständen plus bestätigtem Abbruch oder abgelaufener Liveness**: die Lease gilt als stale (i) wenn der Halter seine Liveness **bestätigt abgebrochen** hat (deterministisch belegt aus dem committeten Git-State, z. B. Halter-Branch-Verlust, abgebrochener Run ohne durable Release, Registry-Status) oder (ii) wenn die **Liveness abgelaufen** ist — ein beobachtbarer Zustand, der aus den committeten Registrierungs-/Markierungs-Zuständen ableitbar ist (§5.12 Gen-/Stale-Marker), nicht aus einem Zeitstempel. **Keine Fremd-Staleness ohne Ownership-Prüfung:** die Stale-Bewertung selbst ist eine **atomare Ownership-Prüfung** — sie verifiziert gegen den geltenden Halter-Zustand (Lock-Inhalt/Inhaber-Run-ID, §5.17 Pkt. 1) und den Registry-Status, bevor irgendeine Übernahme-/Freigabe-Entscheidung greift. Eine höhere Generation führt nur dann zu einer Stale-Klassifikation, wenn zugleich der bestätigte Abbruch/die abgelaufene Liveness vorliegt (Verwaist-Klassifikation, §5.12 Pkt. 3 — Determinismus aus dem committeten Zustand, AD-17h/A0-19).
|
1. **Liveness & Ownership — Staleness verlangt bestätigten Abbruch/abgelaufene Liveness plus atomare Ownership-Prüfung (AC-1):** Eine lebende Lease wird durch eine **höhere sichtbare Generation allein nicht stale** — Staleness folgt nie aus Kalenderzeit (A0-20: keine Time-To-Live über Wanduhr) und nie allein aus der Gen-Registry (§5.12), sondern aus **committeten Zuständen plus bestätigtem Abbruch oder abgelaufener Liveness**: die Lease gilt als stale (i) wenn der Halter seine Liveness **bestätigt abgebrochen** hat (deterministisch belegt aus dem committeten Git-State, z. B. Halter-Branch-Verlust, abgebrochener Run ohne durable Release, Registry-Status) oder (ii) wenn die **Liveness abgelaufen** ist — ein beobachtbarer Zustand, der aus den committeten Registrierungs-/Markierungs-Zuständen ableitbar ist (§5.12 Gen-/Stale-Marker), nicht aus einem Zeitstempel. **Keine Fremd-Staleness ohne Ownership-Prüfung:** die Stale-Bewertung selbst ist eine **atomare Ownership-Prüfung** — sie verifiziert gegen den geltenden Halter-Zustand (Lock-Inhalt/Inhaber-Run-ID, §5.17 Pkt. 1) und den Registry-Status, bevor irgendeine Übernahme-/Freigabe-Entscheidung greift. Eine höhere Generation führt nur dann zu einer Stale-Klassifikation, wenn zugleich der bestätigte Abbruch/die abgelaufene Liveness vorliegt (Verwaist-Klassifikation, §5.12 Pkt. 3 — Determinismus aus dem committeten Zustand, AD-17h/A0-19).
|
||||||
2. **Stale-Übernahme genau einmal — benannte ersetzte Holder-ID, genau eine aktive Root-Lease (AC-2):** Ist eine Lease nachweislich stale (Pkt. 1), übernimmt ein neuer Producer den Root-Scope **genau einmal** — nie zwei Übernehmer, nie zwei aktive Root-Leases (AK-2: die Übernahme ist ownership-gebunden: nur wer den Root-Scope-Lock (§5.17 Pkt. 1) nach der atomaren Ownership-Prüfung erwirbt, wird neuer Inhaber; die Ersetzung erfolgt als **Ref-Schreibvorgang auf den scope-bezogenen Lock** mit neuem Lock-Inhalt — die **ersetzte Holder-ID wird im datumsgruppierten `log.md`-Eintrag benannt** (Vertrag §5; Form: Quell-/Scope-Pfad + `<Baseline-Commit>` + alte Holder-ID + neue Holder-ID), so dass die Übernahme vollständig nachvollziehbar ist und **genau eine** aktive Root-Lease bestehen bleibt (Kardinalität 1, §5.17 Pkt. 2). Die verwaiste Lease wird **nie still gelöscht** (AD-17e): die Registry-Zeile und der per-Lease-Lockfile-/Branch-Ablage-Zustand (§5.11 Pkt. 1) bleiben als Verwaist-/Stale-Nachweis erhalten (§5.12 Pkt. 3/6).
|
2. **Stale-Übernahme genau einmal — benannte ersetzte Holder-ID, genau eine aktive Root-Lease (AC-2):** Ist eine Lease nachweislich stale (Pkt. 1), übernimmt ein neuer Producer den Root-Scope **genau einmal** — nie zwei Übernehmer, nie zwei aktive Root-Leases (AC-2: die Übernahme ist ownership-gebunden: nur wer den Root-Scope-Lock (§5.17 Pkt. 1) nach der atomaren Ownership-Prüfung erwirbt, wird neuer Inhaber; die Ersetzung erfolgt als **Ref-Schreibvorgang auf den scope-bezogenen Lock** mit neuem Lock-Inhalt — die **ersetzte Holder-ID wird im datumsgruppierten `log.md`-Eintrag benannt** (Vertrag §5; Form: Quell-/Scope-Pfad + `<Baseline-Commit>` + alte Holder-ID + neue Holder-ID), so dass die Übernahme vollständig nachvollziehbar ist und **genau eine** aktive Root-Lease bestehen bleibt (Kardinalität 1, §5.17 Pkt. 2). Die verwaiste Lease wird **nie still gelöscht** (AD-17e): die Registry-Zeile und der per-Lease-Lockfile-/Branch-Ablage-Zustand (§5.11 Pkt. 1) bleiben als Verwaist-/Stale-Nachweis erhalten (§5.12 Pkt. 3/6).
|
||||||
3. **Eindeutige Abort-/Protect-Zustandsmaschine für fremde getrackte/ungetrackte Änderungen (AC-3):** Vor jeder Mutation läuft der Preflight (§5.11 Pkt. 3 — Pre-Mutation-Prüfung `git status --porcelain -- <Mutationsbereich>`; `UNCOMMITTED_INPUT`-Abbruch „published/committed Input erforderlich" bleibt §5.11 Pkt. 3-/§5.9-P2-Element-(1)-gebunden, kein zweiter Abbruch-Pfad) und versetzt den Run in eine **eindeutige Abort-/Protect-Zustandsmaschine** für **fremde** (nicht dem Run gehörende, producer-fremde) uncommittete Änderungen im Mutationsbereich:
|
3. **Eindeutige Abort-/Protect-Zustandsmaschine für fremde getrackte/ungetrackte Änderungen (AC-3):** Vor jeder Mutation läuft der Preflight (§5.11 Pkt. 3 — Pre-Mutation-Prüfung `git status --porcelain -- <Mutationsbereich>`; `UNCOMMITTED_INPUT`-Abbruch „published/committed Input erforderlich" bleibt §5.11 Pkt. 3-/§5.9-P2-Element-(1)-gebunden, kein zweiter Abbruch-Pfad) und versetzt den Run in eine **eindeutige Abort-/Protect-Zustandsmaschine** für **fremde** (nicht dem Run gehörende, producer-fremde) uncommittete Änderungen im Mutationsbereich:
|
||||||
- **getrackte fremde Änderung** (modifizierte committete Datei): **Schützen** — Sicherung in die **Scratch-Zone `scratch/<run-id>/`** (außerhalb `wiki/`, deterministisch benannt) oder via **`git stash push -- <Pfad>`** (§5.12 Pkt. 5 zulässige native Variante); der Arbeitssatz wird für den Run bereinigt; **Restore nach Run-Ende byte-identisch** aus der Sicherung (SHA-256-/Byte-Identitäts-Prüfung in beide Richtungen) — nie gelöscht (AD-17e). Abort-Pfad: greift die Schutz-Sicherung nicht (z. B. Sicherung nicht byte-identisch restaurierbar), **HALT des Runs** mit textuell benannter Ursache (NFR-4) und Zustands-Restaurations-Invariante (§5.13 Pkt. 3).
|
- **getrackte fremde Änderung** (modifizierte committete Datei): **Schützen** — Sicherung in die **Scratch-Zone `scratch/<run-id>/`** (außerhalb `wiki/`, deterministisch benannt) oder via **`git stash push -- <Pfad>`** (§5.12 Pkt. 5 zulässige native Variante); der Arbeitssatz wird für den Run bereinigt; **Restore nach Run-Ende byte-identisch** aus der Sicherung (SHA-256-/Byte-Identitäts-Prüfung in beide Richtungen) — nie gelöscht (AD-17e). Abort-Pfad: greift die Schutz-Sicherung nicht (z. B. Sicherung nicht byte-identisch restaurierbar), **HALT des Runs** mit textuell benannter Ursache (NFR-4) und Zustands-Restaurations-Invariante (§5.13 Pkt. 3).
|
||||||
- **ungetrackte fremde Datei** im Mutationsbereich: **Protect** — die Datei wird in die Scratch-Zone gesichert (bzw. via `git stash push -u` eingeschlossen) und nach dem Run **byte-identisch restauriert**; **keine stille Löschung** (AD-17e); der Schutz wird im `log.md`-Eintrag dokumentiert (Vertrag §5, §5.11 Pkt. 3 „Dirty-Tree-Schutz-Dokumentation").
|
- **ungetrackte fremde Datei** im Mutationsbereich: **Protect** — die Datei wird in die Scratch-Zone gesichert (bzw. via `git stash push -u` eingeschlossen) und nach dem Run **byte-identisch restauriert**; **keine stille Löschung** (AD-17e); der Schutz wird im `log.md`-Eintrag dokumentiert (Vertrag §5, §5.11 Pkt. 3 „Dirty-Tree-Schutz-Dokumentation").
|
||||||
|
|||||||
Reference in New Issue
Block a user