152 lines
17 KiB
Markdown
152 lines
17 KiB
Markdown
---
|
||
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: 900–1300 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. 420–437, 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)
|