Files
wow20/_bmad-output/implementation-artifacts/spec-3-6-lease-staleness-recovery-basis-absichern.md
T

152 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 'Story 3.6 — Lease-Staleness & Recovery-Basis absichern'
type: 'feature'
created: '2026-08-19'
status: 'done'
baseline_commit: '895b006f7f2bc951cecc09d7d27d90e28fe2a102'
review_loop_iteration: 0
context:
- '_bmad-output/implementation-artifacts/epic-3-context.md'
---
<!-- Target: 9001300 tokens. Above 1600 = high risk of context rot.
Never over-specify "how" — use boundaries + examples instead.
Cohesive cross-layer stories (DB+BE+UI) stay in ONE file.
IMPORTANT: Remove all HTML comments when filling this template. -->
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
## Intent
**Problem:** Ein abgebrochener Run hinterlässt eine Lease, die §5.11 (Lease-Hold) akquiriert, doch deren Freigabe (Release) nur nach committetem Run erfolgt. Ohne Staleness-Mechanik blockiert eine verwaiste Lease alle nachfolgenden Runs dauerhaft — Wissen bleibt blockiert (AD-17d/A0-15; §5.11 Pkt. 1 „verbleibt bis zum Staleness-/Recovery-Mechanismus der Story 3.6", §5.11 Pkt. 7-Seam: 3.6 = Zeit-/Umgebungs-Zustands-Frage, §7 verbleibendes 3.x-Thema).
**Approach:** Neue Sektion **§5.12 „Lease-Staleness & Recovery-Basis (Story 3.6)"** (nach §5.11, vor §6): **TTL + Lease-Registrierung im Clone-Root-State** deterministisch einführen — das TTL-Ablauf-Kriterium ist **generationen-basiert** (ohne Wanduhr, AD-17h-konform; die A0-20 zugelassene `at`-Notation bleibt dokumentierend) — plus **Verwaist-Handling** (Übernehmen/Stale-Markieren mit `log.md`-Protokollierung) und die **`raw/`-Recovery-Basis** (Zugriffs-/Consistency-Basis, AD-3 unverändert). Die vier Story-3.5-Defers mit Home 3.6 werden aufgegriffen (holder_id-Ableitung, baseline_commit-Diskrepanz-Regel, kumulative Registrierung über Runs, native `git stash`-Variante + Verwaist-Übungs-Thema). Verwaiste/hängende Leases werden dabei **nie gelöscht** (AD-17e) und `raw/` **nie verändert** (AD-3).
## Boundaries & Constraints
**Always:**
- Nur `schema/compiler.md` (neue §5.12 + §7-Auflösung + §8 Revisionslog-Revision 3.1) mutiert (D-3); `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` read-only (AD-3). Kein Standalone (D-3), keine neue §7-Invaliditätsklasse, kein Vertrags-Change.
- Deterministisch aus dem committeten Git-State ableitbar (AD-17h/A0-19): Registrierungs-Aufbau, Ablauf-Kriterium, Verwaist-Klassifikation, log.md-Texte; **kein Wanduhr-Timestamp im Lockfile- oder Registrierungs-Format** (A0-20-Konvention, §5.11 D1).
- `raw/` ist die Zugriffs- und Consistency-Recovery-Basis (AD-17d/A0-15) und bleibt bei jedem Vorgang unverändert (AD-3).
- Verwaiste/hängende Leases werden **nie still gelöscht** und fremde uncommittete Änderungen **nie** als Seitenwirkung entfernt (AD-17e).
**Ask First:**
- Wanduhr-basierte TTL (Timeout nach Kalenderzeit) statt Generationen-Ablauf — wäre ein deterministischer Bruch, weil wanduhr-`at` von der Laufzeit abhängt; die A0-20-Konvention ließe einen dokumentierenden Zeitstempel zu, ein **streng durchsetzendes** Zeit-TTL wäre Ask-First.
- Konflikt zweier gültiger Leases, die nicht per Generationen-Reihenfolge auflösbar ist (Older-wins außerhalb der definierten Klasse).
**Never:**
- `raw/`-Inhalte verändern (AD-3); uncommittete fremde `wiki/`-Änderungen löschen (AD-17e); textuelle Auto-Merge bei Branch-Konvergenz (AD-17c); Leasing über `lease/<area>/<id>`-Branches hinaus; Zeitstempel/`now`-Wanduhr als Leasing-Steuer-Größe; neuer Frontmatter-Key für Lease-Metadaten (Vertrag §3.1–§3.7, §7); Prädikat-/Format-Erweiterung des Validators; EOF-`# Log`-eigener Stand in der Registry (`wiki/log.md`-Akkumulator ist alleiniger Aufzeichnungs-Ort, Vertrag §5).
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| STALE_ABLAUF | uncommittete Lease nach abgebrochenem Run; nachfolgender Run trifft auf sie | Lease gilt als stale, blockiert nicht; Übernahme oder Stale-Markierung mit log.md-Eintrag | verwaiste Lease nie gelöscht (AD-17e); Abbruch „published/committed Input erforderlich" (AD-17a, §5.11 Pkt. 3) unverändert |
| VERWAIST_UEBERNEHMEN | neuer Run findet verwaiste Lease | übernimmt die Lease gegen die erneute Merge-Base-Prüfung, protokolliert die Übernahme | bei bestehendem Konflikt → AD-16-Pfad / menschliche Eskalation (AD-17g, §5.11 Pkt. 4) |
| VERWAIST_STALE_MARKIEREN | verwaiste Lease, Übernahme nicht sinnvoll | als stale markiert (Registry-Marker) und protokolliert; Blockade aufgehoben | Registrierungs-Invariante (Gen > erzeugend oder gleiche Gen, hält den sichtbar höchsten Reg-Generator) |
| RECOVERY_RAW_BASIS | uncommittete Änderungen aus abgebrochenem Run wiederherstellen | `raw/` (immutable, AD-3) als Zugriffs-/Consistency-Basis; Konformität re-bestätigt | `raw/` unverändert; kein `git diff`-/SHA-256-Beweis gegen `raw/`-Inhaltsebene auf dem realen Ist-Baum (Grenzen beachten: EC-1 auf sandbox-Evidenzwege beschränkt) |
| HOLD_GEGEN_HEAD | Lease existiert; zu mutierender Bereich liegt nicht unter der akquirierten Lease | Lease-Hold gegen HEAD; keine Mutation; kein Abbruch-Text | §5.11 Pkt. 1 (Lease-Hold) unverändert |
| DIRTY_TREE_UNVERWAIST | uncommittete fremde Änderung (Verdacht) ohne verwaiste Lease | §5.11 Pkt. 3 (Schutz/Scratch-Zone, nie gelöscht) greift; keine Staleness-Marke | textuell benannt (NFR-4); Restore-Weg dokumentiert |
## Code Map
- `schema/compiler.md`**primär mutiert** (D-3): neue Sektion **§5.12** „Lease-Staleness & Recovery-Basis (Story 3.6)" (nach §5.11, vor §6; §5.11-Seam-Kriterium S-1 und Pkt.-7-Text bleiben unverändert); §7 (`:376`-Bullet) Staleness/Recovery-Vorbehalt **auflösen** („in §5.12 verankert (Story 3.6)"); §8 Revisionslog **Revision 3.1** mit Abschlussklausel; §8 Normreferenzen AD-17d/A0-15 von reiner Story-Zuordnung auf **§5.12-Anker** angehoben (`AD-17d (Lease-Staleness, §5.12)`); konsistente `§5.12`-Verweise in §5.11-Pkt.-1/Pkt.-7/§7 (nur Wortlaut, kein neuer Inhalt). Anker `§5.12`-Sektion: Z. nach §5.11-Ende (nach Z. 320); §7-Bullet Z. 376; §8-AD-17d Z. 387, A0-15 Z. 389; Revisionslog nach Z. 416.
- `wiki/log.md`**append** (append-only, Vertrag §5): Story-3.6-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
- `_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh`**neu** (re-executierbar, Muster sandbox-3-5, Exit 0): Szenarien STALE-1..STALE-6 (s. Design Notes), harte Pass/Fail-Assertionen, Erhaltungs-Invariante erzwungen, keine Berührung des realen Ist-Baums.
- `_bmad-output/implementation-artifacts/sprint-status.yaml`**mutiert**: `3-6-…` `backlog``in-progress`; `last_updated` (Format `MM-DD-YYYY HH:MM`, HEAD-Präzision).
- `_bmad-output/implementation-artifacts/deferred-work.md`**append**: erneut aufgreifen der vier Story-3.5-Defers Home 3.6 (holder_id, baseline_commit, Registry-Akkumulator, git-stash) + ggf. neue Story-3.6-Defers.
- `_bmad-output/implementation-artifacts/epic-3-context.md`, `spec-3-5-…md`**read-only** (continue-context; die epics-/spec-3-5-acme-Kette unverändert).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` -- §5.12 einfügen (§5.11 Pkt. 1/7 + §7 + §8 + Revisionslog-3.1 + Abschlussklausel); Wortlaut nur, kein neuer Prädikat-/Format-Key
- [x] `schema/compiler.md` -- Revision 3.1 in §8 belegen (Anker-Zahlen + Abschlussklausel) und §5.11-`/§7`-Verweise §5.12-wortgleich
- [x] `_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh` -- Szenarien STALE-1..6 + Erhaltungs-Invariante (vertraut auf §5.9-Pkt.-5-Probe-Muster), Exit 0
- [x] `_bmad-output/implementation-artifacts/deferred-work.md` -- vier Story-3.5-Defers Home 3.6 als aufgegriffen markieren + ggf. neue
- [x] `wiki/log.md` -- (Implementierung) Story-3.6-Eintrag, `sprint-status.yaml` → in-progress; Review-Abschluss `done` im Review-Schritt (Workflow-Konvention)
**Acceptance Criteria:**
- Given ein abgebrochener Run, when uncommittete Leases hinterlassen wurden, then gelten sie als stale (TTL plus Lease-Registrierung im Clone-Root-State), blockieren keine nachfolgenden Runs, und `raw/` bleibt Zugriffs-/Consistency-Basis (AD-17d, A0-15; AC-1/AC-2/AC-4).
- Given ein neuer Run, when er eine verwaiste Lease vorfindet, then kann er sie übernehmen oder als stale markieren und protokollieren (AC-3; Alt-Branch nie gelöscht, AD-17e).
- Given ein Story-3.5-Defer mit Home 3.6, when umgesetzt, then ist der Story-3.5-Defer-Bezug nachgezeichnet (holder_id/baseline_commit/Registry-Akkumulator/git-stash; Defer-`### Aufgegriffen`-Block-append).
## Spec Change Log
_Noch leer — wird von step-04 während der Review-Loops befüllt (append-only Konvention)._
- **Review-Loop 1 (2026-08-19, Step-04-Review, 3 Layer, kein Loopback; review_loop_iteration bleibt 0):** Stapel Autofix-Patches — Terminologie-Inversion „jünger→älter" (§5.12 Pkt. 1/3/6 + §8-Revisionslog-3.1-Text + log.md + Spec-Design-Notes; Gen kleiner = älter, ältere/niedrigere Erzeugungs-Generation = stale), Generations-Quellen-Präzedenz deterministisch gepinnt (§5.12 Pkt. 1: committeter `lease-granite-root`-Marker gewinnt, sonst `lease/`-Baumableitung, sonst Startwert `gen = 0`), STALE-1-kern generationen-basiertes TTL gegen dieselbe `id` (HOLD→stale→Akquise), STALE-3-Re-Akquise nach Stale-Markierung, STALE-5-Same-id-A/B (aktiv verweigert / stale-Markierung erlaubt), holder_id-genuine Ableitung (STALE-2), baseline_commit-Merge-Base-Diskrepanz real (Descendant-Commit, `git merge-base` gewinnt), STALE-4-**lesender**-`raw/`-Bind (Zugriffs-/Consistency-Basis, aufgelöste `raw/`-Ressourcen-Referenz), Marker-Idempotenz-Regex, STALE-6-Full-State-Invariante (Registry/Lockfiles/Lease-Branches/log.md SHA-256 byte-identisch), log_bullet-Datumsgruppen-Konvention; **1 neuer Defer** (Branch-Ref-Schicksal nach Übernahme/Stale-Markierung → deferred-work.md). Sandbox re-executiert: Exit 0, 12 harte PASS; AD-3-Readonly + `wiki/`-Scope nur `wiki/log.md` verifiziert; alle Schritte der `## Verification`-Sektion re-ausgeführt.
</frozen-after-approval>
## Design Notes
**Warum §5.12 als eigene Sektion, nicht §5.11-Erweiterung?** §5.11 (3.5) ist strikt committed-state-deterministisch (AD-17h) und hält das Seam-Kriterium S-1 explizit fest — 3.6 verlässt den committeten Zustand (Zeit-/Umgebungs-Zustands-Frage). Das nicht als Punkt an §5.11 anzuhängen, sondern als Eigen-Sektion zu verankern, spiegelt die gekoppelte Abschlussklausel (kein Leasing-Scope, Staleness → Story 3.6) und hält §5.11 als Denk-Basis des Reviews erreichbar.
**Zeit ohne Wanduhr zwingend? (Beste alternative Bestätigung der Voranalyse):** AD-17d nennt als erste Option „TTL + registration of the lease in the clone-root state". Wall-clock-TTL ist deterministisch unhaltbar (AD-17h/A0-19: identische Artifakte bei gleichem Git-State). A0-20 lebt die `at`-Notation als dokumentierendes Konzept (Master-Satz `generated.at`), nicht als steuerndes Leasing-Element; der `at`-Handoff wäre Ask-First. Deshalb: **generationen-basiert** — die Registrierung trägt einen monotonen Run-Generator (ein Highlight-`generation`-Zähler je Clone-Root, aus `lease/`-Baumableitung oder einem Mono-Commit-`lease-granite-root`-Marker), ein Run **älterer (geringerer) Erzeugungs-Generation** gilt als stale (TTL-Ablauf-Äquivalent, Gen kleiner = älter, §5.12 Pkt. 1), ohne Wanduhr.
**Der Story-3.5-Seam (S-1) bleibt unbewegt:** „3.5 = committed-state-deterministisch, 3.6 = Zeit-/Umgebungs-Zustands-Frage (TTL-Ablauf, verwaiste/hängende Leases, Registrierung, `raw/`-Recovery)". §5.12 greift ihn auf, ohne ihn zu ändern — die Review-Layer prüfen die Fuge.
**Defer-Handoff (exakter Pfad):** vier Einträge `deferred-work.md` (Z. 420437, Home Story 3.6): holder_id-Quelle (→ §5.12 Pkt. 2, deterministischer Default), baseline_commit-Merge-Base-Diskrepanz-Regel (→ §5.12 Pkt. 4, Vereinheitlichung), Sandbox-log-Akkumulator (→ §5.12 Pkt. 6, cumulative über Runs; kein eigener `# Log`-Stand), native `git stash`-Variante (→ §5.12 Pkt. 5-Recovery, als Sandbox-Doppel abgebildet). Neu-Defers möglich.
**Sandbox (STALE-1..STALE-6, `bash run-sandbox.sh`, Exit 0):**
- STALE-1: abgebrochener Run erzeugt Lease + Registrierung ohne Release; nachfolgender Run findet sie → stale, kein Blockade-fail
- STALE-2: verwaiste Lease übernehmen (± Merge-Base-Prüfung), Übernahme protokolliert
- STALE-3: verwaiste Lease als stale markieren (Registry-Marker), Blockade aufgehoben, `log.md`-Eintrag
- STALE-4: `raw/`-Recovery-Basis — Ablauf, der `raw/`-Inhalte unveränderlich lässt (Assertion), Konsistenz belegt
- STALE-5: Registrierungs-Invariante (Gen vollständig, höchster-Reg-Invariant) — Verwaiste Kanten nie gelöscht, HOLD-Gegen-HEAD deterministisch
- STALE-6: Determinismus Zwei-Run (AD-17h/A0-19) — gleicher Baum-Input → identische Registrierungs-/Markierungs-Outputs
(Code-/Zahlgenauigkeiten: Die obigen Szenario-Labels sind Fixierung der I/O-Matrix; die Implementierung trägt die harten Assertionen in der Sandbox.)
## Verification
**Commands (re-executierbar, ab Workspace-Root):**
1. `bash _bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh` — expected: STALE-1..STALE-6 harte PASS/Fail, Erhaltungs-Invariante erzwungen, Exit 0.
2. `grep -n "§5.12\|Revision 3.1" schema/compiler.md` — liefert §5.12-Sektion + Revisionslog-Eintrag; `grep -n "Lease-Staleness & Recovery-Basis" schema/compiler.md` — die §5.12-Überschrift wortgleich (inkl. §5.11-Pkt.-7-Seam-Satz „Lease-Staleness/Recovery … TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery").
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`.
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (unverändert, keine Inhalts-Mutation).
5. Auf den **`wiki/`-Scope begrenzt** (`git status --porcelain -- wiki/`): ausschließlich `wiki/log.md` (dieser Eintrag) — Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt; `sprint-status.yaml`/`deferred-work.md`/`sandbox-3-6/` liegen außerhalb `wiki/` und sind nicht Teil der Diff-Probe (wie §5.9 Pkt. 5-Doku, Story-3.5-Präzedenz).
**Zu beachten (beim step-04-Review):** (a) §5.11 Pkt. 1 „verbleibt bis Story 3.6" und Pkt. 7-Seam S-1 (sowie §7-Bullet) müssen textuell **unverändert** bleiben, wenn §5.12 auf sie verweist (Haltbarkeits-Fuge); (b) `raw/`-Berührung ist auf Sandbox-Evidenzwege beschränkt (kein realer Ist-`raw/`-Beweis nötig); (c) die vorhandene §8-Revisionslog-Umsetzungsnummer ist **Revision 3.0** (Story 3.5); die Spec-`-Change-Log`-Notiz „§8-Revision 3.1" der Story 3.5 (Review-Loop-1-Fix) wurde **nicht real** als eigener Revisionslog-Eintrag übernommen (compiler.md-Revisionslog enthält keine Revision 3.1; grep-verifiziert) — **Revision 3.1 ist für Story 3.6 frei**.
## Suggested Review Order
**§5.12-Leasing-Staleness-Instruktion**
- Einstieg: §5.12-Sektion — Staleness-/Recovery-Dimension, Kern der Story (Registrierung, TTL, Klassifikation).
[`compiler.md:322`](../../schema/compiler.md#L322)
- Registrierung & generationenbasiertes TTL mit gepinnter Quellen-Präzedenz (Marker/Baum/Startwert `gen=0`).
[`compiler.md:326`](../../schema/compiler.md#L326)
- Verwaist-Klassifikation — Übernehmen gg. erneute Merge-Base-Prüfung / Stale-Markieren, nie gelöscht (AD-17e).
[`compiler.md:330`](../../schema/compiler.md#L330)
- baseline_commit-Merge-Base-Diskrepanz-Regel — git-merge-base gewinnt, notierter SHA = Sekundär-Fingerprint.
[`compiler.md:335`](../../schema/compiler.md#L335)
- `raw/`-Recovery-Basis & native `git stash`-Variante — raw/ immutable (AD-3), EC-1-Grenze.
[`compiler.md:337`](../../schema/compiler.md#L337)
- Registry-Invariante & kumulativer Aufbau — Gen-Invariante, wiki/log.md alleiniger Aufzeichnungs-Ort.
[`compiler.md:339`](../../schema/compiler.md#L339)
- log.md-Eintragspflicht & Determinismus-Vertrag — datumsgruppiert, kein Wanduhr-Timestamp steuert.
[`compiler.md:341`](../../schema/compiler.md#L341)
**§7/§8-Nachweis**
- §7-Leasing-Enum-Bullet — Staleness/Recovery „in §5.12 verankert (Story 3.6)", §5.11-Rückverweis unverändert.
[`compiler.md:397`](../../schema/compiler.md#L397)
- §8-Revisionslog Revision 3.1 — Verankerung, Abschlussklausel, AD-17d/A0-15 auf §5.12-Anker.
[`compiler.md:438`](../../schema/compiler.md#L438)
**Sandbox-Nachweis**
- Sandbox STALE-1..STALE-6 — re-executierbar, harte PASS/Fail, Erhaltungs-Invariante §5.9 Pkt. 5 (Exit 0, 12 PASS).
[`run-sandbox.sh:1`](../../_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh#L1)
**Story-Protokoll/Defers**
- log.md — Story-3.6-`done`-Eintrag (Review-Abschluss-Konvention).
[`log.md:4`](../../wiki/log.md#L4)
- sprint-status.yaml — `3-6-…` → done.
[`sprint-status.yaml:59`](../../_bmad-output/implementation-artifacts/sprint-status.yaml#L59)
- deferred-work.md — 4 aufgegriffene Story-3.5-Defers + 1 neuer Story-3.6-Defer (Branch-Ref-Schicksal).
[`deferred-work.md:417`](../../_bmad-output/implementation-artifacts/deferred-work.md#L417)