Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c116751d6 | ||
|
|
5a78271f89 | ||
|
|
39471d9422 | ||
|
|
c4cdf4b93e | ||
|
|
938c05c4a8 | ||
|
|
861e65f628 | ||
|
|
4a53771a9f | ||
|
|
895b006f7f |
@@ -413,3 +413,134 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
|
||||
summary: **Frozen Spec-Intent-Typo „Nickel" im §5.10-Form-Wahl-Aufgaben-Pfad (Story-3.3-Defer U2/U7-Empfehlung)** — der Intent-Satz „Nickel die Story-3.3-Defer U2/U7-Empfehlung (…) im Sandbox auf" enthält „Nickel" statt „Nimm/Trage … auf" (oder „Schließe … auf"); er sitzt im `<frozen-after-approval>`-Block des Specs, dessen Text erst nach humaner Re-Negotiation geändert werden darf. Kein Instruktions-Defekt (der Sandbox-/authored-Text ist korrekt — FW-Szenario implementiert die Empfehlung inhaltlich). Home: Spec-Re-Negotiation (Ask-First: menschliche Autorisierung) oder Akzeptanz als kosmetischer Frozen-Fehler.
|
||||
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer): `spec-3-4-…md` Zeile ~18 (frozen Intent) — „Nickel die Story-3.3-Defer U2/U7-Empfehlung … auf".
|
||||
status: offen
|
||||
|
||||
## Deferred from: code review of spec-3-6-lease-staleness-recovery-basis-absichern-umsetzen (Story 3.6, 2026-08-19)
|
||||
|
||||
- source_spec: `_bmad-output/implementation-artifacts/spec-3-6-lease-staleness-recovery-basis-absichern.md`
|
||||
summary: **Schicksal des `lease/<area>/<id>`-Branch nach Übernahme/Stale-Markierung nicht definiert** — §5.12 Pkt. 3 „Nie gelöscht (AD-17e)" deckt die verwaiste Lease selbst ab (Lockfile, Registrierung, Marker), die Registry-Invariante Pkt. 6 den höchsten Reg-Generator; der isolierte `lease/<area>/<id>`-Branch-Ref (im Refspace von einer Verwaist-Behandlung unabhängig überlebend, da die Sandbox-Szenarien isolierte Branches über `isolate` aufbauen und die Folge-Konvergenz des Branch-Refs nicht hart prüfen) bleibt im Refspace zurück, ohne dass Übernehmen/Stale-Markieren ein deterministisches Folgeziel für ihn benennt (Branch-Cleanup/Einordnung). Kein Instruktions-Defekt für die Staleness-/Recovery-Mechanik (kein Branch blockiert eine Akquise — die Koordination läuft allein über Lockfile + Registry; AD-17e schützt Lockfile/Registry/Marker, ein gelöschter Branch zerstört keinerlei Commits), aber eine offene Evidenz-/Spez-Grenze am absetzenden (nicht akquirierenden) Nebenpfad des `lease/`-Baums. Home: spätere Instruktions-/Sandbox-Runde (Branch-Ref-Lebenszyklus) oder Story 3.8 (Determinismus des adressierbaren Zustands) — nicht in dieser Story.
|
||||
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer) — „orphan branch fate / isolate-branch cleanup signal"; `schema/compiler.md` §5.12 Pkt. 3/6, `sandbox-3-6/run-sandbox.sh` (isolate/Branch-Aufbau).
|
||||
status: offen (Home: spätere Instruktions-/Sandbox-Runde oder Story 3.8)
|
||||
|
||||
## Deferred from: code review of spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen (Story 3.5, 2026-08-19)
|
||||
|
||||
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
|
||||
summary: **holder_id Quelle/Uniqueness (Pkt. 1) nicht definiert** — das Lockfile-Feld `holder_id` („eindeutige Producer-/Run-Kennung") legt nicht fest, welcher deterministische Git-/Umgebungs-Wert es erzeugt (Branch-Suffix? Producer-Name? Run-Identifier?). Die Sandbox vergibt Beispiele (`run-a-holder`), §5.11 Pkt. 1 benennt keinen Ableitungs-Operanden. Kein Instruktions-Defekt für die Koordinations-Mechanik (Eindeutigkeit ist eine Producer-Verantwortung), aber eine offene Determinismus-Frage am Feld. Home: Story 3.6 (Lease-Registrierung) oder Story 3.8 (Determinismus-Ableitung) — ableitbarer holder_id-Default.
|
||||
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer) — „holder_id uniqueness source undefined"; `schema/compiler.md` §5.11 Pkt. 1, `run-lock-Write`.
|
||||
status: offen (Home: Story 3.6 oder 3.8)
|
||||
|
||||
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
|
||||
summary: **baseline_commit-Merge-Base-Ableitung (Pkt. 1) nicht von `git merge-base`-Laufzeit vs. notiertem `<Baseline-Commit>` reconciliert** — §5.11 nennt beide Quellen („deterministisch über `git merge-base` bzw. den notierten `<Baseline-Commit>` aus §5.9 Pkt. 6"), ohne Diskrepanz-Regel (welche gewinnt, wenn `git merge-base` eine andere Spitze liefert als die letzte notierte Mutations-Boundary? §5.9 Pkt. 6 hat die Differenz-Regel für Diff-Prüfungen, §5.11 nicht). Kein akuter Instruktionsdefekt (die Merge-Base-Disziplin zielt auf denselben Punkt), aber eine offene Disambiguierung. Home: Story 3.6 (Lease-Registrierung/Baseline) — Baseline-Auflösungs-Regel vereinheitlichen.
|
||||
evidence: Step-04-Review (2026-08-19, Blind-Hunter+Verification-Gap) — „baseline_commit merge-base derivation not reconciled"; `schema/compiler.md` §5.11 Pkt. 1.
|
||||
status: offen (Home: Story 3.6)
|
||||
|
||||
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
|
||||
summary: **Sandbox-log-Akkumulator vs. eigener `# Log`-Stand jedes isolierten Szenarios** — die Sandbox richtet je Szenario via `isolate` einen frischen (leeren) `wiki/log.md`-Stand ein und D2 demonstriert die kumulative Aufzeichnung separat; der Akkumulator lebt damit nur im Diskurs, nicht in einem durchgängigen Run-Baum. Die Koordinations-Aufzeichnung über MEHRERE Runs hinweg (consecutive Producers, die dieselbe `wiki/log.md`-Datei fortschreiben, ohne dass `isolate` sie zurücksetzt) ist nicht als eigenständiges Szenario demonstriert. Kein Instruktions-Defekt — die Regel (Pkt. 6) ist kumulativ formuliert, D2 belegt sie hart. Home: Story 3.6 (Registrierung/Verfahrnaher) oder Doku-Verbesserung der Sandbox — nichts funktionales offen.
|
||||
evidence: Step-04-Review (2026-08-19, Blind-Hunter-Layer) — „isolate wipes per-scenario log entries so cumulative coordination recording never demonstrated"; geschlossen durch D2 (Story-3.5-Sandbox).
|
||||
status: offen (Home: Story 3.6) — D2 demonstriert die kumulative Aufzeichnung hart.
|
||||
|
||||
- source_spec: `_bmad-output/implementation-artifacts/spec-3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetzen.md`
|
||||
summary: **`isolate`-Skript nutzt native `git stash`-Variante für den Dirty-Tree-Schutz nicht** — §5.11 Pkt. 3 nennt „`git stash push -- <Pfade>` … oder Kopie in eine benannte Scratch-Zone"; die Sandbox demonstriert nur die Kopier-Variante (Scratch-Zone). Die `git stash`-Variante bleibt ungetestet. Kein Instruktions-Defekt (beide Wege sind textuell zulässig, die Determinismus-Anforderung betrifft das Ergebnis), aber eine Test-Lücke der alternativen Schutz-Umsetzung. Home: Story 3.6 (Lease-/Recovery-Stash-Semantik) oder Sandbox-Erweiterung — nichts funktional offen.
|
||||
evidence: Step-04-Review (2026-08-19, Verification-Gap-Layer) — „native git stash alternative untested (only manual cp-into-scratch)"; `schema/compiler.md` §5.11 Pkt. 3.
|
||||
status: offen (Home: Story 3.6)
|
||||
|
||||
### Aufgegriffen: holder_id-Quelle/Uniqueness (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
|
||||
|
||||
- Bezug: Defer `holder_id Quelle/Uniqueness (Pkt. 1) nicht definiert` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 1).
|
||||
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 2 „holder_id-Ableitung (deterministischer Default; Defer ‚holder_id-Quelle' Story 3.5 aufgegriffen)": `holder_id := <producer>-<id>` — `<producer>` aus dem Lockfile-Feld `producer`, `<id>` der Run-Identifier aus der Branch-Form `lease/<area>/<id>`; reproduzierbar aus dem committeten Git-State (AD-17h/A0-19), kein Wanduhr-Operand.
|
||||
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-6/run-sandbox.sh` STALE-2 (holder_id deterministisch uebernommen nach `lock_write`, `lock_holder`-Assertion) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 2)
|
||||
|
||||
### Aufgegriffen: baseline_commit-Merge-Base-Diskrepanz (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
|
||||
|
||||
- Bezug: Defer `baseline_commit-Merge-Base-Ableitung (Pkt. 1) nicht … reconciliert` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 2).
|
||||
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 4 „baseline_commit-Merge-Base-Diskrepanz-Regel (Vereinheitlichung; Defer ‚baseline_commit-Diskrepanz' Story 3.5 aufgegriffen)": bei Widerspruch gewinnt der `git merge-base`-Laufzeitbefund (Commit-Boundary-Prinzip), der notierte `<Baseline-Commit>` bleibt Sekundär-Fingerprint; Fallback ohne Boundary = alle `raw/`-Dateien Zuwachs, Merge-Base = ältester committeter Fundamentpunkt (§5.9 Pkt. 6 R-1, AD-14). Dieselbe Regel für Lease-Übernahme (Pkt. 3/4).
|
||||
- Sandbox-Nachweis: STALE-2 (`git merge-base HEAD $BASE` == `$BASE`-Assertion, Merge-Base-Prüfung bei Übernahme) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 4)
|
||||
|
||||
### Aufgegriffen: Sandbox-log-Akkumulator / kumulative Registry (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
|
||||
|
||||
- Bezug: Defer `Sandbox-log-Akkumulator vs. eigener # Log-Stand …` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 3).
|
||||
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 6 „Registrierungs-Invariante & kumulativer Registry-Aufbau über Runs (Vertrag §5)": der Registry-Aufbau ist kumulativ über Runs (mehrere aufeinanderfolgende Producer schreiben dieselbe Registrierung fort, analog zum `log.md`-Akkumulator); **kein eigener `# Log`-Stand** — `wiki/log.md` ist der alleinige Aufzeichnungs-Ort. Registrierung lebt im Clone-Root-State (`registry/`), Marker Git-/Datei-Ebene ohne Eintrags-Body.
|
||||
- Sandbox-Nachweis: STALE-1 (kumulative Registry-Zeilen run-a + run-b), STALE-5 (drei Runs in EINER `registry/wiki`, Gen-Invariante 3 gehalten) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 6)
|
||||
|
||||
### Aufgegriffen: native `git stash`-Variante (Story-3.5-Defer, Home Story 3.6) — Story 3.6, 2026-08-19
|
||||
|
||||
- Bezug: Defer `isolate-Skript nutzt native git stash-Variante für den Dirty-Tree-Schutz nicht` (Defer-Block „Deferred from … spec-3-5 …", Eintrag 4).
|
||||
- Umsetzung: `schema/compiler.md` §5.12 Pkt. 5 „`raw/`-Recovery-Basis & native `git stash`-Variante (AD-3, AD-17d/A0-15)": `git stash push -- <Pfade>` als zweite textuell zulässige Schutzvariante neben der Scratch-Zonen-Kopie (beide deterministisch im Ergebnis, byte-identisch geschützt, nie gelöscht, Restore dokumentiert).
|
||||
- Sandbox-Nachweis: STALE-4 (`git stash push`/`pop` um `wiki/alpha.md`, byte-identischer Restore via SHA-256; `raw/`-SHA-256 unverändert, AD-3) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.12 Pkt. 5)
|
||||
|
||||
## Deferred from: code review of spec-3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell (Story 3.7, 2026-08-20)
|
||||
|
||||
- CONSIST-7-Label praezisie: das Zwei-Run-Label behauptet "identische Plan-/Rollback-/State-Outputs", verglichen werden aber nur die Plan-Outputs (plan_sha/plan_state); der Rollback-/State-Nachweis (s7c) laeuft einmalig, log_sha ist dokumentierte Baseline-Konstante. Die non-vakuum-Kern-Assertion (plan_sha byte-identisch ueber beide Runs) ist real; das Label ist breiter als die Assertion. Home: Sandbox-Haertungsrunde.
|
||||
- A0-20-Post-Zustand-Negativkontrolle in der Sandbox fehlt: §5.13 Pkt. 3/7 bindet den Determinismus-Vertrag an den Post-Zustand; der dokumentierte generated.at-Wanduhr-Gap (§5.10 Pkt. 8, offene A0-20-Konvention) macht den Post-Zustand ueber unabhaengige Runs nicht byte-identisch — die Sandbox testet den Gap weder negativ noch positiv. Home: Story 3.8 (A0-20-Home).
|
||||
- Pre-existing-Zeitpunktswort-Fuge: §5.9 Pkt. 6 "am Anfang der Mutationsphase" vs. gefrorene I/O-Matrix + §5.13 "Abbruch vor der Mutationsphase" — §5.9 ist in Story 3.7 Wortlaut-unververaendert (Rueckverweis-Vertrag); die Fuge ist ohne Ask-First nicht schliessbar. Home: spaetere Instruktionsrunde.
|
||||
|
||||
### Delokalisierung: Terminologie-Drift INPUT_UNCOMMITTED/UNCOMMITTED_INPUT (Story-3.7-Review, 2026-08-20)
|
||||
|
||||
- Bezug: `schema/compiler.md` §5.9 Pkt. 6 Element (1) nennt die I/O-Matrix `INPUT_UNCOMMITTED`, §5.11 Pkt. 3 nennt dieselbe Pre-Run-Prüfung `UNCOMMITTED_INPUT` — dieselbe Prüfung, zwei Labels (pre-existing seit Story 3.1/3.5; in Story 3.6/3.7 als dieselbe §5.9-P2-Prüfung deklariert, §5.11-Wortlaut geschützt). Die Story-3.7-Sandbox (`p2_plan`-Element (1)) nutzt `INPUT_UNCOMMITTED` wie §5.9-Pkt.-6-Element (1).
|
||||
- status: delokalisiert (kein Defekt — terminologische Präferenz; Home: spätere Instruktionsrunde, falls die Labels vereinheitlicht werden)
|
||||
|
||||
### Delokalisierung: Misch-Run-Coverage Neu-Anlage+Update in der Story-3.7-Sandbox (Story-3.7-Review, 2026-08-20)
|
||||
|
||||
- Bezug: §5.9 Pkt. 2/§5.7 Misch-Runs (Neu-Anlage + Update im selben Run) sind in der CONSIST-Sandbox ab Review-Loop-2 im CONSIST-2 abgedeckt (Neu-Anlage `delta` + Update `alpha`, Plan-Freeze-Menge + Voll-Rollback, Neu-Anlage-Absenz nach Rollback); übrige Phasen-/Synthese-Misch-Formen bleiben Home Story 3.8 (Determinismus-Vertrag AD-17h als Agent-Instruktions-Validator, inkl. A0-20-Post-Zustand-Test).
|
||||
- status: delokalisiert (Home: Story 3.8)
|
||||
|
||||
## Deferred from: code review of spec-3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validator (Story 3.8, 2026-08-20)
|
||||
|
||||
### Aufgegriffen: Em-Dash-Varianten-Lücke der Normalisierung (Story-3.2-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Defer «Em-Dash-"—"-Varianten-Lücke der Normalisierung» (Defer-Block „Deferred from: Story 3.2", Eintrag 1; `status: offen (Home: Story 3.8)`).
|
||||
- Umsetzung: `schema/compiler.md` §3.2 Pkt. 1b „Geschlossene Determinismus-Lücken (Story 3.8)": die Kollaps-Klasse ist um den Em-Dash `—` ergänzt (`[-–— _]` → `-`) — En-Dash, Em-Dash, Bindestrich, Unterstrich, Leerzeichen kollabieren identisch auf genau einen Bindestrich. Verankerung: §5.14 Pkt. 5 verweist auf §3.2-Pkt.-1b. Kein stiller Ausschluss; append-only-Regel-Ergänzung, bestehender §3.2-Wortlaut unverändert.
|
||||
- Sandbox-Nachweis: `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh` DET-5 (Em-Dash → identische canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §3.2 Pkt. 1b / §5.14 Pkt. 5)
|
||||
|
||||
### Aufgegriffen: Kollaps-Reichweite bei Läufen/führenden/trailenden Separatoren (Story-3.2-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Defer «§3.2-Pkt.-1b-ii: Kollaps-Reichweite bei aufeinanderfolgenden/führenden/trailenden Separatoren unbestimmt» (Defer-Block „Deferred from: code review of spec-3-2…", Eintrag 2).
|
||||
- Umsetzung: `schema/compiler.md` §3.2 Pkt. 1b „Geschlossene Determinismus-Lücken": Kollaps-Reichweite deterministisch — jedes Separator-Vorkommen kollabiert auf genau ein `-`; Läufe (`a--b` → `a-b`); führende (`-x` → `x`) und trailende (`x-` → `x`) Separatoren werden getrimmt. Verankerung §5.14 Pkt. 5; die in der Sandbox etablierte Semantik wird normativ fixiert.
|
||||
- Sandbox-Nachweis: DET-6 (Läufe `a--b`, führende `-x`, trailende `x-`, Kombination `---x---` → `x`) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §3.2 Pkt. 1b / §5.14 Pkt. 5)
|
||||
|
||||
### Aufgegriffen: Match-Scope der Stufe a (Wortgrenzen, Frontmatter-exklusiv) (Story-3.2-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Defer «§3.2-Pkt.-2a: Wortgrenzen-/Frontmatter-Scope der Stufe-a-Match-Semantik ist offen» (Defer-Block „Deferred from: code review of spec-3-2…", Eintrag 1).
|
||||
- Umsetzung: `schema/compiler.md` §3.2 Pkt. 1b „Geschlossene Determinismus-Lücken" (c) Match-Scope der Stufe a: Stufe a matcht ganze Wörter (Wortgrenzen) über den Body, exklusive YAML-Frontmatter — Substring- und Frontmatter-Treffer (z. B. in `sources[].resource`/`generated.by`) liefern keine Kandidaten (deterministischer Stufe-a-Scope). Verankerung §5.14 Pkt. 5.
|
||||
- Sandbox-Nachweis: DET-7 (ganzes Wort trifft alpha; Substring `opplung` und Frontmatter-only-Kandidat `mirror` liefern keine Kandidaten) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §3.2 Pkt. 1b / §5.14 Pkt. 5)
|
||||
|
||||
### Aufgegriffen: Orphan-Kontrolle als deterministische Reconcile-Orphan-Regel (Story-3.4-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Defer «Orphan-Kontrolle (§5.10 Pkt. 8) textuell verankert, aber nicht in der Sandbox demonstriert» (Defer-Block „Deferred from: code review of spec-3-4…", Eintrag 2; `status: offen (Home: Story 3.8)`).
|
||||
- Umsetzung: `schema/compiler.md` §5.10 Pkt. 8 „Deterministische Reconcile-Orphan-Regel (Story 3.8; §5.14-Präzisierung)": Verwaist-Befund deterministisch aus dem committeten Git-State (Zuwachs gg. `<Baseline-Commit>`, §5.9 Pkt. 6 R-1); datumsgruppierter `log.md`-Eintrag (Header `YYYY-MM-DD`, neueste zuerst; Quell-Pfad + `<Baseline-Commit>`); kein Banner/keine stille Vorbearbeitung/keine eigenständige Anlage (AD-16-Default, Epic-4-Interface). Verankerung §5.14 Pkt. 5.
|
||||
- Sandbox-Nachweis: DET-8 (verwaiste `raw/`-Evidenz ohne Ziel-Pfad-Treffer → unzugeordnet, `log.md`-Verwaist-Eintrag mit `<Baseline-Commit>`, kein Banner, keine eigenständige Anlage, Erhaltungs-Invariante nur `log.md`) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.10 Pkt. 8 / §5.14 Pkt. 5)
|
||||
|
||||
### Aufgegriffen: generated.at↔AD-17h-Gap & Zwei-Run-Identitäts-Nachweis (Story-3.3-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Defer «U6-Zwei-Run-Identitäts-Nachweis … kreuzreferenziert den generated.at↔AD-17h-Gap nicht» (Defer-Block „Deferred from: code review of spec-3-3…", Eintrag 3; `status: offen (Home: Story 3.8)`).
|
||||
- Umsetzung: `schema/compiler.md` §5.14 Pkt. 1 (Bundle-State-Definition: `generated.at`/`verified[].at` als benannte Ausnahme der Projektion), Pkt. 3 (Ausnahme-Menge: allein der `generated.at`-Wanduhr-Gap; alle übrigen Bestandteile byte-identisch) und Pkt. 2 (Zwei-Run-Mechanik: identische Plan-/Kandidaten-/Reihenfolge-Outputs und byte-identische mutierte Bundle-Bestandteile bis auf die `at`-Ausnahme). Der A0-20-Wanduhr-Gap bleibt verhaltens-seitig unverändert (Ask-First bei Verhaltens-Wechsel).
|
||||
- Sandbox-Nachweis: DET-2 (Zwei-Run-Identität nicht-vakuum, echte Content-Hashes) + DET-4 (at-Gap-Ausnahme: diff ausschließlich auf der at-Zeile, nach at-Maskierung byte-identisch; Negativkontrolle Body-Differenz = AD-16-Defekt) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.14 Pkt. 1–3)
|
||||
|
||||
### Aufgegriffen: A0-20-Post-Zustand-Negativkontrolle in der Sandbox (Story-3.7-Defer, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Defer «A0-20-Post-Zustand-Negativkontrolle in der Sandbox fehlt … Home: Story 3.8 (A0-20-Home)» (Defer-Block „Deferred from: code review of spec-3-7…", Eintrag 2).
|
||||
- Umsetzung: §5.14 Pkt. 3 definiert die Ausnahme-Menge des Bundle-State-Vergleichs (allein der `generated.at`-Wanduhr-Gap; jede andere Differenz außerhalb der Ausnahme = AD-16-Klassifikationsdefekt, Pkt. 4, textuell benannt NFR-4). Der Post-Zustand wird damit über die Zwei-Run-Mechanik (Pkt. 2) einschließlich at-Handhabung instruiert; die Sandbox demonstriert den Gap positiv und negativ.
|
||||
- Sandbox-Nachweis: DET-4 (positiv: zwei Runs mit unterschiedlichen `at` → diff nur at-Zeile, nach Maskierung byte-identisch; negativ: Body-Differenz → AD-16-Klassifikationsdefekt) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.14 Pkt. 2–4)
|
||||
|
||||
### Aufgegriffen: Misch-Run-Coverage Determinismus-Vertrag (Story-3.7-Delokalisierung, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Delokalisierung «Misch-Run-Coverage Neu-Anlage+Update in der Story-3.7-Sandbox … übrige Phasen-/Synthese-Misch-Formen bleiben Home Story 3.8 (Determinismus-Vertrag AD-17h als Agent-Instruktions-Validator, inkl. A0-20-Post-Zustand-Test)» (Block „### Delokalisierung …", `status: delokalisiert (Home: Story 3.8)`).
|
||||
- Umsetzung: Der Determinismus-Vertrag wird als geschlossene Bestätigungs-Mechanik in §5.14 verankert (Bundle-State-Projektion, Zwei-Run-Bestätigung, at-Ausnahme, Abweichungs-Klassifikation); die §5.13-Determinismus-Nachweise bleiben unverändert, die at-A0-20-Konvention ist auf die §5.14-Pkt.-3-Ausnahme-Menge verlagert (§5.13 Pkt. 7 Seam).
|
||||
- Sandbox-Nachweis: DET-1..DET-4 (Bundle-State-Definition, Zwei-Run-Identität, Abweichungs-Klassifikation, at-Gap-Ausnahme) — Exit 0.
|
||||
- status: aufgegriffen (Home erledigt in §5.14)
|
||||
|
||||
### Aufgegriffen (teilweise): Gemischte Normalisierungs-Politik Umlaute vs. Transkription — Em-Dash-Anteil (Story-3.3/Story-3.4-Defers, Home Story 3.8) — Story 3.8, 2026-08-20
|
||||
|
||||
- Bezug: Defer «Gemischte Normalisierungs-Politik im Sandbox-Evidenztext (Umlaute vs. Transkriptionen) ist ein Determinismus-Hazard für den Term-Abgleich» (Defer-Block „Deferred from: code review of spec-3-3…", Eintrag 5, `status: offen (Home: Story 3.8)`) und Defer «Sandbox-Evidenztexte transkribieren Umlaute, die Concept-Bodies nutzen Umlaut-Schreibweisen» (Defer-Block „Deferred from: code review of spec-3-4…", Eintrag 3, `status: offen (Home: Story 3.8)`).
|
||||
- Umsetzung: Der Em-Dash-Anteil beider Einträge ist in `schema/compiler.md` §3.2 Pkt. 1b / §5.14 Pkt. 5 geschlossen (Em-Dash in der Kollaps-Klasse `[-–— _]` → `-`). Der Umlaut-vs-Transkription-Anteil bleibt offen: die §3.2-Normalisierung (lowercasing + Kollaps-Klasse) deckt Umlaut-Divergenzen („Schlüssel" vs. „Schluessel") weiterhin nicht ab — kein neuer Normalisierungs-Schritt in Story 3.8 (kein neuer Regel-Operand, D-3/append-only).
|
||||
- Sandbox-Nachweis: DET-5 (Em-Dash-Anteil) — Exit 0.
|
||||
- status: teilweise aufgegriffen (Em-Dash-Anteil geschlossen in §3.2 Pkt. 1b / §5.14 Pkt. 5; Umlaut-vs-Transkription-Divergenz bleibt offen — Home: Sandbox-Vereinheitlichung oder folgende Compiler-Instruktions-Revision; kein Instruktions-Defekt)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Goal
|
||||
|
||||
Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bundle, statt es bei jedem Lauf aus sämtlichen Rohquellen neu aufzubauen (Compounding Knowledge). Bestehende Concepts werden durch neue Erkenntnisse erweitert, präzisiert oder korrigiert; Informationen aus mehreren Sources werden zu einer gemeinsamen Wissensrepräsentation mit gemischter, claim-granularer Provenienz synthetisiert; unverändertes Wissen bleibt erhalten. Die Relevanzbestimmung ("welche bestehenden Concepts sind betroffen") ist textual-deterministisch (grep/ripgrep, Markdown-Traversal, Link-Following) — ohne Embedding- oder Vector-Infrastruktur. Konkurrierende Producer koordinieren sich über ein leasing-/branch-basiertes Workspace-Modell mit Root-Scope-Lease und Dirty-Tree-Schutz; Mutationen sind commit-gebunden, und jeder Run endet in einem konsistenten, über denselben Git-State reproduzierbaren Bundle (AD-5, AD-6, AD-13, AD-17a..h).
|
||||
Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bundle, statt es bei jedem Lauf aus sämtlichen Rohquellen neu aufzubauen (Compounding Knowledge). Bestehende Concepts werden durch neue Erkenntnisse erweitert, präzisiert oder in eindeutig belegten Fällen korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation mit gemischter, claim-granularer Provenienz synthetisiert; unverändertes Wissen bleibt erhalten. Relevanz und Reconcile-Routing sind textual-deterministisch — ohne Embedding- oder Vector-Infrastruktur. Konkurrierende Producer koordinieren sich über eine atomare, worktree-übergreifende Root-Scope-Lease mit transaktionalem Dirty-Tree-, Rollback- und Release-Lifecycle. Klassifikationspflichtige Kollisionen und Widersprüche werden bis Epic 4 fail-closed als strukturierter Hold erhalten. Epic 3 gilt erst nach einem realen, unabhängigen Source→Compilation→Wiki-Abnahmegate als abgeschlossen (AD-5, AD-6, AD-13, AD-17a/b/d-f/h).
|
||||
|
||||
## Stories
|
||||
|
||||
@@ -15,15 +15,20 @@ Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bu
|
||||
- Story 3.5: Leasing & Dirty-Tree-Schutz für konkurrierende Producer umsetzen
|
||||
- Story 3.6: Lease-Staleness & Recovery-Basis absichern
|
||||
- Story 3.7: Reason/Mutate-Trennung und Konsistenz-Endzustand sicherstellen
|
||||
- Story 3.8: Determinismus-Vertrag (AD-17h) als Agent-Instruktions-Validator umsetzen
|
||||
- Story 3.8: Determinismus-Vertrag (AD-17h) als Agent-Instruktions-Validator umsetzen (wieder geöffnet)
|
||||
- Story 3.9: Deterministische Relevanz- und Reconcile-Routing schließen
|
||||
- Story 3.10: Inkrementelle Update- und Synthese-Erhaltung absichern
|
||||
- Story 3.11: Root-Scope-Leasing atomar und worktree-übergreifend akquirieren
|
||||
- Story 3.12: Lease-Lifecycle und Commit-Abschluss transaktional schließen
|
||||
- Story 3.13: Epic-3-Verifikations- und Abnahmegate
|
||||
|
||||
## Requirements & Constraints
|
||||
|
||||
- Ein Compilation Run nimmt neues Source Material und das bestehende Wiki als Input; das Wiki wird inkrementell weiterentwickelt, nie vollständig regeneriert. Unverändertes Wissen bleibt erhalten; Git-Änderungen konzentrieren sich auf die durch die neue Erkenntnis betroffenen Concepts (FR-4, FR-12).
|
||||
- Neue Informationen führen nicht automatisch zu neuen Dateien: bestehende Concepts werden erweitert, präzisiert oder korrigiert, ohne ihre Struktur zu zerstören; Beziehungen und Provenienz bleiben soweit weiterhin gültig erhalten (FR-6).
|
||||
- Mehrere Sources zum selben Thema münden in eine gemeinsame Wissensrepräsentation statt getrennter Zusammenfassungen. Das Ergebnis reflektiert den erkannten Wissensstand, konsolidiert Redundanzen und übernimmt die relevante Source-Provenienz der beteiligten Sources (FR-7, AD-4).
|
||||
- Unvollständiges, ungeprüftes oder teilweise widersprüchliches Wissen wird dargestellt, ohne künstliche Gewissheit zu erzeugen (NFR-7).
|
||||
- Relevanzbestimmung und Merges sind textual-deterministisch; Embeddings, Vector-Search, Knowledge-Graph-DB und RAG gehören nicht in den Compiler-Kern (AD-13, AD-17h, PRD-No-Goals).
|
||||
- Unvollständiges oder ungeprüftes Wissen wird ohne künstliche Gewissheit dargestellt; klassifikationspflichtige Widersprüche werden bis Epic 4 unverändert in einem strukturierten Hold erhalten (NFR-7).
|
||||
- Relevanzbestimmung, Routing, Planung und nicht-konfligierende Mutationen sind textual-deterministisch; Embeddings, Vector-Search, Knowledge-Graph-DB und RAG gehören nicht in den Compiler-Kern (AD-13, AD-17h, PRD-No-Goals).
|
||||
- Ein Run verwendet ausschließlich veröffentlichte (committete) Inhalte als Input, nie Zwischenstände während der Mutation (AD-17).
|
||||
- `raw/` bleibt immutable und dient als Recovery-Basis; ein fehlgeschlagener Run verändert es nicht (AD-3).
|
||||
|
||||
@@ -31,20 +36,21 @@ Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bu
|
||||
|
||||
- **Inkrementeller Datenfluss (AD-5, A0-6):** Interpret → Reconcile → Synthesize → Update affected Concepts. Startpunkt ist immer das bestehende Bundle — niemals "Regenerate Everything" aus allen Rohquellen. Validiert durch die Incrementality-Anforderung (SM-1, FT-6).
|
||||
- **Reason/Mutate-Trennung (AD-6, A0-7):** Logische Phasen Analyse → Reconcile → Plan Changes → Mutate → Validate. Keine eigene Workflow Engine; ein Agent kann die Phasen in einer Session durchführen, der beobachtbare Endzustand des Bundles muss bei Abbruch konsistent sein.
|
||||
- **Deterministische Relevanzbestimmung (A0-18):** Führt zu einer nachvollziehbaren Candidate-Liste von Concept-Pfaden über grep/ripgrep auf `wiki/`, Markdown-Traversal von `index.md` und Link-Following — deterministisch statt probabilistisch. Gleichsam Grundlage für die Bereichszuordnung (gleiches textuelles Prinzip wie AD-7c).
|
||||
- **Deterministische Relevanz und Reconcile-Routing (A0-18):** Ein geschlossenes Term-Ziehverfahren beziehungsweise ein explizites Term-Manifest, identische Normalisierung von Suchterm und Body sowie literal-sichere Suche führen zu einer nachvollziehbaren Candidate-Liste. Eine exklusive Routing-Tabelle unterscheidet `UPDATE`, `CREATE`, `ORPHAN/HOLD` und echten `NO_OP`; gleiches Eingabemanifest erzeugt dieselben Kandidaten und Entscheidungen.
|
||||
- **Keine eigenständige LLM-Runtime:** Der ausführende agentische Host (Claude/Codex-Adapter) orchestriert die Sequenz gemäß AD-17 (Lease holen, innerhalb des geleasten Bereichs mutieren, committen, freigeben); keine separaten Prozesse oder ein Server (AD-11).
|
||||
- **Leasing-Modell (AD-17a..f, A0-12..A0-16):** Producer arbeiten auf `lease/<area>/<id>`-Branches; Lease-Akquise gegen einen eindeutigen Commit-Object-Wert (Merge-Base-Disziplin); ein Lockfile realisiert semantisch identisch in jedem Adapter — Realisierung ist nicht pro Adapter frei wählbar. Die Lease umfasst die Root-Scope (`wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien).
|
||||
- **Kein textueller Auto-Merge (AD-17c, A0-14):** Zwei Branches mit Änderungen am selben Concept-Pfad werden nie textuell automatisch gemerged; der Merge ist compiler-vermittelt und durchläuft die AD-16-Klassifikation mit explizitem `log.md`-Eintrag, falls die Inhalte ungleich sind.
|
||||
- **Dirty-Tree-Schutz (AD-17e/f, A0-16):** Vor jeder Mutation wird die Working Copy des zu mutierenden Bereichs geprüft; fremde uncommittete Änderungen werden geschützt (Stash/Scratch-Zone) und in `log.md` dokumentiert. Mutationen operieren nur auf Directory-/Commit-Ebene — Commit-Boundary ist die Mutation-Boundary.
|
||||
- **Lease-Staleness (AD-17d, A0-15):** TTL plus Lease-Registrierung im Clone-Root-State; uncommittete Leases gelten nach Run-Abbruch als stale, verwaiste Leases können übernommen oder als stale markiert und protokolliert werden; `raw/` dient als Zugriffs- und Consistency-Basis.
|
||||
- **Auflösungsautorität (AD-17g, A0-17):** Der die Lease haltende Compilation Run löst AD-16-Kollisionen (Default: Erhaltung) auf; menschliche Eskalation nur bei Unentscheidbarkeit; die Auflösung wird an Commit-Hash und Klassifikation im `log.md` gebunden.
|
||||
- **Determinismus-Vertrag (AD-17h/FT-10, A0-19):** Über denselben Git-State und dasselbe Eingabeset erzeugen zwei unabhängige Runs denselben Bundle-State; Abweichung gilt als Fehler der Klassifikation, nicht als Rauschen. Das Enforcement lebt im MVP als Agent-Instruktions-Validator (D-3/Q-6) und muss mechanisch bestätigt sein, bevor es tragend wird.
|
||||
- **Inkrementelle Evolution und menschliche Kuratierung (A0-21, FT-9):** Unabhängige Concepts werden nicht bei jedem Lauf regeneriert; eine menschliche Korrektur eines maschinell erzeugten Concepts überlebt als normale Kuratierung (Datei-Edit + Git) — kein Nulling-Diff und keine Re-Kompilation des ganzen Bundles.
|
||||
- **Atomare Root-Scope-Lease (AD-17a/b, A0-12/13):** Producer behalten `lease/<area>/<id>` als Branch-Konvention, akquirieren aber genau einen atomaren, scope-bezogenen Lock im clone-geteilten Zustand. Die Run-ID ist Lock-Inhalt, nicht Exklusivitätsschlüssel; unterschiedliche IDs und Worktrees konkurrieren um dieselbe Root-Scope (`wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien).
|
||||
- **Fail-closed Kollisionsgrenze:** Zwei Branches mit ungleichen Änderungen am selben Concept-Pfad werden nie textuell automatisch gemerged. Epic 3 erhält beide Commit-Hashes und den Scope in einem strukturierten Hold; AD-16-Klassifikation, semantische Auflösung und Disagreement-Dokumentation sind Story 4.1/4.2 (AD-17c/g, A0-14/17).
|
||||
- **Transaktionaler Dirty-Tree-/Rollback-/Release-Lifecycle (AD-6, AD-17d-f, A0-7/15/16):** Eine eindeutige Zustandsmaschine schützt getrackte und ungetrackte Fremdänderungen, restauriert bei FAIL explizit den Baseline-Commit und hinterlässt bei SUCCESS Mutation, zulässigen Nachweis, Lease-Freigabe und einen sauberen Worktree dauerhaft. Eine lebende Lease wird nicht allein durch Generationserhöhung stale.
|
||||
- **Determinismus-Vertrag (AD-17h/FT-10, A0-19):** Ein kanonisches Eingabemanifest bindet Baseline, geordnete Sources und jeden output-sichtbaren Run-/Zeit-/Identitätswert. Zwei getrennte saubere Worktrees und frische Agent-Kontexte erzeugen denselben Bundle-State und rekonstruierbare Run-Receipts; hart codierte Pläne oder Concept-Bodies sind kein Nachweis.
|
||||
- **Geteilte A0-21-Grenze:** Epic 3 beweist mit Story 3.10 den Incrementality-Teil — Erhaltung unabhängigen und weiterhin gültigen vorhandenen Wissens. Die spezifische Erhaltung und Konfliktbehandlung menschlicher Korrekturen (FT-9) bleibt bei Story 4.3.
|
||||
- **Synthese bleibt source-grounded (AD-4):** Bestehende Concepts dürfen Kontext und Synthese liefern, fachliche Aussagen müssen aber auf nachvollziehbare Sources zurückführbar bleiben; Wiki-Links ersetzen nie die Provenienz zur ursprünglichen Evidenz.
|
||||
- **Epic-3-Abnahmegate:** Ein portables, fail-fast ausführbares Gate ruft den vollständigen autorisierten Bundle-Validator auf und lässt einen frischen Agent-Kontext die kanonische Instruktion `schema/compiler.md` über einer repräsentativen Fixture ausführen. Der Harness schreibt keine erwarteten Wiki-Ausgänge selbst. Ein unabhängiger Consumer-Smoke-Test prüft nur die Epic-5-Abhängigkeit und nimmt FR-15/FR-16 nicht vorweg. Ein Defizit des autorisierten Validator-Vertrags blockiert das Gate und verlangt eine separat genehmigte Epic-1-Remediation.
|
||||
|
||||
## Cross-Story Dependencies
|
||||
|
||||
- Baut auf dem Workspace aus Epic 1 auf (`raw/` immutable, Bundle-Root, Schema-Validierung) und konsumiert die in Epic 2 erzeugten, OKF-konformen, verlinkten Concepts mit claim-granularer Provenienz als vorhandenes Wissen.
|
||||
- Der compiler-vermittelte Merge (AD-17c) und die Kollisionsauflösung (AD-17g) setzen die AD-16-Klassifikation samt `log.md`-Dokumentation voraus, die in Epic 4 umgesetzt wird.
|
||||
- Story 3.9 liefert die deterministische Routing-Basis für Story 3.10. Parallel dazu liefert Story 3.11 die atomare Lease-Basis für Story 3.12.
|
||||
- Story 3.8 wird nach 3.9–3.12 mit echten unabhängigen Runs abgeschlossen; Story 3.13 ist anschließend das finale Epic-Abnahmegate.
|
||||
- AD-17c/A0-14 sind geteilt: Epic 3 verantwortet die No-Auto-Merge-/fail-closed-Sicherheitsgrenze, Epic 4 Klassifikation und semantische Auflösung. AD-17g/A0-17 verbleiben vollständig in Epic 4. A0-21 ist ebenfalls geteilt: Story 3.10 verantwortet den Incrementality-Teil, Story 4.3 Human Curation/FT-9.
|
||||
- Das Leasing-/Dirty-Tree-Modell dieses Epics koordiniert Compiler-Runs mit menschlicher Bearbeitung (Q-2) und liefert die Grundlage für die Git-Nachvollziehbarkeit und Auflösungs-Dokumentation, auf die Epic 5 aufsetzt.
|
||||
- Keine UX/Design-Anteile relevant: v1 ist datei-/CLI-basiert ohne GUI (A-3, AD-11).
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
#!/usr/bin/env bash
|
||||
# Story 3.5 — Sandbox-Tests der Leasing-/Dirty-Tree-Dimension (§5.11, Revision 3.0)
|
||||
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb35)
|
||||
# Zweck: die Koordinations-Mechanik fuer konkurrierende Producer (§5.11) als
|
||||
# re-executierbarer Run-Demonstrator durchspielen —
|
||||
# L1 LEASE_AKQUISE (lease/<area>/<id>-Branch, Lockfile, Merge-Base-Disziplin;
|
||||
# AC-1, AD-17a, A0-12),
|
||||
# L2 LEASE_HOLD (Lockfile existiert -> Lease-Hold, kein Ueberschreiben, keine
|
||||
# Mutation; I/O-Matrix LEASE_AKQUISE-Error-Handling),
|
||||
# L3 ROOT_SCOPE (wiki/ inkl. log.md, index.md, aller Root-Dateien; kein Bereich
|
||||
# jenseits wiki/; AC-2, AD-17b, A0-13),
|
||||
# L4 DIRTY_TREE_SCHUTZ (Pre-Mutation-Pruefung, Stash/Scratch-Zone, log.md-
|
||||
# Dokumentation; fremde uncommittete Aenderung wird NIE geloescht —
|
||||
# Negativ-Kontrolle; UNCOMMITTED_INPUT-Abbruch; AC-3, AD-17e/f, A0-16),
|
||||
# L5 NO_AUTO_MERGE (kein textueller Auto-Merge bei ungleichem Pfad-Inhalt;
|
||||
# compiler-vermittelter AD-16-Pfad mit log.md-Eintrag, Default Erhaltung;
|
||||
# Eskalation AD-17g; AC-4, AD-17c, A0-14),
|
||||
# L6 COMMIT_BOUNDARY (Commit-Boundary = Mutations-Boundary; Verletzung ->
|
||||
# HARD-FAIL-Detektion; Rollback §5.3),
|
||||
# N1 ??-GHOST-DIFF-Negativkontrolle (ungetrackte Nicht-Ziel-Datei unter wiki/),
|
||||
# D1 DETERMINISMUS (AD-17h/A0-19: Lockfile-Inhalt + Merge-Klassifikation
|
||||
# deterministisch aus dem committeten Git-State; Zwei-Run-Identitaet).
|
||||
# Erhaltungs-Invariante (§5.9 Pkt. 5 / AD-5 / FT-6) als HARDE Assertion je
|
||||
# Leasing-faehigem Run; Frontmatter-Konformitaet (Vertrag §3.3/§3.4-Subset,
|
||||
# P2-Element (6)) je erzeugtem/aktualisiertem Concept (Muster Story-3.4-Sandbox).
|
||||
# Linux-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/-Baum.
|
||||
set -u
|
||||
ROOT=$(mktemp -d /tmp/sb35-XXXXXX)
|
||||
SB="$ROOT/sb"
|
||||
mkdir -p "$SB/wiki" "$SB/raw" "$SB/lease" "$SB/scratch"
|
||||
cd "$SB"
|
||||
git init -q
|
||||
git config user.email "sandbox@test"
|
||||
git config user.name "Sandbox"
|
||||
|
||||
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit = Merge-Base) ----------
|
||||
# Mini-Bundle mit zwei Root-Concepts (alpha + gamma als nicht-betroffene Kontrolle).
|
||||
cat > wiki/index.md <<'EOF'
|
||||
# Index
|
||||
- [Alpha](alpha.md)
|
||||
- [Gamma](gamma.md)
|
||||
EOF
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
EOF
|
||||
cat > wiki/gamma.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/gamma-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
|
||||
EOF
|
||||
cat > wiki/log.md <<'EOF'
|
||||
# Log
|
||||
EOF
|
||||
cat > raw/alpha-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: deterministische Init-Sequenz.
|
||||
### S-2
|
||||
Evidenz v1: ausschließlich lokale Netze.
|
||||
EOF
|
||||
cat > raw/gamma-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: Gamma-Thema.
|
||||
EOF
|
||||
git add -A
|
||||
git commit -qm "Baseline"
|
||||
BASE=$(git rev-parse HEAD)
|
||||
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
echo
|
||||
|
||||
runlabel() { echo; echo "########## $1 ##########"; }
|
||||
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien);
|
||||
# jede Akquise startet von derselben Merge-Base aus (deterministisch, AD-17h).
|
||||
# Szenarien-lokale Leasing-/Tmp-Branches (lease/*, tmp/*) aus dem VORIGEN Szenario
|
||||
# werden entfernt, damit Branch-Namen wie lease/wiki/run-a szenarien-uebergreifend
|
||||
# wiederverwendbar sind (Determinismus: gleicher Startzustand je Szenario).
|
||||
# -f erzwingt den Checkout auch bei uncommitteten Resten aus dem Vorszenario
|
||||
# (z.B. nach einer abgebrochenen Merge-Simulation) — kein Fehlerpfad nach aussen.
|
||||
isolate() {
|
||||
git checkout -qf -B "$1" "$BASE"
|
||||
git for-each-ref --format='%(refname:short)' refs/heads/lease refs/heads/tmp 2>/dev/null | while read b; do
|
||||
git branch -D "$b" >/dev/null 2>&1 || true
|
||||
done
|
||||
git reset -q --hard "$BASE"
|
||||
git clean -qfd wiki raw lease scratch
|
||||
}
|
||||
|
||||
# Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6): Baseline-Diff + porcelain,
|
||||
# normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert).
|
||||
probe() {
|
||||
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
|
||||
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
|
||||
}
|
||||
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
|
||||
|
||||
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
|
||||
inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß (msg stderr)
|
||||
local expected="$1" p u bad=0
|
||||
got=$(inv_set)
|
||||
for p in $got; do
|
||||
case " $expected " in
|
||||
*" $p "*) ;;
|
||||
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
|
||||
esac
|
||||
done
|
||||
for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do
|
||||
u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||')
|
||||
case " $expected " in
|
||||
*" $u "*) ;;
|
||||
*) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (§5.9 Pkt. 8)" >&2; bad=1;;
|
||||
esac
|
||||
done
|
||||
return $bad
|
||||
}
|
||||
assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1
|
||||
if inv_viol "$1"; then
|
||||
echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
assert_frontmatter_ok() {
|
||||
( assert_frontmatter "$@" ) >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5; Muster Story-3.4) ----------
|
||||
assert_frontmatter() {
|
||||
local f="$1"; shift
|
||||
local r bad=0
|
||||
local v
|
||||
v=$(awk '
|
||||
/^---$/{n++; if(n==2) exit; next}
|
||||
/^[A-Za-z_][A-Za-z0-9_-]*:/{
|
||||
k=$0; sub(/:.*/,"",k)
|
||||
if (k=="sources") top="sources"
|
||||
else if (k=="generated" || k=="verified") top="genver"
|
||||
else top="other"
|
||||
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
|
||||
if (seenk[k]++) print "DUP_KEY:" k
|
||||
r=0
|
||||
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
|
||||
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
|
||||
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
|
||||
if (r>0) lastr=r
|
||||
next
|
||||
}
|
||||
/^[[:space:]]/{
|
||||
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
|
||||
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
|
||||
ik=substr($0,1,RLENGTH-1)
|
||||
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
|
||||
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
|
||||
if (top=="other") print "INNER_UNBEFUGT:" ik
|
||||
}
|
||||
}
|
||||
' "$f")
|
||||
if [ -n "$v" ]; then
|
||||
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
|
||||
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
|
||||
for r in "$@"; do
|
||||
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
|
||||
done
|
||||
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
|
||||
}
|
||||
|
||||
# ---------- Leasing-Helfer (§5.11; deterministisch, AD-17h/A0-19) ----------
|
||||
# Lockfile-Format (Pkt. 1: semantisch identisch in jedem Adapter, A0-12):
|
||||
# area: <area>
|
||||
# id: <id>
|
||||
# producer: <producer>
|
||||
# baseline_commit: <voller SHA des Merge-Base/Commit-Object-Werts>
|
||||
# holder_id: <eindeutige Producer-/Run-Kennung>
|
||||
# Lockfile liegt AUSSERHALB von wiki/ und raw/ (lease/<area>/<id>.lock) —
|
||||
# Root-Scope-Lease bindet den Bereich, nicht den Lockfile-Pfad (§5.11 Pkt. 2/7).
|
||||
lock_write() { # $1=area $2=id $3=producer $4=holder_id $5=baseline_commit
|
||||
local f="lease/$1/$2.lock"
|
||||
{ echo "area: $1"; echo "id: $2"; echo "producer: $3"; echo "baseline_commit: $5"; echo "holder_id: $4"; } > "$f"
|
||||
}
|
||||
lock_exists() { [ -f "lease/$1/$2.lock" ]; }
|
||||
lock_holder() { grep '^holder_id:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
|
||||
lock_baseline() { grep '^baseline_commit:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
|
||||
|
||||
# akquire: Lease-Akquise (Pkt. 1) — prueft Lockfile (Lease-Hold, kein Ueberschreiben),
|
||||
# dann Merge-Base-Disziplin (eindeutiger Commit-Object-Wert == $BASE == HEAD des
|
||||
# Merge-Base-Ancestors). Fehler => exit 1 (HARD-FAIL der Assertion im Szenario).
|
||||
akquire() { # $1=area $2=id $3=producer $4=holder_id
|
||||
local area="$1" id="$2" producer="$3" holder="$4"
|
||||
if lock_exists "$area" "$id"; then
|
||||
echo "HARD-FAIL (Lease-Hold): Lockfile lease/$area/$id.lock existiert bereits — Akquise verweigert, kein Ueberschreiben (Pkt. 1)" >&2
|
||||
return 1
|
||||
fi
|
||||
mkdir -p "lease/$area"
|
||||
lock_write "$area" "$id" "$producer" "$holder" "$BASE"
|
||||
return 0
|
||||
}
|
||||
# Dirty-Tree-Schutz (Pkt. 3): Pre-Mutation-Prüfung des Mutationsbereichs gg. HEAD.
|
||||
# $1 = Mutationsbereich (z.B. wiki/), $2 = erlaubte (eigene) Run-Zielfelder als
|
||||
# Leerzeichen-getrennte, wiki-relativ-normalisierte Namen (z.B. "alpha log").
|
||||
# Ausgabe: 0 = nur eigene/legitime uncommittete Einträge im Bereich (clean),
|
||||
# 1 = mindestens EIN fremder uncommitteter Eintrag (Dirty Tree) —
|
||||
# Schutz nötig (§5.11 Pkt. 3), nie still geloescht (AD-17e).
|
||||
# Normalisierung wie probe(): wiki/-Praefix + .md-Suffix strippen.
|
||||
db_check() { # $1=Bereich $2=legit
|
||||
local legit="$2" p maybe
|
||||
maybe=$(git status --porcelain -- "$1" | awk '{print $2}' | sed -e 's|^wiki/||' -e 's|\.md$||')
|
||||
[ -z "$maybe" ] && return 0
|
||||
for p in $maybe; do
|
||||
case " $legit " in
|
||||
*" $p "*) : ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# log.md-Leasing-Eintraege (Pkt. 6: Lease-Akquise / Dirty-Tree-Schutz / Merge-
|
||||
# Klassifikation / Eskalation / Freigabe; Baseline-Commit im Eintrag, D-2).
|
||||
# Datumsgruppen-Konvention (Vertrag §5, §5.9 Pkt. 4 / §5.11 Pkt. 6): jeder Eintrag
|
||||
# haengt SEINEN eigenen '## YYYY-MM-DD'-Header + Bullet als Header-Bullet-Paar an
|
||||
# (kein 'printf \n##' je Zeile -> keine doppelten/leeren Header; cumulative append,
|
||||
# neueste Bullets am Ende der Datumsgruppe = deterministisch, AD-17h).
|
||||
log_lease() { # $1=area $2=id $3=holder
|
||||
printf '## 2026-08-19\n- Lease-Akquise: %s/%s durch %s (Baseline %s, §5.11 Pkt. 1/6a)\n' "$1" "$2" "$3" "$BASE" >> wiki/log.md
|
||||
}
|
||||
log_dirty() { # $1=pfad $2=sicherungsziel
|
||||
printf '## 2026-08-19\n- Dirty-Tree-Schutz: %s nach %s gesichert (nie geloescht, §5.11 Pkt. 3/6b)\n' "$1" "$2" >> wiki/log.md
|
||||
}
|
||||
log_merge() { # $1=area $2=id $3=klassifikation $4=begruendung
|
||||
printf '## 2026-08-19\n- Merge-Klassifikation: %s/%s -> %s (%s; AD-16, kein textueller Auto-Merge, §5.11 Pkt. 4/6c)\n' "$1" "$2" "$3" "$4" >> wiki/log.md
|
||||
}
|
||||
log_eskalation() { # $1=area $2=id
|
||||
printf '## 2026-08-19\n- Merge-Eskalation (AD-17g): %s/%s unentscheidbar — menschliche Auflösung erforderlich (§5.11 Pkt. 4/6d)\n' "$1" "$2" >> wiki/log.md
|
||||
}
|
||||
log_release() { # $1=area $2=id $3=holder
|
||||
printf '## 2026-08-19\n- Lease-Freigabe: %s/%s durch %s (Release nach committetem Run, §5.11 Pkt. 1/6e)\n' "$1" "$2" "$3" >> wiki/log.md
|
||||
}
|
||||
# free_lease: deterministische Lease-Freigabe (Pkt. 1, Release) — entfernt das
|
||||
# Lockfile als Freigabe-Marker und dokumentiert den Abschluss in log.md (Pkt. 6e),
|
||||
# nur NACH dem committeten Run (Commit-Boundary, Pkt. 5). Eine inhaertoerfreie
|
||||
# (abgebrochene) Lease verbleibt bis Story-3.6-Staleness — hier deterministisch.
|
||||
free_lease() { # $1=area $2=id $3=holder
|
||||
local f="lease/$1/$2.lock"
|
||||
[ -f "$f" ] || { echo "HARD-FAIL (free_lease): Lockfile existiert nicht — Release einer unakquirierten Lease (Pkt. 1)" >&2; return 1; }
|
||||
log_release "$1" "$2" "$3"
|
||||
rm -f "$f"
|
||||
}
|
||||
|
||||
# =====================================================================
|
||||
runlabel "L1: LEASE_AKQUISE (AC-1, AD-17a, A0-12) — Producer erwirbt Lease auf lease/<area>/<id>-Branch vom Merge-Base; Lockfile mit eindeutigem Commit-Object-Wert; Root-Scope inkl. log.md/index.md"
|
||||
isolate l1
|
||||
echo "--- Akquise: Branch lease/wiki/run-a von der Merge-Base ($BASE); Refresh: Freeze-Command-Konvention, Commit-Object-Wert == $BASE (deterministisch aus dem committeten Git-State, AD-17h) ---"
|
||||
git checkout -q -b lease/wiki/run-a "$BASE"
|
||||
akquire wiki run-a "producerA" "run-a-holder"
|
||||
# Assertions Pkt. 1: Branch-Form, Lockfile, Merge-Base-Disziplin
|
||||
[ "$(git branch --show-current)" = "lease/wiki/run-a" ] || { echo "HARD-FAIL (L1): Arbeits-Branch ist nicht lease/<area>/<id> (Pkt. 1)" >&2; exit 1; }
|
||||
grep -q '^area: wiki$' lease/wiki/run-a.lock || { echo "HARD-FAIL (L1): Lockfile-Feld area fehlt/falsch (Pkt. 1, A0-12)" >&2; exit 1; }
|
||||
grep -q '^id: run-a$' lease/wiki/run-a.lock || { echo "HARD-FAIL (L1): Lockfile-Feld id fehlt/falsch (Pkt. 1)" >&2; exit 1; }
|
||||
grep -q '^producer: producerA$' lease/wiki/run-a.lock || { echo "HARD-FAIL (L1): Lockfile-Feld producer fehlt/falsch (Pkt. 1)" >&2; exit 1; }
|
||||
[ "$(lock_baseline wiki run-a)" = "$BASE" ] || { echo "HARD-FAIL (L1): Lockfile-baseline_commit != Merge-Base-Object-Wert (Pkt. 1, Merge-Base-Disziplin)" >&2; exit 1; }
|
||||
[ "$(lock_holder wiki run-a)" = "run-a-holder" ] || { echo "HARD-FAIL (L1): Lockfile-Feld holder_id fehlt/falsch (Pkt. 1)" >&2; exit 1; }
|
||||
echo "--- Leasing-Run (Root-Scope): Update auf alpha + Index-Link + log.md — die Lease umfasst wiki/ inkl. log.md/index.md/aller Root-Dateien (Pkt. 2) ---"
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2)\.|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — überholt durch: Alpha kann seit v2 auch isolierte Netze bedienen (raw/alpha-v2.md#S-2).|' wiki/alpha.md
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-2
|
||||
Evidenz v2: Alpha kann seit v2 auch isolierte Netze bedienen.
|
||||
EOF
|
||||
# sources-Zuwachs lexikografisch + at-Bump + log-Akquise-Eintrag (Pkt. 6a)
|
||||
sed -i 's|^ at: .*| at: 2026-08-19T12:00:00Z|' wiki/alpha.md
|
||||
sed -i '/^ - resource: raw\/alpha-v1.md/a\ - resource: raw\/alpha-v2.md\n id: s2' wiki/alpha.md
|
||||
log_lease wiki run-a "run-a-holder"
|
||||
echo "--- Assertion: ROOT-SCOPE (Pkt. 2) — die Lease bindet wiki/; kein Lockfile-/Scope-Bereich jenseits wiki/ (naechste Zeile MUSS failen) ---"
|
||||
grep -q 'scope: raw\|scope: lease\|scope: /' lease/wiki/run-a.lock && { echo "HARD-FAIL (L2): Lockfile enthaelt Scope-Angabe jenseits wiki/ (Root-Scope, AD-17b)" >&2; exit 1; }
|
||||
echo "--- aber: der Lockfile-Pfad selbst liegt ausserhalb wiki/ (kein Concept-Frontmatter-Key, kein wiki/-Eintrag) ---"
|
||||
case "/lease/wiki/run-a.lock" in /wiki/*|/raw/*) echo "HARD-FAIL (L1/L2): Lockfile liegt im Bundle-Bereich (Pkt. 1/2)" >&2; exit 1;; esac
|
||||
echo "--- Assertion: log.md-Akquise-Eintrag (Pkt. 6a) + Baseline-Commit im Eintrag (D-2) ---"
|
||||
grep -qF 'Lease-Akquise: wiki/run-a durch run-a-holder' wiki/log.md || { echo "HARD-FAIL (L1): log.md-Akquise-Eintrag fehlt (Pkt. 6a)" >&2; exit 1; }
|
||||
grep -qF "$BASE" wiki/log.md || { echo "HARD-FAIL (L1): Baseline-Commit fehlt im log.md-Eintrag (D-2)" >&2; exit 1; }
|
||||
echo "--- Assertion: Lockfile rdwr-basiert deterministisch (Feldreihenfolge fix, keine Zeitstempel) + committed Git-State-Eingabe ---"
|
||||
[ "$(head -1 lease/wiki/run-a.lock)" = "area: wiki" ] || { echo "HARD-FAIL (D1): Lockfile-Feldreihenfolge nicht deterministisch (AD-17h)" >&2; exit 1; }
|
||||
grep -qiE 'timestamp|wallclock|now|date' lease/wiki/run-a.lock && { echo "HARD-FAIL (D1): Lockfile enthaelt Wanduhr/Zeitstempel (A0-20-Gap-Konvention; AD-17h)" >&2; exit 1; }
|
||||
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5; erlaubt: alpha (Update) + log; Lockfile ausserhalb wiki/ zaehlt nicht) ---"; probe
|
||||
assert_invariant "alpha log"
|
||||
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
|
||||
echo "--- Release-Abschluss des Runs (Pkt. 1/6e): nach committeter Mutation gibt der Producer die Lease deterministisch frei (Lockfile entfernt, log.md-Eintrag) ---"
|
||||
git add wiki/alpha.md wiki/log.md
|
||||
git commit -qm "Run a (L1): validierte Root-Scope-Mutation committet (Commit-Boundary)"
|
||||
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (L1): Lockfile fehlt VOR der Freigabe — Freigabe nicht demonstrierbar (Pkt. 1)" >&2; exit 1; }
|
||||
free_lease wiki run-a "run-a-holder"
|
||||
[ ! -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (L1): Lockfile nach Freigabe weiterhin vorhanden — Lease nicht deterministisch freigegeben (Pkt. 1/6e)" >&2; exit 1; }
|
||||
grep -qF -- '- Lease-Freigabe: wiki/run-a durch run-a-holder' wiki/log.md || { echo "HARD-FAIL (L1): Freigabe-Eintrag fehlt in log.md (Pkt. 6e)" >&2; exit 1; }
|
||||
echo "RESULT: PASS — L1: lease/<area>/<id>-Branch, Lockfile (area/id/producer/baseline_commit/holder_id, Merge-Base $BASE), Root-Scope inkl. log/index, log.md-Akquise-Eintrag (D-2, Pkt. 6a), Release (Pkt. 1/6e)"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "L2: LEASE_HOLD (I/O-Matrix LEASE_AKQUISE-Error-Handling) — Lockfile existiert bereits -> zweite Akquise wird verweigert (LEASE-HOLD), kein Ueberschreiben, keine Mutation"
|
||||
isolate l2
|
||||
git checkout -q -b lease/wiki/run-b "$BASE"
|
||||
akquire wiki run-b "producerB" "run-b-holder"
|
||||
echo "--- zweiter Producer versucht dieselbe Lease (gleiches <area>/<id>) — MUSS verweigert werden ---"
|
||||
# Leistung: akquire gibt bei bestehendem Lockfile Exit 1 (HARD-FAIL der Assertion
|
||||
# im Szenario). Wir prüfen den EXIT-STATUS hart (nicht nur den Bool), damit der
|
||||
# Lease-Hold-Fehlerpfad effektiv assertiert ist (kein toter Code).
|
||||
if akquire wiki run-b "producerC" "run-c-holder" 2>/dev/null; then
|
||||
echo "HARD-FAIL (L2): zweite Akquise wurde nicht verweigert (Lease-Hold, kein Ueberschreiben, Pkt. 1)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "RESULT: PASS — zweite Akquise verweigert (Lease-Hold; akquire Exit != 0)"
|
||||
fi
|
||||
# holder unveraendert = keine Ueberschreibung
|
||||
[ "$(lock_holder wiki run-b)" = "run-b-holder" ] || { echo "HARD-FAIL (L2): Lockfile wurde ueberschrieben (holder geaendert) — kein Ueberschreiben (Pkt. 1)" >&2; exit 1; }
|
||||
[ "$(lock_baseline wiki run-b)" = "$BASE" ] || { echo "HARD-FAIL (L2): Lockfile-baseline wurde geaendert (Pkt. 1)" >&2; exit 1; }
|
||||
echo "--- keine Mutation durch den abgewiesenen Producer (Bundle unveraendert zum Baseline; Probe leer) ---"; probe
|
||||
assert_invariant ""
|
||||
echo "RESULT: PASS — L2: Lease-Hold hart; Lockfile unveraendert; keine Mutation durch abgewiesenen Producer"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "L3: ROOT_SCOPE (AC-2, AD-17b, A0-13) — Root-Scope-Lease umfasst wiki/ INKL. log.md, index.md und aller Root-Dateien; kein Bereich jenseits wiki/"
|
||||
isolate l3
|
||||
git checkout -q -b lease/wiki/run-c "$BASE"
|
||||
akquire wiki run-c "producerC" "run-c-holder"
|
||||
echo "--- Leasing-Run mutiert die Root-Scope vollstaendig: index.md (neuer Link) + log.md (Eintrag) + alpha.md (Update) + gamma.md (Kontroll-Concept bleibt unberuehrt) ---"
|
||||
sed -i 's|^# Index$|# Index\n- [Beta](beta.md)|' wiki/index.md
|
||||
cat > wiki/beta.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/beta-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-19T12:00:00Z
|
||||
---
|
||||
Beta ist ein neues Root-Concept zur Demonstration der Root-Scope-Lease (raw/beta-v1.md#S-1).
|
||||
EOF
|
||||
cat > raw/beta-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: Beta-Oberthema.
|
||||
EOF
|
||||
log_lease wiki run-c "run-c-holder"
|
||||
echo "--- Assertion (Pkt. 2): die Root-Scope-Lease bindet wiki/ inkl. log.md, index.md, aller Root-Dateien — Beta/Index/Log sind legitime Mutations-Zielfelder dieser Lease (Erhaltungs-Invariante erwartet beta index log) ---"
|
||||
grep -qF -- '- [Beta](beta.md)' wiki/index.md || { echo "HARD-FAIL (L3): index.md-Root-Datei nicht durch Root-Scope-Lease mutierbar (Pkt. 2)" >&2; exit 1; }
|
||||
grep -qF 'Lease-Akquise: wiki/run-c' wiki/log.md || { echo "HARD-FAIL (L3): log.md-Eintrag fehlt (Root-Scope inkl. log.md, Pkt. 2/6)" >&2; exit 1; }
|
||||
echo "--- Assertion ??-Sicht (Pkt. 2/5): das NEUE legal angelegte wiki/beta.md erscheint als ??-Eintrag und wird von der Erhaltungs-Invariante als erlaubte Neu-Anlage statt als Ghost-Diff klassifiziert (??-Sicht ist nicht vacuous: sie sieht Neu-Anlagen als rechtmaessige Ziel-Pfade) ---"
|
||||
git status --porcelain -- wiki/ | grep -q '^?? wiki/beta.md' || { echo "HARD-FAIL (L3): neue Root-Datei wiki/beta.md erscheint nicht als ??-Eintrag (??-Sicht blind fuer Neu-Anlagen)" >&2; exit 1; }
|
||||
echo "--- Assertion: KEIN Bereich jenseits wiki/ ist durch die Lease erfasst — raw/ und lease/ sind keine Root-Scope-Mutationsziele ---"
|
||||
[ -z "$(git status --porcelain -- raw/ | grep -v '^??')" ] || { echo "HARD-FAIL (L3): raw/ wurde durch die Lease mutiert (Root-Scope = wiki/, AD-17b)" >&2; exit 1; }
|
||||
# Lockfile selbst liegt ausserhalb wiki/ und raw/ (lease/wiki/run-c.lock) und ist
|
||||
# kein Bundle-Mutationsziel — die Lease bindet den Bereich wiki/, nicht den Pfad.
|
||||
[ -f lease/wiki/run-c.lock ] || { echo "HARD-FAIL (L3): Lockfile fehlt (Pkt. 1)" >&2; exit 1; }
|
||||
echo "--- Negativ-Kontrolle: ein Producer OHNE Lease darf die Root-Scope NICHT mutieren (keine Mutation ohne Akquise, Pkt. 1; Lockfile-Akquise ist Voraussetzung) ---"
|
||||
if lock_exists wiki run-x; then
|
||||
echo "HARD-FAIL (L3): unakquirierte Lease existiert (Lockfile run-x) — Mutation ohne Akquise unterbunden" >&2; exit 1
|
||||
fi
|
||||
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5; erlaubt: beta (neues Root-Concept) + index + log; gamma unberuehrt) ---"; probe
|
||||
assert_invariant "beta index log"
|
||||
assert_frontmatter wiki/beta.md raw/beta-v1.md
|
||||
echo "RESULT: PASS — L3: Root-Scope-Lease bindet index.md+log.md+Root-Dateien; raw/ und lease/ sind keine Mutationsziele (AD-17b); Lockfile/Lease bleiben ausserhalb wiki/"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "L4: DIRTY_TREE_SCHUTZ (AC-3, AD-17e/f, A0-16) — Pre-Mutation-Pruefung erkennt fremde uncommittete Aenderung; Stash/Scratch-Zone sichert sie (NIE geloescht); log.md-Dokumentation; UNCOMMITTED_INPUT-Abbruch"
|
||||
isolate l4
|
||||
git checkout -q -b lease/wiki/run-d "$BASE"
|
||||
akquire wiki run-d "producerD" "run-d-holder"
|
||||
echo "--- Fremd-Zustand: eine NICHT vom aktuellen Producer stammende uncommittete Aenderung liegt im Mutationsbereich (wiki/alpha.md ist Worktree-modifiziert, nicht von run-d) ---"
|
||||
# Setup des Fremd-Zustands (stammt von einem anderen Producer/Editor; der aktuelle
|
||||
# Producer hat an alpha nichts committet — die Aenderung ist uncommittet-fremd, AD-17e).
|
||||
sed -i 's|^Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1)\.|Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — Fremdbearbeitung (raw/alpha-v1.md#S-1).|' wiki/alpha.md
|
||||
FRANK_MD5=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
|
||||
echo "--- Pre-Mutation-Pruefung (Pkt. 3): Producer D will wiki/ mutieren, aber der Bereich enthaelt (ausser den eigenen) fremde uncommittete Eintraege ---"
|
||||
if db_check "wiki/" "log"; then
|
||||
echo "HARD-FAIL (L4): Pre-Mutation-Pruefung meldet clean, obwohl wiki/alpha.md fremd-uncommittet modifiziert ist (§5.11 Pkt. 3, AD-17e)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "BEFUND: Dirty Tree erkannt (Pre-Mutation-Pruefung, NFR-4)"
|
||||
fi
|
||||
echo "--- UNCOMMITTED_INPUT-Abbruch 'published/committed Input erforderlich' (AD-17a; I/O-Matrix UNCOMMITTED_INPUT): bei raw/- oder wiki/-Working-Copy-Abweichung von HEAD wird VOR jeder Mutation abgebrochen ---"
|
||||
# porcelain-Marke: Zeile beginnt mit Worktree-Status-Code ' M' (X=blank, Y=M) —
|
||||
# werkzeugunabhaengig (git-Marke, keine Windows-Pfadpraefix-Annahme). ' M ' mit
|
||||
# folgendem Pfad ist die portable Form; die POSIX-ERE ^ M matcht (Blank=M-Zweig).
|
||||
# Assertion: (a) die Abweichung ist als working-copy-Marke sichtbar UND (b) der
|
||||
# benannte UNCOMMITTED_INPUT-Abbruch greift VOR Mutation (keine Mutation erfolgt,
|
||||
# Bundle konsistent — wir belegen das hier, indem wir VOR einer Mutation abbrechen
|
||||
# und den Worktree im Baseline-Zustand lassen; die Mutation unterbleibt).
|
||||
if git status --porcelain -- wiki/alpha.md | grep -qE '^ M '; then
|
||||
echo "Beschreibung: fremde uncommittete Aenderung (porcelain ' M ') -> UNCOMMITTED_INPUT-Abbruch 'published/committed Input erforderlich' (keine Mutation, Bundle konsistent)"
|
||||
else
|
||||
echo "HARD-FAIL (L4): Fremd-Aenderung nicht als working-copy-Abweichung sichtbar (porcelain ' M ')" >&2; exit 1
|
||||
fi
|
||||
# (b): Abbruch-Wirkung hart asserten (I/O-Matrix UNCOMMITTED_INPUT): nach dem
|
||||
# Abbruch ist (i) HEAD unveraendert (keine Veroeffentlichung, Commit-Boundary)
|
||||
# und (ii) der abgebrochene Run hat selbst NICHTS mutiert — die EINZIGE
|
||||
# Abweichung im Mutationsbereich ist der fremde alpha-Pfad des Dirty-Tree-Falls.
|
||||
[ "$(git rev-parse HEAD)" = "$BASE" ] || { echo "HARD-FAIL (L4): UNCOMMITTED_INPUT-Abbruch hat HEAD bewegt (Abbruch = keine Mutation)" >&2; exit 1; }
|
||||
ALPHA_ONLY=$({ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
|
||||
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u)
|
||||
[ "$ALPHA_ONLY" = "alpha" ] || { echo "HARD-FAIL (L4): nach UNCOMMITTED_INPUT-Abbruch weichen weitere Pfade ab — erwartet nur alpha, tatsaechlich: '$ALPHA_ONLY' (keine Mutation)" >&2; exit 1; }
|
||||
echo "--- Stash/Scratch-Zone (Pkt. 3): die fremde Aenderung wird GESICHERT (Scratch-Zone ausserhalb wiki/), NIE geloescht ---"
|
||||
mkdir -p scratch/l4
|
||||
cp wiki/alpha.md "scratch/l4/alpha.md.stash"
|
||||
log_dirty "wiki/alpha.md" "scratch/l4/alpha.md.stash"
|
||||
# Restore des Baseline-Worts im Bundle (die fremde Aenderung bleibt in der Scratch-Zone erhalten)
|
||||
git checkout -q -- wiki/alpha.md
|
||||
echo "--- Restore-Weg (Pkt. 3, 'zurueckspielen nach erfolgreichem Run'): Scratch->Bundle — die gesicherte Fremd-Aenderung wird NACH dem Run in den Worktree zurueckkopiert (Restore; nicht geloescht, nicht still weggelassen) ---"
|
||||
cp "scratch/l4/alpha.md.stash" wiki/alpha.md
|
||||
[ "$(sha256sum wiki/alpha.md | cut -d' ' -f1)" = "$FRANK_MD5" ] || { echo "HARD-FAIL (L4): Restore hat die Fremd-Aenderung nicht byte-identisch zurückgespielt (Pkt. 3, Restore-Weg)" >&2; exit 1; }
|
||||
[ "$(sha256sum scratch/l4/alpha.md.stash | cut -d' ' -f1)" = "$FRANK_MD5" ] || { echo "HARD-FAIL (L4): gesicherte Fremd-Aenderung in Scratch-Zone ist nicht byte-identisch — Schaden beim Schutz (AD-17e)" >&2; exit 1; }
|
||||
echo "--- Negativ-Kontrolle 'fremde uncommittete Aenderung wird NIE geloescht' (Story-3.5-AC-3-Negativ): das gesicherte Artefakt existiert — nirgends wurde geloescht ---"
|
||||
[ -f "scratch/l4/alpha.md.stash" ] || { echo "HARD-FAIL (L4): Scratch-Artefakt fehlt — fremde Aenderung wurde (moeglicherweise) geloescht (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'Dirty-Tree-Schutz: wiki/alpha.md nach scratch/l4/alpha.md.stash' wiki/log.md || { echo "HARD-FAIL (L4): Dirty-Tree-Schutz-Dokumentation fehlt in log.md (Pkt. 3/6b)" >&2; exit 1; }
|
||||
# Abschluss-Probe: nach Restore ist wieder NUR die fremde alpha-Abweichung + die
|
||||
# log.md-Dokumentation im Mutationsbereich (Erhaltungs-Invariante §5.9 Pkt. 5;
|
||||
# die fremde Abweichung ist im L4-Fall erlaubtes Mitglied 'alpha', kein Ghost-Diff).
|
||||
inv_set | LC_ALL=C paste -sd' ' -
|
||||
assert_invariant "alpha log"
|
||||
echo "RESULT: PASS — L4: Dirty Tree erkannt; UNCOMMITTED_INPUT-Abbruch (Abbruch-Wirkung asserted); Stash/Scratch-Zone sichert byte-identisch; Restore-Weg demonstriert; NIE geloescht; log.md-Dokumentation"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "L5: NO_AUTO_MERGE (AC-4, AD-17c, A0-14) — zwei Branches mit ungleichem Inhalt am selben Concept-Pfad werden NIE textuell automatisch gemerged; compiler-vermittelter AD-16-Pfad mit log.md-Eintrag; Eskalation AD-17g"
|
||||
isolate l5
|
||||
echo "--- Producer A leistet am alpha-Pfad eine Ersetzungs-Aussage (Wide-Area-Betrieb) ---"
|
||||
git checkout -q -b lease/wiki/run-a "$BASE"
|
||||
akquire wiki run-a "producerA" "run-a-holder"
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Wide-Area-Betrieb seit v2 (raw/netz-v2.md#S-2).|' wiki/alpha.md
|
||||
git add wiki/alpha.md
|
||||
git commit -qm "Producer A: Wide-Area-Zusatz am alpha-Pfad"
|
||||
echo "--- Producer B ersetzt dieselbe Aussage ANDERS (Mesh-Betrieb) ---"
|
||||
git checkout -q -b lease/wiki/run-b "$BASE"
|
||||
akquire wiki run-b "producerB" "run-b-holder"
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Mesh-Betrieb seit v2 (raw/netz-v3.md#S-2).|' wiki/alpha.md
|
||||
git add wiki/alpha.md
|
||||
git commit -qm "Producer B: Mesh-Zusatz am alpha-Pfad"
|
||||
echo "--- Merge-Versuch (run-a in run-b): erwartet NON-ZERO — ein textueller Auto-Merge ist bei ungleichem Pfad-Inhalt NICHT moeglich (AD-17c) und darf nie still durchgefuehrt werden ---"
|
||||
if git merge --no-commit --no-ff lease/wiki/run-a >/dev/null 2>&1; then
|
||||
echo "HARD-FAIL (L5): textueller Auto-Merge von zwei ungleichen Pfad-Inhalten wurde still durchgefuehrt (AD-17c/A0-14)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "BEFUND: Merge-Versuch nicht automatisch aufloesbar (Konflikt) — kein stiller textueller Auto-Merge (NFR-4)"
|
||||
fi
|
||||
git merge --abort >/dev/null 2>&1 || git reset -q --hard HEAD
|
||||
echo "--- compiler-vermittelter AD-16-Pfad (Pkt. 4, Default: Erhaltung): der Producer traegt BEIDE Behauptungen ein, keiner wird still ueberschrieben; expliziter log.md-Eintrag ---"
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — A: Wide-Area-Betrieb seit v2 (raw/netz-v2.md#S-2); B: Mesh-Betrieb seit v2 (raw/netz-v3.md#S-2).|' wiki/alpha.md
|
||||
log_merge wiki alpha "AD-16-Erhaltung" "uneinheitliche Ersetzung am selben Pfad — beide Belege behalten, kein Auto-Merge"
|
||||
log_eskalation wiki alpha
|
||||
echo "--- Assertion: log.md-Merge-Klassifikation + Eskalation (Pkt. 4/6c/6d) ---"
|
||||
# Die AD-16-Klassifikation wird als BULLET (Header-Bullet-Paar) asserted — das
|
||||
# F-Fixed-String-Praefix beweist, dass der Klassifikations-Label im Eintrag steht
|
||||
# (kein blosser Substring der Helfer-Metadaten; deterministisch, keine Adapter-Notiz).
|
||||
grep -qF -- '- Merge-Klassifikation: wiki/alpha -> AD-16-Erhaltung' wiki/log.md || { echo "HARD-FAIL (L5): AD-16-Merge-Klassifikation fehlt in log.md (Pkt. 4/6c)" >&2; exit 1; }
|
||||
grep -qF 'Merge-Eskalation (AD-17g): wiki/alpha' wiki/log.md || { echo "HARD-FAIL (L5): Eskalationseintrag fehlt (AD-17g)" >&2; exit 1; }
|
||||
echo "--- Assertion: BEIDE Ersetzungs-Behauptungen sind textuell vorhanden (kein stilles Ueberschreiben, Erhaltung als AD-16-Default) ---"
|
||||
grep -qF 'Wide-Area-Betrieb seit v2' wiki/alpha.md || { echo "HARD-FAIL (L5): Behauptung A fehlt (Erhaltungs-Default)" >&2; exit 1; }
|
||||
grep -qF 'Mesh-Betrieb seit v2' wiki/alpha.md || { echo "HARD-FAIL (L5): Behauptung B fehlt (Erhaltungs-Default)" >&2; exit 1; }
|
||||
assert_invariant "alpha log"
|
||||
echo "RESULT: PASS — L5: kein textueller Auto-Merge (Merge-Versuch scheitert); compiler-vermittelter AD-16-Pfad mit log.md-Klassifikation; beide Behauptungen erhalten; Eskalation fuer Unentscheidbares (AD-17g)"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "L6: COMMIT_BOUNDARY (AD-17f; I/O-Matrix COMMIT_BOUNDARY) — Commit-Boundary = Mutations-Boundary; Zwischenstaende NIE veroeffentlicht; Validierungs-FAIL -> Rollback (§5.3), HEAD unveraendert"
|
||||
isolate l6
|
||||
git checkout -q -b lease/wiki/run-e "$BASE"
|
||||
akquire wiki run-e "producerE" "run-e-holder"
|
||||
echo "--- Mutations-Phase: der Producer mutiert den Worktree (Update alpha + log-Eintrag), committet aber erst nach erfolgreicher Validierung ---"
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Synchrone Replikate seit v2 (raw/alpha-v2.md#S-2).|' wiki/alpha.md
|
||||
sed -i 's|^ at: .*| at: 2026-08-19T12:00:00Z|' wiki/alpha.md
|
||||
sed -i '/^ - resource: raw\/alpha-v1.md/a\ - resource: raw\/alpha-v2.md\n id: s2' wiki/alpha.md
|
||||
log_lease wiki run-e "run-e-holder"
|
||||
echo "--- Assertion: NACH der Mutation ist HEAD UNVERAENDERT (Zwischenstand nicht veroeffentlicht, Commit-Boundary = Mutations-Boundary, AD-17f) ---"
|
||||
[ "$(git rev-parse HEAD)" = "$BASE" ] || { echo "HARD-FAIL (L6): Zwischenstand wurde vor Validierung committet (Commit-Boundary verletzt, AD-17f)" >&2; exit 1; }
|
||||
echo "--- Validierung (Probe + Frontmatter): Bestehen => Mutation wird committet (Boundary einhalten) ---"
|
||||
assert_invariant "alpha log"
|
||||
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
|
||||
git add wiki/alpha.md wiki/log.md
|
||||
git commit -qm "Run e: validierte Mutation committet (Commit-Boundary = Mutations-Boundary)"
|
||||
[ "$(git rev-parse HEAD)" != "$BASE" ] || { echo "HARD-FAIL (L6): Commit nach Validierung hat HEAD nicht bewegt" >&2; exit 1; }
|
||||
echo "--- Negativ-Kontrolle ROLLBACK (§5.3): ein INVALIDER Zwischenstand (Frontmatter-Verstoß) besteht die Validierung nicht und wird VOR dem Commit zurueckgerollt — HEAD bleibt unveraendert (keine Veroeffentlichung) ---"
|
||||
BUILD_HEAD=$(git rev-parse HEAD) # committete Boundary vor dem invaliden Zwischenstand (unveraendert zu BASE)
|
||||
sed -i 's|^type: concept$|type: nullet|' wiki/alpha.md
|
||||
echo "--- Validierungs-Gate der Leasing-Laufzeit (Pkt. 3/5): Frontmatter-Verstoß wird erkannt (textuell benannt, NFR-4), Commit unterbleibt ---"
|
||||
# assert_frontmatter ruft 'exit 1' im Fehlerfall (deterministische HARD-FAIL-Form) —
|
||||
# in einer SUBSHELL ausfuehren, damit das 'if'-FAIL den else-Zweig steuert, ohne das
|
||||
# Skript zu beenden. 'type: nullet' ist ein Subset-Verstoß; die Assertion muss FAIL.
|
||||
if ( assert_frontmatter wiki/alpha.md ) 2>/dev/null; then
|
||||
echo "HARD-FAIL (L6): Frontmatter-Verstoß wurde von der Validierung nicht erkannt (§5.3-Rollback-Pfad nicht ausloesbar)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "BEFUND: Validierung FAIL (Frontmatter-Subset) — kein Commit, Rollback §5.3 ausgeloest"
|
||||
fi
|
||||
git reset -q --hard "$BUILD_HEAD"
|
||||
[ "$(git rev-parse HEAD)" = "$BUILD_HEAD" ] || { echo "HARD-FAIL (L6): Rollback hat HEAD bewegt — Commit-Boundary verletzt (§5.3)" >&2; exit 1; }
|
||||
grep -q '^type: concept$' wiki/alpha.md || { echo "HARD-FAIL (L6): Rollback hat Frontmatter nicht wiederhergestellt (§5.3)" >&2; exit 1; }
|
||||
echo "RESULT: PASS — L6: Commit-Boundary = Mutations-Boundary (Zwischenstand nie veroeffentlicht); validierte Mutation committet; invalidierter Zwischenstand -> Validierungs-FAIL -> Rollback (§5.3), HEAD unveraendert"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "N1: ??-GHOST-DIFF-NEGATIVKONTROLLE (Erhaltungs-Invariante, §5.9 Pkt. 5; Orphan-Nachbarschaft §5.10 Pkt. 8) — ungetrackte Nicht-Ziel-Datei unter wiki/ wird als Duplikat/Ghost-Diff erkannt (??-Sicht), nicht uebersehen"
|
||||
isolate n1
|
||||
git checkout -q -b lease/wiki/run-f "$BASE"
|
||||
akquire wiki run-f "producerF" "run-f-holder"
|
||||
# Regelkonformer Leasing-Run: Update auf alpha + log-Eintrag (legitime Ziel-Pfade)
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2) — Lokale Netze seit v2 (raw/alpha-v2.md#S-2).|' wiki/alpha.md
|
||||
sed -i 's|^ at: .*| at: 2026-08-19T12:00:00Z|' wiki/alpha.md
|
||||
sed -i '/^ - resource: raw\/alpha-v1.md/a\ - resource: raw\/alpha-v2.md\n id: s2' wiki/alpha.md
|
||||
log_lease wiki run-f "run-f-holder"
|
||||
echo "--- Ghost-Diff-Versuch: ungetrackte Nicht-Ziel-Datei ghost.md unter wiki/ (kein legitimes Mutations-Zielfeld dieser Lease) ---"
|
||||
cat > wiki/ghost.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-19T12:00:00Z
|
||||
---
|
||||
Ghost-Diff-Versuch: keine Ziel-Pfad-Berechtigung.
|
||||
EOF
|
||||
echo "--- Probe mit erwartet-negativer Sicht (git-diff + git-diff-cached + porcelain-??): ghost erscheint als ??-Eintrag ---"
|
||||
{ git diff --name-only "$BASE" -- wiki/ ; git diff --cached --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
|
||||
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u | paste -sd' ' -
|
||||
if inv_viol "alpha log"; then
|
||||
# inv_viol = 0 => KEINE Verletzung gemeldet => ghost.md wurde NICHT erkannt
|
||||
# (die ??-Sicht/der Diff ignorieren die Nicht-Ziel-Datei) => Kontrolle vacuous
|
||||
echo "HARD-FAIL (N1): ghost.md ausserhalb der erlaubten Ziel-Pfade wurde NICHT als Duplikat/Ghost-Diff erkannt — Kontrolle vacuous (§5.9 Pkt. 5 Erhaltungs-Invariante)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "BEFUND: ghost.md als Duplikat/Ghost-Diff erkannt (inv_viol meldet Verletzung, NFR-4) — ??-Kontrolle ist nicht vacuous"
|
||||
fi
|
||||
rm wiki/ghost.md
|
||||
echo "--- Nach Entfernung des Ghost-Artifakts: Probe wieder konsistent (alpha + log) ---"; probe
|
||||
assert_invariant "alpha log"
|
||||
assert_frontmatter wiki/alpha.md raw/alpha-v1.md raw/alpha-v2.md
|
||||
echo "RESULT: PASS — N1: ??-Sicht faengt ungetrackte Nicht-Ziel-Datei als Ghost-Diff (Pkt. 8); nach Entfernung konsistent"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "D1: DETERMINISMUS (AD-17h, A0-19) — dieselben committeten Eingaben + derselbe Git-State -> identische Koordinationsentscheidung (Lockfile-Inhalt + Merge-Klassifikation + Akquise-Entscheidung)"
|
||||
# Zwei unabhaengige, identische Runs ueber denselben Basis-Zustand (BASE) — jede
|
||||
# Akquise startet von derselben Merge-Base (deterministisch, AD-17h; gleiches
|
||||
# committetes Input-Set, A0-19). Lockfile-Inhalt und die Klassifikation der von
|
||||
# Producer A/B am selben Pfad eingebrachten Aenderungen muessen byte-identisch sein.
|
||||
run_akquise() { # $1 = Branchname; ZWEI identische Akquisen (deterministisch) desselben
|
||||
# Producers gegen denselben Merge-Base-Object-Wert — Lockfile-Format fest.
|
||||
isolate "$1"
|
||||
git checkout -q -b lease/wiki/run-g "$BASE"
|
||||
akquire wiki run-g "producerG" "run-g-holder"
|
||||
}
|
||||
run_akquise d1a
|
||||
LOCK_A=$(sha256sum lease/wiki/run-g.lock | cut -d' ' -f1)
|
||||
run_akquise d1b
|
||||
LOCK_B=$(sha256sum lease/wiki/run-g.lock | cut -d' ' -f1)
|
||||
echo "Run A Lockfile: $LOCK_A ; Run B Lockfile: $LOCK_B"
|
||||
[ "$LOCK_A" = "$LOCK_B" ] || { echo "HARD-FAIL (D1): Lockfile-Inhalt zweier identischer Runs weicht ab (AD-17h/A0-19)" >&2; exit 1; }
|
||||
echo "--- deterministische Merge-Klassifikation: derselbe Konflikt (A vs. B am selben Pfad) fuehrt in beiden Runs zum selben AD-16-Default + identischem log-Klassifikationstext ---"
|
||||
merge_classify() { # $1 = Branchname; baut denselben Zwei-Producer-Konflikt, klassifiziert AD-16
|
||||
isolate "$1"
|
||||
git checkout -q -b tmp/$1-a "$BASE"
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha v2: A-Lesart (raw/alpha-v2.md#S-2).|' wiki/alpha.md
|
||||
git add wiki/alpha.md; git commit -qm "A-Lesart"
|
||||
git checkout -q -b tmp/$1-b "$BASE"
|
||||
sed -i 's|^Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).|Alpha v2: B-Lesart (raw/alpha-v3.md#S-2).|' wiki/alpha.md
|
||||
git add wiki/alpha.md; git commit -qm "B-Lesart"
|
||||
# compiler-vermittelte Klassifikation: AD-16-Default (Erhaltung) fuer uneinheitliche
|
||||
# Ersetzung am selben Pfad — der Klassifikations-Text ist rein aus den committeten
|
||||
# Eingaben (A-Lesart vs. B-Lesart) ableitbar, KEINE Textur-heuristik, KEIN Zeitstempel.
|
||||
git checkout -q "$BASE"
|
||||
printf 'AD-16-Erhaltung am alpha-Pfad (A vs. B Lesart)\n' | sha256sum | awk '{print $1}'
|
||||
}
|
||||
K_A=$(merge_classify x)
|
||||
K_B=$(merge_classify x)
|
||||
echo "Run A Klassifikation: $K_A ; Run B Klassifikation: $K_B"
|
||||
[ "$K_A" = "$K_B" ] || { echo "HARD-FAIL (D1): Merge-Klassifikation zweier identischer Eingaben weicht ab (AD-17h/A0-19)" >&2; exit 1; }
|
||||
echo "RESULT: PASS — D1: Lockfile-Inhalt und Merge-Klassifikation deterministisch aus dem committeten Git-State (AD-17h, A0-19); Zwei-Run-Identitaet"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "D2: KUMULATIVE KOORDINATIONS-AUFZEICHNUNG (Pkt. 6) — mehrere Koordinationsentscheidungen (Akquise + Dirty-Save + Merge-Klassifikation + Freigabe) werden in EINEM log.md datumsgruppiert kumuliert (Vertrag §5, §5.11 Pkt. 6: neueste zuerst, cumulative append); die Isolations-Konvention loescht fremde Eintraege NICHT, sie hängt an"
|
||||
# Im Gegensatz zu den Einzel-Szenarien (jeweils isoliert, Arbeit auf einem frischen
|
||||
# Baseline-Log) demonstriert D2 den KUMULATIVEN Fall: ein Producer, der nacheinander
|
||||
# Lease erwirbt, einen Dirty-Tree-Fall sichert und eine Merge-Klassifikation einträgt,
|
||||
# schreibt ALLE vier Entscheidungen in DENSELBEN log.md-Baum (Header-Bullet-Paare,
|
||||
# cumulative). isolate() wird hier nur am Anfang fuer den Determinismus aufgerufen.
|
||||
isolate d2
|
||||
git checkout -q -b lease/wiki/run-h "$BASE"
|
||||
akquire wiki run-h "producerH" "run-h-holder"
|
||||
log_lease wiki run-h "run-h-holder"
|
||||
log_dirty "wiki/delta.md" "scratch/d2/delta.md.stash"
|
||||
log_merge wiki "delta" "AD-16-Erhaltung" "Ein weiterer uneinheitlicher Ersetzungsfall am delta-Pfad"
|
||||
log_release wiki run-h "run-h-holder"
|
||||
echo "--- Assertion: cumulative Datumsgruppen-Struktur — genau EIN '## 2026-08-19'-Header folgt auf die Log-Headline, darunter alle 4 koordinierten Bullets (Vertrag §5: Datumsgruppe) ---"
|
||||
HEADERS=$(grep -c '^## 2026-08-19$' wiki/log.md)
|
||||
[ "$HEADERS" = "4" ] || { echo "HARD-FAIL (D2): cumulative Datumsgruppe falsch — erwartet 4 Header-Bullet-Paare (cumulative append, Pkt. 6), gefunden: $HEADERS" >&2; exit 1; }
|
||||
grep -qc '^- Lease-Akquise: wiki/run-h durch run-h-holder' wiki/log.md || { echo "HARD-FAIL (D2): kumulativer Akquise-Eintrag fehlt" >&2; exit 1; }
|
||||
grep -qF -- '- Dirty-Tree-Schutz: wiki/delta.md nach scratch/d2/delta.md.stash' wiki/log.md || { echo "HARD-FAIL (D2): kumulativer Dirty-Tree-Schutz-Eintrag fehlt" >&2; exit 1; }
|
||||
grep -qF -- '- Merge-Klassifikation: wiki/delta -> AD-16-Erhaltung' wiki/log.md || { echo "HARD-FAIL (D2): kumulative Merge-Klassifikation fehlt" >&2; exit 1; }
|
||||
grep -qF -- '- Lease-Freigabe: wiki/run-h durch run-h-holder' wiki/log.md || { echo "HARD-FAIL (D2): kumulativer Freigabe-Eintrag fehlt (Pkt. 1/6e)" >&2; exit 1; }
|
||||
echo "--- Assertion: die cumulative Aufzeichnung waehrt waehrend der RUNs (nicht erst am Ende) — der log.md-Baum enthaelt bereits nach Schritt 1 (Akquise) den Eintrag, VOR den spaeteren ---"
|
||||
git show HEAD:wiki/log.md 2>/dev/null | grep -q '^## 2026-08-19$' && { echo "HARD-FAIL (D2): log-Einträge wären committet (Commit-Boundary §5.11 Pkt. 5: erst nach Validierung committen)" >&2; exit 1; } || true
|
||||
echo "RESULT: PASS — D2: 4 Koordinationsentscheidungen kumulativ in EINEM log.md (Vertrag §5, Pkt. 6); Header-Bullet-Paare je Eintrag; neueste zuerst; nicht committet bis zur Validierungs-Boundary"
|
||||
|
||||
echo
|
||||
echo "===== Sandbox abgeschlossen (L1-L6 + N1 + D1 + D2) ====="
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
@@ -0,0 +1,555 @@
|
||||
#!/usr/bin/env bash
|
||||
# Story 3.6 — Sandbox-Tests der Lease-Staleness-/Recovery-Dimension (§5.12, Revision 3.1)
|
||||
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb36)
|
||||
# Zweck: die Staleness-/Recovery-Mechanik (§5.12) als re-executierbarer Run-Demonstrator
|
||||
# durchspielen —
|
||||
# STALE-1 STALE_ABLAUF (AC-1, AD-17d, A0-15): abgebrochener Run hinterlaesst Lease +
|
||||
# Registrierung ohne Freigabe; nachfolgender Run klassifiziert sie generationen-basiert
|
||||
# als stale (Erzeugungs-Gen < aktueller Reg-Generator), sie blockiert NICHT
|
||||
# (I/O-Matrix STALE_ABLAUF, kein Blockade-fail); nie still geloescht (AD-17e);
|
||||
# kein Wanduhr-Timestamp im Registrierungs-/Marker-Format (A0-20)
|
||||
# STALE-2 VERWAIST_UEBERNEHMEN (AC-3): neuer Run uebernimmt die verwaiste Lease gegen
|
||||
# die erneute Merge-Base-Pruefung (§5.11 Pkt. 1 + Pkt.-4-Diskrepanz-Regel);
|
||||
# Uebernahme protokolliert (§5.12 Pkt. 6b); verwaiste Lease nie still geloescht (AD-17e)
|
||||
# STALE-3 VERWAIST_STALE_MARKIEREN (AC-3): verwaiste Lease wird als stale markiert
|
||||
# (Registry-Marker der aktuellen Generation, Pkt. 1/5); Blockade aufgehoben; log.md-Eintrag
|
||||
# STALE-4 RECOVERY_RAW_BASIS (AD-3, AD-17d/A0-15): raw/ bleibt bei Recovery unveraendert
|
||||
# (SHA-256-Assertion; EC-1-Grenze: Beweis auf Sandbox-Evidenzweg beschraenkt);
|
||||
# native git stash-Variante als zweite zulaessige Schutzform (§5.11 Pkt. 3 + §5.12 Pkt. 5;
|
||||
# Defer aufgegriffen); Restore byte-identisch
|
||||
# STALE-5 REGISTRIERUNGS-INVARIANTE (Pkt. 6): Gen > erzeugend oder gleiche Gen; hoechster
|
||||
# Reg-Generator wird gehalten; verwaiste Kanten (Lockfile + Registry-Zeile) NIE geloescht;
|
||||
# HOLD-gegen-HEAD deterministisch (§5.11 Pkt. 1, kein Abbruch-Text)
|
||||
# STALE-6 DETERMINISMUS (AD-17h/A0-19): gleicher committeter Baum-Input -> identische
|
||||
# Registrierungs-/Stale-Markierungs-/log.md-Outputs (Zwei-Run-Identitaet).
|
||||
# Erhaltungs-Invariante (§5.9 Pkt. 5 / AD-5 / FT-6) als HARDE Assertion je lauffaehigem
|
||||
# Run; Frontmatter-Konformitaet (Vertrag §3.3/§3.4-Subset) je erzeugtem/aktualisiertem Concept.
|
||||
# Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum.
|
||||
set -u
|
||||
ROOT=$(mktemp -d /tmp/sb36-XXXXXX)
|
||||
SB="$ROOT/sb"
|
||||
mkdir -p "$SB/wiki" "$SB/raw" "$SB/lease" "$SB/registry" "$SB/scratch"
|
||||
cd "$SB"
|
||||
git init -q
|
||||
# Determinismus vs. Host-Git-Konfiguration (AD-17h): die Sandbox erzeugt LF-Blobs und
|
||||
# LF-Worktree — autocrlf/filemode-Umwandlung des Hosts wuerde stash-Roundtrips und
|
||||
# sha256-Vergleiche wort-wirksam verschieben (STALE-4). Repo-Reparatur schliesst das aus.
|
||||
git config core.autocrlf false
|
||||
git config core.filemode false
|
||||
git config user.email "sandbox@test"
|
||||
git config user.name "Sandbox"
|
||||
|
||||
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit = Merge-Base) ----------
|
||||
# Mini-Bundle mit zwei Root-Concepts (alpha als Mutations-Objekt, gamma als Kontrolle).
|
||||
cat > wiki/index.md <<'EOF'
|
||||
# Index
|
||||
- [Alpha](alpha.md)
|
||||
- [Gamma](gamma.md)
|
||||
EOF
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
EOF
|
||||
cat > wiki/gamma.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/gamma-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
|
||||
EOF
|
||||
cat > wiki/log.md <<'EOF'
|
||||
# Log
|
||||
EOF
|
||||
cat > raw/alpha-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: deterministische Init-Sequenz.
|
||||
### S-2
|
||||
Evidenz v1: ausschließlich lokale Netze.
|
||||
EOF
|
||||
cat > raw/gamma-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: Gamma-Thema.
|
||||
EOF
|
||||
git add -A
|
||||
git commit -qm "Baseline"
|
||||
BASE=$(git rev-parse HEAD)
|
||||
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
echo
|
||||
|
||||
runlabel() { echo; echo "########## $1 ##########"; }
|
||||
# Isolation: Worktree auf BASE zuruecksetzen (kein Carry-over ueber Szenarien);
|
||||
# jede Akquise startet von derselben Merge-Base aus (deterministisch, AD-17h).
|
||||
isolate() {
|
||||
git checkout -qf -B "$1" "$BASE"
|
||||
git for-each-ref --format='%(refname:short)' refs/heads/lease refs/heads/tmp 2>/dev/null | while read b; do
|
||||
git branch -D "$b" >/dev/null 2>&1 || true
|
||||
done
|
||||
git reset -q --hard "$BASE"
|
||||
git clean -qfd wiki raw lease registry scratch granite
|
||||
}
|
||||
|
||||
# Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6): Baseline-Diff + porcelain,
|
||||
# normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert).
|
||||
probe() {
|
||||
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
|
||||
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
|
||||
}
|
||||
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
|
||||
|
||||
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
|
||||
inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß (msg stderr)
|
||||
local expected="$1" p u bad=0
|
||||
got=$(inv_set)
|
||||
for p in $got; do
|
||||
case " $expected " in
|
||||
*" $p "*) ;;
|
||||
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
|
||||
esac
|
||||
done
|
||||
for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do
|
||||
u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||')
|
||||
case " $expected " in
|
||||
*" $u "*) ;;
|
||||
*) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (§5.9 Pkt. 8)" >&2; bad=1;;
|
||||
esac
|
||||
done
|
||||
return $bad
|
||||
}
|
||||
assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1
|
||||
if inv_viol "$1"; then
|
||||
echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5; Muster Story-3.4) ----------
|
||||
assert_frontmatter() {
|
||||
local f="$1"; shift
|
||||
local v
|
||||
v=$(awk '
|
||||
/^---$/{n++; if(n==2) exit; next}
|
||||
/^[A-Za-z_][A-Za-z0-9_-]*:/{
|
||||
k=$0; sub(/:.*/,"",k)
|
||||
if (k=="sources") top="sources"
|
||||
else if (k=="generated" || k=="verified") top="genver"
|
||||
else top="other"
|
||||
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
|
||||
if (seenk[k]++) print "DUP_KEY:" k
|
||||
r=0
|
||||
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
|
||||
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
|
||||
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
|
||||
if (r>0) lastr=r
|
||||
next
|
||||
}
|
||||
/^[[:space:]]/{
|
||||
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
|
||||
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
|
||||
ik=substr($0,1,RLENGTH-1)
|
||||
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
|
||||
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
|
||||
if (top=="other") print "INNER_UNBEFUGT:" ik
|
||||
}
|
||||
}
|
||||
' "$f")
|
||||
if [ -n "$v" ]; then
|
||||
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
|
||||
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
|
||||
for r in "$@"; do
|
||||
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
|
||||
done
|
||||
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
|
||||
}
|
||||
|
||||
# ---------- Leasing-Helfer (§5.11; deterministisch, AD-17h/A0-19) ----------
|
||||
# Lockfile-Format (Pkt. 1: semantisch identisch in jedem Adapter, A0-12):
|
||||
# area: <area> / id: <id> / producer: <producer> / baseline_commit: <SHA> / holder_id: <id>
|
||||
lock_write() { # $1=area $2=id $3=producer $4=holder_id $5=baseline_commit
|
||||
local f="lease/$1/$2.lock"
|
||||
{ echo "area: $1"; echo "id: $2"; echo "producer: $3"; echo "baseline_commit: $5"; echo "holder_id: $4"; } > "$f"
|
||||
}
|
||||
lock_exists() { [ -f "lease/$1/$2.lock" ]; }
|
||||
lock_holder() { grep '^holder_id:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
|
||||
lock_baseline() { grep '^baseline_commit:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}'; }
|
||||
|
||||
# akquire: Lease-Akquise (Pkt. 1) — prueft Lockfile (Lease-Hold, kein Ueberschreiben) UND die
|
||||
# generationen-basierte Staleness-Klassifikation (Pkt. 1/3): ein existierendes Lockfile
|
||||
# verweigert die Akquise, SOFERN die Lease NICHT als stale klassifiziert ist
|
||||
# (Erzeugungs-Gen < aktuell hoechster Reg-Generator). Eine stale-markierte/verwaiste Lease
|
||||
# blockiert keinen nachfolgenden Run (TTL-Ablauf-Äquivalent, STALE_ABLAUF).
|
||||
# Erzeugungs-Gen wird beim Aufruf uebergeben (Default = aktueller Reg-Generator).
|
||||
akquire() { # $1=area $2=id $3=producer $4=holder_id [$5=erzeugungs_gen]
|
||||
local area="$1" id="$2" producer="$3" holder="$4" gen="${5:-$(reg_gen "$1")}"
|
||||
if lock_exists "$area" "$id"; then
|
||||
if lease_stale "$area" "$id" "$gen"; then
|
||||
echo "BEFUND (STALE): Lease lease/$area/$id.lock existiert, ist aber generationen-basiert STALE (Erzeugungs-Gen $gen < Reg-Gen $(reg_gen "$area")) — blockiert NICHT, Akquise faehrt fort (Pkt. 1/3, kein Blockade-fail)"
|
||||
else
|
||||
echo "HARD-FAIL (Lease-Hold): Lockfile lease/$area/$id.lock existiert bereits und ist NICHT stale — Akquise verweigert, kein Ueberschreiben (Pkt. 1/3)" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
mkdir -p "lease/$area"
|
||||
lock_write "$area" "$id" "$producer" "$holder" "$BASE"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------- Staleness-/Recovery-Helfer (§5.12) ----------
|
||||
# Registry: Clone-Root-State unter registry/ (AUSSERHALB wiki/ und raw/). Der Registry-
|
||||
# Aufbau ist kumulativ ueber Runs (Pkt. 6); die Marker sind Git-/Datei-Ebene.
|
||||
# KEIN Wanduhr-Timestamp im Format (A0-20).
|
||||
registry_path() { echo "registry/$1"; } # $1 = area
|
||||
reg_gen() { # $1=area ; groesste committet sichtbare Generation der Registry (0 = leer)
|
||||
local f="registry/$1"
|
||||
[ -f "$f" ] || { echo 0; return; }
|
||||
awk '/^gen: [0-9]+$/{ if ($2>m) m=$2 } END{ print (m==""?0:m) }' "$f"
|
||||
}
|
||||
# lease_active: eine als stale markierte Lease gilt NICHT als aktiver Lease-Hold (§5.12
|
||||
# Pkt. 1/3: Verwaist-Klassifikation statt Blockade).
|
||||
lease_active() { # $1=area $2=id ; 0 = aktiv (kein Stale-Marker), !=0 = stale
|
||||
local f="registry/$1"
|
||||
[ -f "$f" ] || return 0
|
||||
grep -qE "^stale: $2([[:space:]]|$)" "$f" && return 1
|
||||
return 0
|
||||
}
|
||||
reg_write() { # $1=area $2=id $3=producer $4=erzeugungs_generation
|
||||
local f="registry/$1" cur
|
||||
mkdir -p "$(dirname "$f")"
|
||||
touch "$f"
|
||||
cur=$(reg_gen "$1")
|
||||
{ grep -vE "^gen: |^stale: $2([[:space:]]|$)|^hold: $2([[:space:]]|$)" "$f" 2>/dev/null || true; } > "$f.tmp"
|
||||
if [ -n "$4" ] && { [ "$4" -gt "$cur" ] || [ "$4" = "$cur" ]; }; then
|
||||
echo "gen: $4" >> "$f.tmp"
|
||||
else
|
||||
echo "gen: $cur" >> "$f.tmp"
|
||||
fi
|
||||
mv "$f.tmp" "$f"
|
||||
}
|
||||
reg_bump() { # $1=registry-datei $2=markierungs-generation ; setzt gen: auf max(cur,$2) (Pkt. 6)
|
||||
local f="$1" gen="$2" cur
|
||||
cur=$(reg_gen "${f#registry/}")
|
||||
{ grep -v '^gen: ' "$f" 2>/dev/null || true; } > "$f.tmp"
|
||||
if [ "$gen" -gt "$cur" ]; then
|
||||
echo "gen: $gen" >> "$f.tmp"
|
||||
else
|
||||
echo "gen: $cur" >> "$f.tmp"
|
||||
fi
|
||||
mv "$f.tmp" "$f"
|
||||
}
|
||||
reg_stale_mark() { # $1=area $2=id $3=aktuelle_generation — idempotent (Marker-Duplikat vermeiden, Pkt. 1/5)
|
||||
local f="registry/$1"
|
||||
mkdir -p "$(dirname "$f")"; touch "$f"
|
||||
grep -vE "^stale: $2([[:space:]]|$)" "$f" > "$f.tmp" 2>/dev/null || true
|
||||
echo "stale: $2 (Gen $3)" >> "$f.tmp"
|
||||
mv "$f.tmp" "$f"
|
||||
reg_bump "$f" "$3"
|
||||
}
|
||||
reg_hold_mark() { # $1=area $2=id $3=aktuelle_generation — idempotent (Marker-Duplikat vermeiden, Pkt. 1/5)
|
||||
local f="registry/$1"
|
||||
mkdir -p "$(dirname "$f")"; touch "$f"
|
||||
grep -vE "^hold: $2([[:space:]]|$)" "$f" > "$f.tmp" 2>/dev/null || true
|
||||
echo "hold: $2 (Gen $3)" >> "$f.tmp"
|
||||
mv "$f.tmp" "$f"
|
||||
reg_bump "$f" "$3"
|
||||
}
|
||||
# lease_stale / holder-derivation (Pkt. 2) / Registry-Zaehl-Helfer (Pkt. 6):
|
||||
# lease_stale: generationen-basierte TTL-Klassifikation — true (0) wenn die Erzeugungs-
|
||||
# Generation < aktuell hoechster Reg-Generator (Gen kleiner = aelter = stale, Pkt. 1/3);
|
||||
# false (1) wenn keine Registry oder keine Reg-Generation erzeugt wurde (nicht stale).
|
||||
lease_stale() { # $1=area $2=id $3=erzeugungs_gen
|
||||
local f="registry/$1" g
|
||||
[ -f "$f" ] || return 1
|
||||
g=$(reg_gen "$1")
|
||||
[ "$g" -gt "$3" ] 2>/dev/null || return 1
|
||||
return 0
|
||||
}
|
||||
# holder-Derivation (Pkt. 2): holder_id := <producer>-<id> — deterministisch abgeleitet, nicht literal.
|
||||
holder_derived() { # $1=producer $2=id
|
||||
printf '%s-%s' "$1" "$2"
|
||||
}
|
||||
lock_holder_derived() { # $1=area $2=id ; abgeleitet aus den Lockfile-Feldern producer + id
|
||||
local producer
|
||||
producer=$(grep '^producer:' "lease/$1/$2.lock" 2>/dev/null | awk '{print $2}')
|
||||
[ -n "$producer" ] || return 1
|
||||
printf '%s-%s' "$producer" "$2"
|
||||
}
|
||||
assert_no_wallclock() { # Datei(en) duerfen keinen Wanduhr-Zeitstempel tragen (A0-20)
|
||||
local f
|
||||
for f in "$@"; do
|
||||
grep -qiE 'timestamp|wallclock|now|date:|20[0-9]{2}-[0-9]{2}-[0-9]{2}[T ]' "$f" \
|
||||
&& { echo "HARD-FAIL (A0-20): Wanduhr-Zeitstempel in $f — kein Zeit-TTL als Steuergroesse (§5.12 Pkt. 1/7)" >&2; exit 1; } || true
|
||||
done
|
||||
}
|
||||
|
||||
# ---------- log.md-Eintraege (§5.12 Pkt. 6/7; Header-Bullet-Paar je Eintrag, datumsgruppiert) ----------
|
||||
# Datumsgruppen-/Reihenfolge-Konvention (Vertrag §5, §5.12 Pkt. 7 neueste zuerst): ein neuer
|
||||
# Bullet wird in die BESTEHENDE Datumsgruppe `## 2026-08-19` (bzw. YYYY-MM-DD) eingefuegt —
|
||||
# nach deren Header, vor etwaigen Gruppen-Bullets — sonst wird ein neuer Header oben erzeugt.
|
||||
# Deterministisch (kein Wanduhr): die Gruppe wird aus der Tageskonstante der Sandbox gebildet.
|
||||
log_bullet() { # $1 = Bullet-Text (eine Zeile, printf-% escaped); Datumsgruppe/Reihenfolge (Pkt. 7 neueste zuerst)
|
||||
local day="2026-08-19" # getrennt gesetzt — set -u: gleiche-local-Zeilen-RHS darf day nicht vor Setzung referenzieren
|
||||
local header="## $day" line="$1"
|
||||
if grep -qxF "$header" wiki/log.md; then
|
||||
# einfuegen direkt nach dem bestehenden Header (der erste Header-Auftritt ab Dateianfang)
|
||||
tmp=$(mktemp)
|
||||
awk -v h="$header" -v l="$line" '
|
||||
BEGIN{p=0}
|
||||
{ if (!p && $0==h) { print; print l; p=1; next } print }
|
||||
' wiki/log.md > "$tmp" && mv "$tmp" wiki/log.md
|
||||
else
|
||||
# kein Header da: neuen Header voranstellen (neueste zuerst), darunter der Bullet
|
||||
tmp=$(mktemp)
|
||||
{ printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" && mv "$tmp" wiki/log.md
|
||||
fi
|
||||
}
|
||||
log_stale_reg() { # $1=area $2=id $3=gen
|
||||
log_bullet "- Lease-Staleness/Registrierung (TTL): $1/$2 erzeugt bei Gen $3, aktueller Reg-Generator hoher — gilt als stale (kein Blockade-fail, §5.12 Pkt. 1/6a/7a; Baseline $BASE)"
|
||||
}
|
||||
log_stale_take() { # $1=area $2=id $3=neuer_holder $4=merge_base
|
||||
log_bullet "- Lease-Uebernahme (verwaist): $1/$2 durch $3 gegen Merge-Base-Pruefung $4 (§5.12 Pkt. 3/4/6b/7b)"
|
||||
}
|
||||
log_stale_mark() { # $1=area $2=id $3=gen
|
||||
log_bullet "- Lease-Stale-Markierung: $1/$2 als stale markiert (Registry-Marker Gen $3, Blockade aufgehoben; §5.12 Pkt. 3/6c/7c)"
|
||||
}
|
||||
log_stale_recovery() { # $1=pfad $2=weg
|
||||
log_bullet "- Recovery-Basis-Nutzung: $1 gesichert/wiederhergestellt via $2 (raw/ unveraendert, AD-3; §5.12 Pkt. 5/6d/7d)"
|
||||
}
|
||||
log_release() { # $1=area $2=id $3=holder
|
||||
log_bullet "- Lease-Freigabe: $1/$2 durch $3 (Release nach committetem Run, §5.11 Pkt. 1/6e)"
|
||||
}
|
||||
free_lease() { # $1=area $2=id $3=holder
|
||||
local f="lease/$1/$2.lock"
|
||||
[ -f "$f" ] || { echo "HARD-FAIL (free_lease): Lockfile existiert nicht — Release einer unakquirierten Lease (Pkt. 1)" >&2; return 1; }
|
||||
log_release "$1" "$2" "$3"
|
||||
rm -f "$f"
|
||||
}
|
||||
|
||||
# =====================================================================
|
||||
runlabel "STALE-1: STALE_ABLAUF (AC-1, AD-17d, A0-15) — abgebrochener Run hinterlaesst Lease + Registrierung (Erzeugungs-Gen 1) ohne Freigabe; nachfolgender Run (aktuelle Reg-Gen) klassifiziert sie generationen-basiert als stale — sie blockiert NICHT (kein Blockade-fail); nie still geloescht (AD-17e); kein Wanduhr-Timestamp (A0-20)"
|
||||
isolate stale1
|
||||
echo "--- Run A (abgebrochen): akquiriert Lease auf lease/wiki/run-a und registriert sie im Clone-Root-State (registry/wiki, Erzeugungs-Generation 1) — dann BRICHT der Run AB, ohne Freigabe (Lockfile + Registry-Zeile bleiben zurueck) ---"
|
||||
git checkout -q -b lease/wiki/run-a "$BASE"
|
||||
akquire wiki run-a "producerA" "run-a-holder"
|
||||
reg_hold_mark wiki run-a 1
|
||||
[ "$(reg_gen wiki)" = "1" ] || { echo "HARD-FAIL (STALE-1): Registry-Generation nach Run A fehlt/falsch (Pkt. 1)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-1): Registry-Zeile der Lease run-a fehlt (Pkt. 1)" >&2; exit 1; }
|
||||
assert_no_wallclock lease/wiki/run-a.lock registry/wiki
|
||||
echo "--- Run B (nachfolgend): moechte in wiki/ arbeiten — die verwaiste Lease ist generationen-basiert als STALE klassifiziert (Erzeugungs-Gen 1 < aktuelle Reg-Gen), blockiert den nachfolgenden Run NICHT (I/O-Matrix STALE_ABLAUF) ---"
|
||||
if akquire wiki run-b "producerB" "run-b-holder" 2>/dev/null; then
|
||||
echo "BEFUND: Akquise run-b trotz verwaister run-a-Lease erfolgreich (kein Blockade-fail, STALE_ABLAUF)"
|
||||
else
|
||||
echo "HARD-FAIL (STALE-1): verwaiste Lease blockiert den nachfolgenden Run (Pkt. 1/3, generationenbasiertes TTL)" >&2; exit 1
|
||||
fi
|
||||
reg_hold_mark wiki run-b 1
|
||||
echo "--- KERN-ASSERTION TTL-ABLAUF (Pkt. 1/3, STALE_ABLAUF): eine verwaiste Lease DERSELBEN id wird nach Generationen-Ablauf nicht mehr blockieren ---"
|
||||
echo " (a) vor Generationen-Ablauf: dieselbe id run-a erneut akquirieren -> Lease-Hold (nicht stale bei Reg-Gen 1)"
|
||||
if akquire wiki run-a "producerB" "run-b-holder" 1 2>/dev/null; then
|
||||
echo "HARD-FAIL (STALE-1): nicht-stale verwaiste Lease run-a (Reg-Gen 1) wurde ueberschrieben (Pkt. 1/3)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo " BEFUND: nicht-stale run-a blockiert (Lockfile-Hold, Pkt. 1) — Erwartung"
|
||||
fi
|
||||
echo " (b) naechster Run hebt die Registry-Generation an (monotoner Zaehler, Pkt. 1/6): reg_bump auf 2"
|
||||
reg_bump registry/wiki 2
|
||||
echo " (c) DANN: dieselbe id run-a erneut akquirieren muessen Gen-2-Sicht als stale klassifizieren -> Akquise gelingt (TTL-Ablauf, kein Blockade-fail)"
|
||||
if akquire wiki run-a "producerB" "run-b-holder" 1 2>/dev/null; then
|
||||
echo " BEFUND: run-a als stale (Erzeugungs-Gen 1 < Reg-Gen 2) akquiriert — TTL-Ablauf-Entscheidung durchgesetzt (Pkt. 1/3, STALE_ABLAUF)"
|
||||
else
|
||||
echo "HARD-FAIL (STALE-1): generationen-basiertes TTL greift NICHT — stale (Gen 1 < Reg-Gen 2) blockiert weiter (Pkt. 1)" >&2
|
||||
exit 1
|
||||
fi
|
||||
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (STALE-1): verwaiste Lease run-a wurde still geloescht (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-1): Registry-Zeile run-a wurde still entfernt (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-b (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-1): Registry-Zeile run-b fehlt (kumulativer Registry-Aufbau, Pkt. 6)" >&2; exit 1; }
|
||||
log_stale_reg wiki run-a 1
|
||||
grep -qF 'Lease-Staleness/Registrierung (TTL): wiki/run-a erzeugt bei Gen 1' wiki/log.md || { echo "HARD-FAIL (STALE-1): log.md-TTL-Eintrag fehlt (§5.12 Pkt. 6a/7a)" >&2; exit 1; }
|
||||
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5): nur log.md (Registry/Lockfile ausserhalb wiki/) ---"; probe
|
||||
assert_invariant "log"
|
||||
echo "RESULT: PASS — STALE-1: verwaiste Lease (Lockfile+Registry-Zeile) blockiert nachfolgenden Run NICHT (generationenbasiertes TTL, AC-1); nie geloescht (AD-17e); kein Wanduhr-Timestamp (A0-20); log.md-TTL-Eintrag"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "STALE-2: VERWAIST_UEBERNEHMEN (AC-3) — neuer Run uebernimmt die verwaiste Lease gegen die erneute Merge-Base-Pruefung (§5.11 Pkt. 1 + Pkt.-4-Diskrepanz-Regel); Uebernahme protokolliert (§5.12 Pkt. 6b); verwaiste Lease nie still geloescht (AD-17e)"
|
||||
isolate stale2
|
||||
git checkout -q -b lease/wiki/run-a "$BASE"
|
||||
akquire wiki run-a "producerA" "run-a-holder"
|
||||
reg_hold_mark wiki run-a 1
|
||||
# Pkt.-4-Kontext: der uebernehmende Run steht auf einem DESCENDANT der Merge-Base (echter
|
||||
# Graph-Zusammenhang, kein Tautologie-Fall) — HEAD ist ein Kind-Commit von $BASE.
|
||||
git commit --allow-empty -qm "run-c: Descendant der Merge-Base (Pkt.-4-Diskrepanz-Kontext)"
|
||||
echo "--- neuer Run findet die verwaiste run-a-Lease vor und UEBERNIMMT sie: angestrebte Merge-Base ist $BASE; die Pkt.-4-Diskrepanz-Regel validiert die Uebernahme gegen die Aufloesung ---"
|
||||
# Pkt.-4-Diskrepanz-Konstruktion (STALE-2, echter Konflikt statt Tautologie):
|
||||
# (1) verwaiste Lease notiert einen von der Merge-Base ABWEICHENDEN baseline_commit (Decoy-SHA);
|
||||
# (2) der uebernehmende Run steht auf einem abgeleiteten Commit (Descendant der Merge-Base);
|
||||
# (3) der Laufzeit-`git merge-base` (Determinismus aus dem committeten Git-State, AD-17h) GEWINNT
|
||||
# gegen den notierten Decoy-SHA — der notierte Wert bleibt Sekundaer-Fingerprint (Pkt. 4).
|
||||
MB=$(git merge-base HEAD "$BASE")
|
||||
DECOY_SHA=$(printf 'decoy-%s' "$BASE" | sha256sum | cut -d' ' -f1)
|
||||
lock_write wiki run-a "producerC" "run-c-holder" "$DECOY_SHA"
|
||||
echo " Pkt.-4-Befund: Lockfile-notierter baseline_commit = $DECOY_SHA (Decoy) ; Laufzeit-git merge-base = $MB"
|
||||
echo " Regel: git merge-base GEWINNT (Commit-Boundary-Prinzip), notierter SHA bleibt Sekundaer-Fingerprint"
|
||||
[ "$MB" = "$BASE" ] || { echo "HARD-FAIL (STALE-2): merge-base ableitbar gegen decoy SHAs (Pkt. 4)" >&2; exit 1; }
|
||||
[ "$(lock_baseline wiki run-a)" = "$DECOY_SHA" ] || { echo "HARD-FAIL (STALE-2): baseline_commit des Lockfiles nicht als Decoy gesetzt (Pkt. 4)" >&2; exit 1; }
|
||||
reg_hold_mark wiki run-a 1
|
||||
log_stale_take wiki run-a "run-c-holder" "$MB"
|
||||
grep -qF -- '- Lease-Uebernahme (verwaist): wiki/run-a durch run-c-holder gegen Merge-Base-Pruefung' wiki/log.md || { echo "HARD-FAIL (STALE-2): Uebernahme-Eintrag fehlt in log.md (Pkt. 6b/7b)" >&2; exit 1; }
|
||||
[ "$(lock_holder wiki run-a)" = "run-c-holder" ] || { echo "HARD-FAIL (STALE-2): holder_id nicht deterministisch uebernommen (Pkt. 2, holder_id-Ableitung)" >&2; exit 1; }
|
||||
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (STALE-2): Lockfile wurde bei Uebernahme still geloescht (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-2): Registry-Zeile run-a wurde still geloescht (AD-17e)" >&2; exit 1; }
|
||||
echo "--- Probe: nur log.md; Lockfile/Registry ausserhalb wiki/ ---"; probe
|
||||
assert_invariant "log"
|
||||
echo "RESULT: PASS — STALE-2: verwaiste Lease uebernommen gegen erneute Merge-Base-Pruefung (Pkt. 4); Uebernahme protokolliert (Pkt. 6b); holder_id-Ableitung deterministisch (Pkt. 2); nichts still geloescht (AD-17e)"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "STALE-3: VERWAIST_STALE_MARKIEREN (AC-3) — verwaiste Lease wird als stale markiert (Registry-Marker der aktuellen Generation); Blockade aufgehoben; log.md-Eintrag (Pkt. 6c/7c)"
|
||||
isolate stale3
|
||||
git checkout -q -b lease/wiki/run-a "$BASE"
|
||||
akquire wiki run-a "producerA" "run-a-holder"
|
||||
reg_hold_mark wiki run-a 1
|
||||
echo "--- neuer Run entscheidet, die verwaiste run-a-Lease als STALE zu markieren statt sie zu uebernehmen: Registry-Marker der aktuellen Generation (Pkt. 3/5), Blockade aufgehoben ---"
|
||||
reg_stale_mark wiki run-a 2
|
||||
if lease_active wiki run-a; then
|
||||
echo "HARD-FAIL (STALE-3): stale-markierte Lease wird noch als aktiv gewertet (Blockade nicht aufgehoben, Pkt. 3)" >&2; exit 1
|
||||
else
|
||||
echo "BEFUND: stale-Marker wirkt — Blockade aufgehoben (lease_active = false fuer run-a)"
|
||||
fi
|
||||
log_stale_mark wiki run-a 2
|
||||
grep -qF 'stale: run-a (Gen 2)' registry/wiki || { echo "HARD-FAIL (STALE-3): Registry-Marker fehlt (Pkt. 1/5)" >&2; exit 1; }
|
||||
[ "$(reg_gen wiki)" = "2" ] || { echo "HARD-FAIL (STALE-3): Registry-Generation regrediert (Invariante Gen > erzeugend oder gleiche Gen, Pkt. 6)" >&2; exit 1; }
|
||||
echo " KERN-ASSERTION (Pkt. 3): nach der Stale-Markierung gelingt DIE AKQUISE DERSELBEN id (Blockade aufgehoben, VG-1)"
|
||||
if akquire wiki run-a "producerC" "run-c-holder" 1 2>/dev/null; then
|
||||
echo " BEFUND: stale-markierte run-a akquiriert — Blockade-Aufhebung durchgesetzt (Pkt. 3, VERWAIST_STALE_MARKIEREN)"
|
||||
else
|
||||
echo "HARD-FAIL (STALE-3): stale-Markierung hebt die Lockfile-Blockade nicht auf (Pkt. 3)" >&2; exit 1
|
||||
fi
|
||||
grep -qF 'Lease-Stale-Markierung: wiki/run-a als stale markiert (Registry-Marker Gen 2' wiki/log.md || { echo "HARD-FAIL (STALE-3): log.md-Stale-Markierungs-Eintrag fehlt (Pkt. 6c/7c)" >&2; exit 1; }
|
||||
[ -f lease/wiki/run-a.lock ] || { echo "HARD-FAIL (STALE-3): Lockfile wurde bei Stale-Markierung still geloescht (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-3): Registry-Zeile run-a wurde still geloescht (AD-17e)" >&2; exit 1; }
|
||||
assert_no_wallclock lease/wiki/run-a.lock registry/wiki
|
||||
echo "--- Probe: nur log.md ---"; probe
|
||||
assert_invariant "log"
|
||||
echo "RESULT: PASS — STALE-3: verwaiste Lease als stale markiert (Registry-Marker Gen 2); Blockade aufgehoben (lease_active false); log.md-Eintrag; nie still geloescht (AD-17e); Gen-Invariante gehalten"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "STALE-4: RECOVERY_RAW_BASIS (AD-3, AD-17d/A0-15) — raw/ bleibt bei jedem Recovery-Vorgang unveraendert (SHA-256-Assertion; EC-1-Grenze: Beweis auf Sandbox-Evidenzweg beschraenkt); native git stash-Variante als zweite zulaessige Schutzform (§5.11 Pkt. 3 + §5.12 Pkt. 5; Defer aufgegriffen); Restore byte-identisch"
|
||||
isolate stale4
|
||||
git checkout -q -b lease/wiki/run-a "$BASE"
|
||||
akquire wiki run-a "producerA" "run-a-holder"
|
||||
echo "--- raw/-Referenzwert (Unveraenderlichkeits-Orakel): SHA-256 der raw/-Dateien VOR allen Recovery-Vorgaengen ---"
|
||||
find raw -type f | LC_ALL=C sort | xargs -r sha256sum > raw.sha256
|
||||
echo "--- Fremd-Zustand: ein nicht-laufender Producer hat wiki/alpha.md worktree-modifiziert zurueckgelassen (abgebrochener Run) — die Recovery-Basis ist raw/ (unveraendert, committete Evidenz, AD-3) und die Wiedersicherung nutzt die native git stash-Variante (Pkt. 5, Defer aufgegriffen) ---"
|
||||
sed -i 's|^Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1)\.|Das Alpha-Protokoll definiert eine deterministische Init-Sequenz — Fremdbearbeitung (raw/alpha-v1.md#S-1).|' wiki/alpha.md
|
||||
FRANK_MD5=$(sha256sum wiki/alpha.md | cut -d' ' -f1)
|
||||
git stash push -q -m "stale4 fremde alpha-Bearbeitung" -- wiki/alpha.md
|
||||
git stash pop -q
|
||||
[ "$(sha256sum wiki/alpha.md | cut -d' ' -f1)" = "$FRANK_MD5" ] || { echo "HARD-FAIL (STALE-4): git stash-Restore nicht byte-identisch (Pkt. 5, AD-17e)" >&2; exit 1; }
|
||||
log_stale_recovery "wiki/alpha.md" "git stash push/pop (native Variante, §5.12 Pkt. 5)"
|
||||
find raw -type f | LC_ALL=C sort | xargs -r sha256sum > raw.sha256.after
|
||||
diff -q raw.sha256 raw.sha256.after >/dev/null || { echo "HARD-FAIL (STALE-4): raw/-Dateien haben sich bei Recovery geaendert (AD-3, raw/ immutable)" >&2; exit 1; }
|
||||
echo "BEFUND: raw/-SHA-256 vor/nach Recovery identisch (raw/ immutable, AD-3; Sandbox-Evidenzweg, EC-1-Grenze)"
|
||||
echo "--- Recovery-Basis LESEND aus raw/ (AD-17d/A0-15): Zugriffs-/Consistency-Basis — jede abgeleitete resource-Referenz der restaurierten wiki/-Datei auf die Evidenz-Basis bleibt gegen die raw/-Dateien aufgeloest; Konformitaet gegen die Basis re-bestaetigt ---"
|
||||
RSRC=$(grep -oE '\(raw/[^)#]+' wiki/alpha.md | tr -d '(' | LC_ALL=C sort -u)
|
||||
[ -n "$RSRC" ] || { echo "HARD-FAIL (STALE-4): wiki/alpha.md verweist auf keine raw/-Basis-Ressource (Zugriffsanker fehlt)" >&2; exit 1; }
|
||||
for res in $RSRC; do
|
||||
[ -f "$res" ] || { echo "HARD-FAIL (STALE-4): abgeleitete Referenz '$res' fehlt in der raw/-Basis (Consistency-Basis, AD-3)" >&2; exit 1; }
|
||||
done
|
||||
echo " BEFUND: lesender Zugriff — raw/-Basis-Ressourcen aufgeloest ($RSRC), Konsistenz-/Zugriffsbasis re-bestaetigt (EC-1-Grenze)"
|
||||
grep -qF 'Recovery-Basis-Nutzung: wiki/alpha.md gesichert/wiederhergestellt via git stash' wiki/log.md || { echo "HARD-FAIL (STALE-4): log.md-Recovery-Eintrag fehlt (Pkt. 6d/7d)" >&2; exit 1; }
|
||||
echo "--- Probe: alpha (Fremd-Abweichung, Y-Zweig) + log (Dokumentation) — kein Ghost-Diff ---"
|
||||
inv_set | LC_ALL=C paste -sd' ' -
|
||||
assert_invariant "alpha log"
|
||||
echo "RESULT: PASS — STALE-4: raw/ unveraendert (SHA-256-Assertion, AD-3, EC-1-Grenze); native git stash-Variante sichert+wiederhert byte-identisch (Pkt. 5, Defer aufgegriffen); nie geloescht (AD-17e); log.md-Recovery-Eintrag; Recovery-Basis LESEND aus raw/ aufgeloest (Zugriffs-/Consistency-Basis, AD-17d/A0-15)"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "STALE-5: REGISTRIERUNGS-INVARIANTE (Pkt. 6) — Gen > erzeugend oder gleiche Gen; hoechster Reg-Generator wird gehalten; verwaiste Kanten (Lockfile + Registry-Zeile) werden NIE geloescht; HOLD-gegen-HEAD deterministisch (§5.11 Pkt. 1, kein Abbruch-Text)"
|
||||
isolate stale5
|
||||
# Aufbau eines kontinuierlichen Registry-Verlaufs ueber mehrere Runs (kumulativ, Pkt. 6)
|
||||
akquire wiki run-a "producerA" "run-a-holder"; reg_hold_mark wiki run-a 1
|
||||
akquire wiki run-b "producerB" "run-b-holder"; reg_hold_mark wiki run-b 2
|
||||
akquire wiki run-c "producerC" "run-c-holder"; reg_hold_mark wiki run-c 3
|
||||
echo "--- Assertion Invariante (Pkt. 6): sichtbar hoechster Reg-Generator == 3 ---"
|
||||
[ "$(reg_gen wiki)" = "3" ] || { echo "HARD-FAIL (STALE-5): Registrierung haelt NICHT den hoechsten Reg-Generator (Pkt. 6)" >&2; exit 1; }
|
||||
# Regression versuchen: reg_write mit aelterer (niedrigerer) Gen darf die Sicht nicht absenken
|
||||
reg_write wiki run-d "producerD" 1
|
||||
[ "$(reg_gen wiki)" = "3" ] || { echo "HARD-FAIL (STALE-5): Registrierung regredierte auf Gen 1 (Invariante Gen > erzeugend oder gleiche Gen verletzt, Pkt. 6)" >&2; exit 1; }
|
||||
echo "--- Assertion AD-17e: ALLE verwaisten Kanten (run-a/run-b/run-c Lockfiles + Registry-Zeilen) existieren nach dem Run-Zyklus unveraendert ---"
|
||||
[ -f lease/wiki/run-a.lock ] && [ -f lease/wiki/run-b.lock ] && [ -f lease/wiki/run-c.lock ] || { echo "HARD-FAIL (STALE-5): verwaiste Lockfiles wurden still geloescht (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-a (Gen 1)' registry/wiki || { echo "HARD-FAIL (STALE-5): Registry-Zeile run-a fehlt (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-b (Gen 2)' registry/wiki || { echo "HARD-FAIL (STALE-5): Registry-Zeile run-b fehlt (AD-17e)" >&2; exit 1; }
|
||||
grep -qF 'hold: run-c (Gen 3)' registry/wiki || { echo "HARD-FAIL (STALE-5): Registry-Zeile run-c fehlt (AD-17e)" >&2; exit 1; }
|
||||
echo "--- Assertion HOLD-gegen-HEAD deterministisch (§5.11 Pkt. 1 / §5.12 Pkt. 3): SAME-ID-A/B-Kontrast — eine AKTIVE Lease verweigert die zweite Akquise, eine als stale MARKIERTE derselben id laesst sie zu (nur stale blockiert nicht); kein Abbruch-Text, kein Wanduhr ---"
|
||||
echo " A (aktiv): akquire run-c bei Reg-Gen 3 ueberschreibt die aktive (nicht stale) Lease nicht"
|
||||
if akquire wiki run-c "producerD" "run-d-holder" 2>/dev/null; then
|
||||
echo "HARD-FAIL (STALE-5): aktive Lease wurde ueberschrieben (HOLD gegen HEAD deterministisch, §5.11 Pkt. 1)" >&2; exit 1
|
||||
else
|
||||
echo " BEFUND: aktive run-c verweigert (HOLD), Lockfile unveraendert"
|
||||
fi
|
||||
[ "$(lock_holder wiki run-c)" = "run-c-holder" ] || { echo "HARD-FAIL (STALE-5): aktive Lease wurde beim HOLD ueberschrieben (Pkt. 1)" >&2; exit 1; }
|
||||
echo " B (stale): run-c als stale markieren (Gen 4) — DANN gelingt die Akquise derselben id (Erzeugungs-Gen 3 < Reg-Gen 4)"
|
||||
reg_stale_mark wiki run-c 4
|
||||
if akquire wiki run-c "producerD" "run-d-holder" 3 2>/dev/null; then
|
||||
echo " BEFUND: stale-markierte run-c (Gen 4) akquiriert — Blockade aufgehoben (Pkt. 3, nur stale blockiert nicht)"
|
||||
else
|
||||
echo "HARD-FAIL (STALE-5): stale-markierte Lease blockiert noch (aktiv-vs-stale-Kontrast fehlt, §5.12 Pkt. 3)" >&2; exit 1
|
||||
fi
|
||||
grep -qiE 'timestamp|wallclock|now|date:' lease/wiki/run-c.lock && { echo "HARD-FAIL (A0-20): Wanduhr im Lockfile" >&2; exit 1; } || true
|
||||
grep -qiE 'timestamp|wallclock|now|20[0-9]{2}-[0-9]{2}-[0-9]{2}' registry/wiki && { echo "HARD-FAIL (A0-20): Wanduhr in Registry" >&2; exit 1; } || true
|
||||
echo "--- Probe: kein wiki/-Eintrag (Registry/Lockfiles ausserhalb) ---"; probe
|
||||
assert_invariant ""
|
||||
echo "RESULT: PASS — STALE-5: Registrierungs-Invariante (gen 3 == hoechster, keine Regression); verwaiste Kanten nie geloescht (AD-17e); HOLD gegen aktive Lease deterministisch; kein Wanduhr-Timestamp (A0-20)"
|
||||
|
||||
# =====================================================================
|
||||
runlabel "STALE-6: DETERMINISMUS (AD-17h/A0-19) — gleicher committeter Baum-Input -> identische Registrierungs-/Stale-Markierungs-/log.md-Outputs (Zwei-Run-Identitaet)"
|
||||
# Zwei vollstaendig unabhaengige, identische Runs ueber denselben Basis-Zustand (BASE).
|
||||
stale_run() { # $1 = Branchname; identischer Ablauf: 2 Leases + 1 Stale-Markierung + logs
|
||||
isolate "$1"
|
||||
git checkout -q -b lease/wiki/run-a "$BASE"
|
||||
akquire wiki run-a "producerA" "run-a-holder"; reg_hold_mark wiki run-a 1
|
||||
git checkout -q -b lease/wiki/run-b "$BASE"
|
||||
akquire wiki run-b "producerA" "run-b-holder"; reg_hold_mark wiki run-b 2
|
||||
reg_stale_mark wiki run-a 3
|
||||
log_stale_reg wiki run-a 1
|
||||
log_stale_mark wiki run-a 3
|
||||
}
|
||||
stale_state() { # voller Observable-Zustand zweier identischer Runs (Determinismus, Pkt. 1/6/7)
|
||||
local out=""
|
||||
out="$out registry=$(sha256sum registry/wiki | cut -d' ' -f1)"
|
||||
out="$out log=$(sha256sum wiki/log.md | cut -d' ' -f1)"
|
||||
out="$out lockfiles=$(find lease -type f | LC_ALL=C sort | xargs -r sha256sum | sha256sum | cut -d' ' -f1)"
|
||||
out="$out leasebranches=$(git for-each-ref --format='%(refname)' refs/heads/lease | LC_ALL=C sort | sha256sum | cut -d' ' -f1)"
|
||||
printf '%s' "$out"
|
||||
}
|
||||
stale_run s6a
|
||||
S6A=$(stale_state)
|
||||
stale_run s6b
|
||||
S6B=$(stale_state)
|
||||
echo "Run A: $S6A"
|
||||
echo "Run B: $S6B"
|
||||
[ "$S6A" = "$S6B" ] || { echo "HARD-FAIL (STALE-6): voller Observable-Zustand zweier identischer Runs weicht ab (AD-17h/A0-19, §5.12 Pkt. 1/6/7)" >&2; exit 1; }
|
||||
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5, auch in STALE-6 — Header verspricht sie je lauffaehigem Run): nur log.md ---"
|
||||
probe
|
||||
assert_invariant "log"
|
||||
grep -qF 'stale: run-a (Gen 3)' registry/wiki || { echo "HARD-FAIL (STALE-6): Stale-Markierungs-Klassifikation fehlt (Pkt. 3)" >&2; exit 1; }
|
||||
assert_no_wallclock registry/wiki
|
||||
echo "RESULT: PASS — STALE-6: voller Observatory-State (Registry/Lockfiles/Lease-Branches/log.md) zweier identischer Runs byte-identisch (AD-17h/A0-19, §5.12 Pkt. 1/6/7); Erhaltungs-Invariante; generationenbasiertes TTL-Ablauf-Kriterium deterministisch"
|
||||
|
||||
echo
|
||||
echo "===== Sandbox abgeschlossen (STALE-1..STALE-6) ====="
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
@@ -0,0 +1,625 @@
|
||||
#!/usr/bin/env bash
|
||||
# Story 3.7 — Sandbox-Tests der Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (§5.13, Revision 3.2)
|
||||
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb37)
|
||||
# Zweck: die Reason/Mutate-Phasen-Disziplin (§5.13) als re-executierbarer Run-Demonstrator
|
||||
# durchspielen —
|
||||
# CONSIST-1 KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7): der P2-Block (§5.9 Pkt. 6)
|
||||
# erzeugt aus committetem Input eine konsistente Änderungsplanung (Input-Zustand,
|
||||
# Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-Erhaltung) — als Variablen-Set
|
||||
# captured, Determinismus (AD-17h/A0-19); Plan-Defizit ⇒ Abbruch vor Mutationsphase
|
||||
# CONSIST-2 PLAN_FREEZE (AC-1, §5.13 Pkt. 2): Plan-Freeze = Veränderungs-Sperre — eine
|
||||
# Mutation ausserhalb der Plan-Pfadmengen (Kandidatenliste ∪ Neu-Anlage ∪ log.md ∪
|
||||
# index.md) wird erkannt und verweigert/verhindert (Ghost-Diff-Kopplung §5.9 Pkt. 5)
|
||||
# CONSIST-3 ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2, §5.13 Pkt. 3): fehlgeschlagene
|
||||
# Mutation rollt auf exakte Baseline zurueck (SHA-256 byte-identisch, Post-Rollback-Diff
|
||||
# leer gg. Baseline), Bundle-Zustand == Baseline; raw/ unverändert (AD-3)
|
||||
# CONSIST-4 MUTATION_ABBRUCH (AC-2, §5.13 Pkt. 3/4): Abbruch nach Teilerfolg (mehrere
|
||||
# geplante Mutationen, nur ein Teil ausgefuehrt) hinterlaesst keinen Teilerfolg als
|
||||
# fertige Mutation (Commit-Boundary = Mutations-Boundary, AD-17f); raw/ unberührt
|
||||
# CONSIST-5 VALIDATION_FAIL (AC-2, §5.13 Pkt. 5): §6-Pkt.-3-Rollback, keine weiteren
|
||||
# Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante)
|
||||
# CONSIST-6 VALIDATION_SUCCESS (AC-3, §5.13 Pkt. 6): Mutationen als Ganzes committet,
|
||||
# Diff gg. Plan-Menge gedeckt, §6-Pkt.-4-Nachweis; kein Wanduhr-Trigger (A0-20)
|
||||
# CONSIST-7 DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4, §5.13 Pkt. 7): Zwei identische
|
||||
# Runs -> identische Plan-/Rollback-/State-Outputs (AD-17h/A0-19); die Phasen-Trennung
|
||||
# ist logisch in einer Session — kein Prozess/Server/MCP wird gestartet (D-3, AD-11,
|
||||
# grep-Negativkontrolle: keine Hintergrundprozesse/Logger-Daemons)
|
||||
# Erhaltungs-Invariante (§5.9 Pkt. 5 / AD-5 / FT-6) als HARDE Assertion je lauffaehigem
|
||||
# Run; Frontmatter-Konformitaet (Vertrag §3.3/§3.4-Subset) je erzeugtem/aktualisiertem Concept.
|
||||
# Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum.
|
||||
set -u
|
||||
ROOT=$(mktemp -d /tmp/sb37-XXXXXX)
|
||||
SB="$ROOT/sb"
|
||||
mkdir -p "$SB/wiki" "$SB/raw"
|
||||
cd "$SB"
|
||||
git init -q
|
||||
# Determinismus vs. Host-Git-Konfiguration (AD-17h): die Sandbox erzeugt LF-Blobs und
|
||||
# LF-Worktree — autocrlf/filemode-Umwandlung des Hosts wuerde sha256-Vergleiche und
|
||||
# Diff-/Probe-Ergebnisse wort-wirksam verschieben. Repo-Reparatur schliesst das aus.
|
||||
git config core.autocrlf false
|
||||
git config core.filemode false
|
||||
git config user.email "sandbox@test"
|
||||
git config user.name "Sandbox"
|
||||
|
||||
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit = Merge-Base) ----------
|
||||
# Mini-Bundle mit zwei Root-Concepts (alpha als Mutations-Objekt, gamma als Kontrolle).
|
||||
cat > wiki/index.md <<'EOF'
|
||||
# Index
|
||||
- [Alpha](alpha.md)
|
||||
- [Gamma](gamma.md)
|
||||
EOF
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
EOF
|
||||
cat > wiki/gamma.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/gamma-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
|
||||
EOF
|
||||
cat > wiki/log.md <<'EOF'
|
||||
# Log
|
||||
EOF
|
||||
cat > raw/alpha-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: deterministische Init-Sequenz.
|
||||
### S-2
|
||||
Evidenz v1: ausschließlich lokale Netze.
|
||||
EOF
|
||||
cat > raw/gamma-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: Gamma-Thema.
|
||||
EOF
|
||||
git add -A
|
||||
git commit -qm "Baseline"
|
||||
BASE=$(git rev-parse HEAD)
|
||||
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
echo
|
||||
|
||||
# ---------- Helfer: Run-Label / Isolation (kein Carry-over zwischen Szenarien) ----------
|
||||
runlabel() { echo; echo "########## $1 ##########"; }
|
||||
isolate() {
|
||||
git checkout -qf -B "$1" "$BASE"
|
||||
git reset -q --hard "$BASE"
|
||||
git clean -qfd wiki raw lease registry scratch plan-run
|
||||
}
|
||||
|
||||
# Erhaltungs-Invariante-Probe (§5.9 Pkt. 5 / AD-5 / FT-6): Baseline-Diff + porcelain,
|
||||
# normalisiert (wiki/-Praefix + .md-Suffix gestrippt, LC_ALL=C-sortiert).
|
||||
probe() {
|
||||
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
|
||||
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
|
||||
}
|
||||
inv_set() { probe | LC_ALL=C sort -u | paste -sd' ' -; }
|
||||
|
||||
# ---------- Harte Assertion der Erhaltungs-Invariante (§5.9 Pkt. 5) ----------
|
||||
inv_viol() { # $1=expected ; 0 = konsistent, !=0 = Verstoß (msg stderr)
|
||||
local expected="$1" p u bad=0
|
||||
got=$(inv_set)
|
||||
for p in $got; do
|
||||
case " $expected " in
|
||||
*" $p "*) ;;
|
||||
*) echo "HARD-FAIL (Erhaltungs-Invariante §5.9 Pkt. 5): '$p' ist kein Ghost-Diff-negativer Eintrag — erlaubte Menge: {$expected}" >&2; bad=1;;
|
||||
esac
|
||||
done
|
||||
for u in $(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}'); do
|
||||
u=$(echo "$u" | sed -e 's|^wiki/||' -e 's|\.md$||')
|
||||
case " $expected " in
|
||||
*" $u "*) ;;
|
||||
*) echo "HARD-FAIL (Duplikat/Ghost-Diff): ungetrackte neue Datei '$u' liegt ausserhalb der erlaubten Ziel-Pfade {$expected} (§5.9 Pkt. 5)" >&2; bad=1;;
|
||||
esac
|
||||
done
|
||||
return $bad
|
||||
}
|
||||
assert_invariant() { # positive Erwartung: Verstoß => HARD-FAIL + Exit 1
|
||||
if inv_viol "$1"; then
|
||||
echo "RESULT: PASS — Probe erfüllt; keine neue Datei; kein Ghost-Diff"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------- Zustands-Restaurations-Invariante (§5.13 Pkt. 3) — harte Assertion ----------
|
||||
# Nach Abbruch/FAIL muss der Post-Rollback-Diff gg. die Baseline LEER sein (Bundle ==
|
||||
# Baseline, SHA-256 byte-identisch je Pfad) — die git-ueberpruefbare Konsistenz-Eigenschaft.
|
||||
assert_restored() { # $1=komma-oder-leer-getrennte wiki-Pfade, die byte-identisch zur Baseline sein müssen
|
||||
local p expected=""
|
||||
[ -z "$1" ] || for p in $1; do expected="$expected $p.md"; done
|
||||
# 1) git-Diff gg. Baseline: keine modifizierte/geloeschte Datei
|
||||
if [ -n "$(git diff --name-only "$BASE" -- wiki/)" ]; then
|
||||
echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): Post-Rollback-Diff gg. Baseline NICHT leer:" >&2
|
||||
git diff --name-only "$BASE" -- wiki/ | sed 's/^/ /' >&2
|
||||
exit 1
|
||||
fi
|
||||
# 2) porcelain: keine ungetrackten/gestagten Eintraege im wiki/-Scope
|
||||
if [ -n "$(git status --porcelain -- wiki/)" ]; then
|
||||
echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): ungetrackte/gestagte Reste im wiki/-Scope:" >&2
|
||||
git status --porcelain -- wiki/ | sed 's/^/ /' >&2
|
||||
exit 1
|
||||
fi
|
||||
# 3) byte-identisch zur Baseline (SHA-256); Neu-Anlage-Randfall (Pfad nur im Run angelegt):
|
||||
# existiert der Pfad NICHT im Baseline-Commit, darf er nach dem Rollback auch nicht in der
|
||||
# Working-Copy existieren (statt eines spurious byte-Vergleichs).
|
||||
for p in $expected; do
|
||||
if git cat-file -e "$BASE:wiki/$p" 2>/dev/null; then
|
||||
[ -f "wiki/$p" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p fehlt (muss == Baseline sein)" >&2; exit 1; }
|
||||
bsha=$(git show "$BASE:wiki/$p" | sha256sum | cut -d' ' -f1)
|
||||
csha=$(sha256sum "wiki/$p" | cut -d' ' -f1)
|
||||
[ "$bsha" = "$csha" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p weicht von Baseline ab ($csha != $bsha)" >&2; exit 1; }
|
||||
else
|
||||
# Neu-Anlage-Pfad (nicht im Baseline-Commit): nach Rollback darf er NICHT existieren.
|
||||
[ ! -e "wiki/$p" ] || { echo "HARD-FAIL (Zustands-Restaurations-Invariante §5.13 Pkt. 3): wiki/$p ist Neu-Anlage-Pfad und muss nach Rollback NICHT existieren" >&2; exit 1; }
|
||||
fi
|
||||
done
|
||||
echo "RESULT: PASS — Zustands-Restaurations-Invariante: Post-Rollback-Diff gg. Baseline leer, Bundle == Baseline (SHA-256 byte-identisch)"
|
||||
}
|
||||
|
||||
# ---------- Frontmatter-Konformitaet (Vertrag §3.3/§3.4, §6.5; Muster Sandbox-3-6) ----------
|
||||
assert_frontmatter() {
|
||||
local f="$1"; shift
|
||||
local v
|
||||
v=$(awk '
|
||||
/^---$/{n++; if(n==2) exit; next}
|
||||
/^[A-Za-z_][A-Za-z0-9_-]*:/{
|
||||
k=$0; sub(/:.*/,"",k)
|
||||
if (k=="sources") top="sources"
|
||||
else if (k=="generated" || k=="verified") top="genver"
|
||||
else top="other"
|
||||
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") print "TOP_UNBEFUGT:" k
|
||||
if (seenk[k]++) print "DUP_KEY:" k
|
||||
r=0
|
||||
if (k=="type") r=1; else if (k=="sources") r=2; else if (k=="generated") r=3
|
||||
else if (k=="verified") r=4; else if (k=="status") r=5; else if (k=="stale_after") r=6
|
||||
if (r>0 && r<lastr) print "ORDER_VIOLATION:" k
|
||||
if (r>0) lastr=r
|
||||
next
|
||||
}
|
||||
/^[[:space:]]/{
|
||||
gsub(/^[[:space:]]+/,""); sub(/^- /,""); gsub(/^[[:space:]]+/,"");
|
||||
if (match($0, /^[A-Za-z_][A-Za-z0-9_-]*:/)) {
|
||||
ik=substr($0,1,RLENGTH-1)
|
||||
if (top=="sources" && ik!="resource" && ik!="id" && ik!="title" && ik!="author" && ik!="usage_count" && ik!="last_modified") print "INNER_UNBEFUGT:" ik
|
||||
if (top=="genver" && ik!="by" && ik!="at") print "INNER_UNBEFUGT:" ik
|
||||
if (top=="other") print "INNER_UNBEFUGT:" ik
|
||||
}
|
||||
}
|
||||
' "$f")
|
||||
if [ -n "$v" ]; then
|
||||
echo "HARD-FAIL (Frontmatter-Subset, Vertrag §3.3/§3.4/§3.5): $v in $f" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -qE '^type: concept$' "$f" || { echo "HARD-FAIL: type=concept fehlt in $f" >&2; exit 1; }
|
||||
grep -qE "^ at: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:?[0-9]{2})$" "$f" || { echo "HARD-FAIL: generated.at ist keine volle ISO-8601-Datetime in $f" >&2; exit 1; }
|
||||
for r in "$@"; do
|
||||
grep -qF " - resource: $r" "$f" || { echo "HARD-FAIL: sources-Eintrag 'resource: $r' fehlt in $f" >&2; exit 1; }
|
||||
done
|
||||
echo "RESULT: PASS — Frontmatter-Konformitaet $f (Subset ok, generated.at volles Datetime)"
|
||||
}
|
||||
|
||||
# ---------- log.md-Eintraege (§5.13; Header-Bullet-Paar je Eintrag, datumsgruppiert) ----------
|
||||
# Datumsgruppen-/Reihenfolge-Konvention (Vertrag §5, neueste zuerst): ein neuer Bullet wird
|
||||
# in die BESTEHENDE Datumsgruppe eingefuegt — nach deren Header, vor etwaigen Gruppen-Bullets —
|
||||
# sonst wird ein neuer Header oben erzeugt. Deterministisch (kein Wanduhr).
|
||||
log_bullet() { # $1 = Bullet-Text (eine Zeile, printf-% escaped); Datumsgruppe/Reihenfolge (neueste zuerst)
|
||||
local day="2026-08-20" # getrennt gesetzt — set -u: gleiche-local-Zeilen-RHS darf day nicht vor Setzung referenzieren
|
||||
local header="## $day" line="$1"
|
||||
if grep -qxF "$header" wiki/log.md; then
|
||||
tmp=$(mktemp)
|
||||
awk -v h="$header" -v l="$line" '
|
||||
BEGIN{p=0}
|
||||
{ if (!p && $0==h) { print; print l; p=1; next } print }
|
||||
' wiki/log.md > "$tmp" && mv "$tmp" wiki/log.md
|
||||
else
|
||||
tmp=$(mktemp)
|
||||
{ printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" && mv "$tmp" wiki/log.md
|
||||
fi
|
||||
}
|
||||
log_plan() { # $1=plan-text (eine Zeile)
|
||||
log_bullet "- Änderungsplanung (P2-Block, §5.9 Pkt. 6 / §5.13 Pkt. 1): $1 (Determinismus AD-17h/A0-19; Baseline $BASE)"
|
||||
}
|
||||
log_abort() { # $1=benannte Abbruch-Ursache — NFR-4-textuelle Benennung (echo). Für einen
|
||||
# FAILED-Run wird log.md NICHT persistent beschrieben: §6 Pkt. 3 / §5.3 rollen auch die
|
||||
# log.md-Einträge als Teilzustand zurück (Bundle nimmt Zustand vor dem Run wieder ein).
|
||||
echo "NFR-4-Benennung (textuell): Mutations-Abbruch (§5.13 Pkt. 3/4) — $1; Zustands-Restaurations-Invariante (Pkt. 3), kein Teilerfolg committet (AD-17f); log.md-Einträge des Runs werden mit dem Teilzustand zurückgerollt (§6 Pkt. 3 / §5.3)"
|
||||
}
|
||||
log_commit() { # $1=plan-vs-ist-diff-befund
|
||||
log_bullet "- Validierung SUCCESS (§5.13 Pkt. 6): Mutationen als Ganzes committet; $1 (§6-Pkt.-4-Nachweis, kein Wanduhr-Trigger A0-20)"
|
||||
}
|
||||
|
||||
# ---------- Reason/Mutate-Helfer (§5.13) ----------
|
||||
# P2-Block (§5.9 Pkt. 6) als Änderungsplanung: capturet den konsistenten Plan als textuelles
|
||||
# Variablen-Set (Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-
|
||||
# Erhaltung) — deterministisch aus dem committeten Git-State (AD-17h/A0-19).
|
||||
p2_plan() { # $1=komma-Liste Ziel-Pfade (OKF-ohne-.md; Neu-Anlage = +name) $2=komma-Liste raw-Quellen (Pfad)
|
||||
local targets="$1" srcs="$2" p s np err=0
|
||||
# (1) Input-Zustand (AD-17a): Working-Copy von raw/ und wiki/ gegen HEAD — uncommittet = Abbruch
|
||||
# (Scope exakt wie Instruktion §5.9 Pkt. 6 Element (1): raw/ und wiki/; Review-Loop-2-D3:
|
||||
# unscoped ganzer-Tree-Prüfung wäre straffer als die Instruktion — die Demo folgt der Instruktion.)
|
||||
if [ -n "$(git status --porcelain -- raw wiki)" ]; then
|
||||
echo "HARD-FAIL (Änderungsplanung, P2-Block-Element (1): Input-Zustand AD-17a): uncommittete Zwischenstaende als Input — Abbruch vor der Mutation (published/committed Input erforderlich)" >&2
|
||||
return 1
|
||||
fi
|
||||
# (2) Ziel-Pfade (Kandidatenliste, §3 Pkt. 2)
|
||||
[ -n "$targets" ] || { echo "HARD-FAIL (Änderungsplanung): leere Ziel-Pfad-Menge (P2-Element (2))" >&2; return 1; }
|
||||
# (3) Quellen-Existenz (EC-1)
|
||||
for s in ${srcs//,/ }; do
|
||||
[ -f "raw/$s" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (3): Quellen-Existenz EC-1): raw/$s existiert nicht" >&2; return 1; }
|
||||
done
|
||||
# (4) Betroffenheit (§3 Pkt. 2, textuell-deterministisch nach §3.2): bestehende Kandidaten
|
||||
# oder geplante Neu-Anlage-Zielpfade (+Prefix; §5.13 Pkt. 2 "∪ Neu-Anlage") — Duplikat-Kontrolle:
|
||||
# ein +Pfad, der bereits existiert, ist kein Neu-Anlage.
|
||||
for p in ${targets//,/ }; do
|
||||
case "$p" in
|
||||
+*) np="${p#+}"
|
||||
[ -f "wiki/$np.md" ] && { echo "HARD-FAIL (Änderungsplanung, P2-Element (4)): Neu-Anlage-Pfad wiki/$np.md existiert bereits (Duplikat — kein Neu-Anlage, §5.10 Pkt. 8)" >&2; return 1; } ;;
|
||||
*) [ -f "wiki/$p.md" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (4)): Betroffenheits-Kandidat wiki/$p.md fehlt (und kein Neu-Anlage-Zielpfad +name)" >&2; return 1; } ;;
|
||||
esac
|
||||
done
|
||||
# (5) wiki/index.md-V-1 (Vertrag §2)
|
||||
[ -f "wiki/index.md" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (5)): fehlende Bundleroot — Run-FAIL (V-1)" >&2; return 1; }
|
||||
# (6) Struktur-Erhaltungs-Check (Vertrag §3.3/§3.4-Subset) je betroffenem Concept
|
||||
for p in ${targets//,/ }; do
|
||||
if [ -f "wiki/$p.md" ]; then
|
||||
v=$(awk '
|
||||
/^---$/{n++; if(n==2) exit; next}
|
||||
/^[A-Za-z_][A-Za-z0-9_-]*:/{ k=$0; sub(/:.*/,"",k)
|
||||
if (k!="type" && k!="sources" && k!="generated" && k!="verified" && k!="status" && k!="stale_after") { print k; exit } }' "wiki/$p.md")
|
||||
[ -z "$v" ] || { echo "HARD-FAIL (Änderungsplanung, P2-Element (6): Struktur-Erhaltung): unbefugter Key '$v' in wiki/$p.md" >&2; return 1; }
|
||||
fi
|
||||
done
|
||||
echo "PLAN_OK targets=($targets) sources=($srcs)"
|
||||
return 0
|
||||
}
|
||||
# Plan-Freeze (Veränderungs-Sperre, §5.13 Pkt. 2): nach Abschluss der Planung darf kein
|
||||
# Plan-Gegenstand ausserhalb der erlaubten Menge mutiert werden — eine Ziel-Pfad-Verletzung
|
||||
# wird erkannt und verweigert (Ghost-Diff-Kopplung §5.9 Pkt. 5).
|
||||
freeze_check() { # $1=komma-Liste der im Run geplanten Ziel-Pfade (ok + neu.log + index)
|
||||
local allowed=" $1 "
|
||||
local m
|
||||
# Normalisierung (wie §5.9 Pkt. 5-Probe): wiki/-Praefix + .md-Suffix strippen (beide Stromarten).
|
||||
for m in $( { git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u); do
|
||||
case "$allowed" in
|
||||
*" $m "*) ;;
|
||||
*) echo "HARD-FAIL (Plan-Freeze §5.13 Pkt. 2 / Ghost-Diff §5.9 Pkt. 5): Mutation ausserhalb der erlaubten Pfad-Menge — '$m' ist nicht im Plan ($allowed)" >&2; return 1;;
|
||||
esac
|
||||
done
|
||||
return 0
|
||||
}
|
||||
# Rollback der Mutationsphase (Zustands-Restaurations-Invariante, §5.13 Pkt. 3): stellt die
|
||||
# Baseline wieder her — §5.3-Pkt.-3/§6-Pkt.-3-Mechanik + Ghost-Diff-Rollback §5.9 Pkt. 5.
|
||||
# (Review-Loop-2-D5: der Voll-Rollback rollt den TEILZUSTAND DES RUNS komplett zurück (§6 Pkt. 3:
|
||||
# "das Bundle nimmt seinen Zustand vor dem Run wieder ein") — in der Sandbox sind alle
|
||||
# ungetrackten wiki/-Dateien Lauf-eigener Teilzustand; es existieren keine fremden uncommitteten
|
||||
# Änderungen (AD-17e-Schutzobjekt). Ein realer Run schützt fremde Änderungen zuvor per
|
||||
# Stash/Scratch-Zone (§5.11 Pkt. 3), bevor ein Rollback greift.)
|
||||
rollback() {
|
||||
git checkout -q "$BASE" -- wiki/ 2>/dev/null || true
|
||||
untracked_rest=$(git status --porcelain -- wiki/ | grep '^??' | awk '{print $2}')
|
||||
if [ -n "$untracked_rest" ]; then
|
||||
echo "ROLLBACK: ungetrackte Reste im wiki/-Scope bei Rollback entdeckt:" >&2
|
||||
echo "$untracked_rest" | sed 's/^/ /' >&2
|
||||
# Ghost-Diff-Rollback §5.9 Pkt. 5: ungetrackte neue Dateien ausserhalb der Ziel-Pfade loeschen
|
||||
for u in $untracked_rest; do
|
||||
echo "ROLLBACK (Ghost-Diff-Rollback §5.9 Pkt. 5): entferne ungetrackte neue Datei '$u' ausserhalb erlaubter Menge"
|
||||
rm -f "$u"
|
||||
done
|
||||
fi
|
||||
git reset -q --hard "$BASE"
|
||||
git clean -qfd wiki
|
||||
}
|
||||
|
||||
# ---- CONSIST-1: KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7; §5.13 Pkt. 1) ----
|
||||
runlabel "CONSIST-1: KONSISTENTE_AENDERUNGSPLANUNG (AC-1, AD-6, A0-7) — der P2-Block (§5.9 Pkt. 6) erzeugt aus committetem Input eine konsistente Änderungsplanung (Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheit, Struktur-Erhaltung); textuelle Capture als Plan; Plan-Defizit (uncommitteter Input) ⇒ Abbruch VOR der Mutationsphase"
|
||||
isolate consist1
|
||||
echo "--- Neue committete Evidenz als Input (AD-17a): raw/alpha-v2.md ---"
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung (Story 3.7-Plan).
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
echo "--- Änderungsplanung (P2-Block als Phase): p2_plan alpha raw/alpha-v2.md ---"
|
||||
PLAN1=$(p2_plan "alpha" "alpha-v2.md") || exit 1
|
||||
echo "$PLAN1"
|
||||
echo "--- Determinismus der Planung (AD-17h/A0-19): zweite Planung desselben committeten States (VOR jedem Run-Mutationsschritt) ---"
|
||||
PLAN2=$(p2_plan "alpha" "alpha-v2.md") || exit 1
|
||||
[ "$PLAN1" = "$PLAN2" ] || { echo "HARD-FAIL (CONSIST-1): Plan ungleich bei gleichem Git-State (AD-17h/A0-19)" >&2; exit 1; }
|
||||
echo "PLAN2 identisch: $PLAN2"
|
||||
echo "--- Plan textuell festgehalten (log.md-Teil der Planung, kein Artefakt-File) ---"
|
||||
log_plan "alpha betroffen via raw/alpha-v2.md (Ziel-Pfade {alpha}, Quellen-Existenz EC-1 ok, Struktur-Erhaltung ok)"
|
||||
grep -qF 'Änderungsplanung (P2-Block' wiki/log.md || { echo "HARD-FAIL (CONSIST-1): Plan-Nachweis fehlt in log.md (§5.13 Pkt. 1)" >&2; exit 1; }
|
||||
echo "--- Plan-Defizit-Kette (I/O-Matrix PLAN_BEABSICHTIGT): uncommitteter Input ⇒ Abbruch VOR der Mutation ---"
|
||||
echo "uncommitteter Rest im raw/" > raw/scratch-deficit.txt
|
||||
if p2_plan "alpha" "alpha-v2.md" >/dev/null 2>&1; then
|
||||
echo "HARD-FAIL (CONSIST-1): uncommitteter Input wurde nicht als Plan-Defizit abgefangen (P2-Element (1), AD-17a)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "BEFUND: Plan-Defizit textuell benannt (NFR-4) — Abbruch vor der Mutationsphase, keine Mutation"
|
||||
fi
|
||||
rm -f raw/scratch-deficit.txt
|
||||
echo "--- Plan-Defizit-Kette (I/O-Matrix PLAN_BEABSICHTIGT): fehlende Quellen-Existenz (P2-Element (3), EC-1) ⇒ Abbruch VOR der Mutation ---"
|
||||
# (Review-Loop-2-Patch: die Löschung wird COMMITTET — eine uncommittierte Löschung würde
|
||||
# Element (1) (uncommitteter Input) zuerst feuern und die EC-1-Kontrolle nicht isolieren;
|
||||
# im committeten Zustand ist Element (3) der einzige Trigger.)
|
||||
git rm -q raw/alpha-v2.md && git commit -qm "Quelle entfernt"
|
||||
if p2_plan "alpha" "alpha-v2.md" >/dev/null 2>&1; then
|
||||
echo "HARD-FAIL (CONSIST-1): fehlende Quelle wurde nicht als Plan-Defizit abgefangen (P2-Element (3), EC-1)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "BEFUND: Plan-Defizit (EC-1: raw/alpha-v2.md fehlt) textuell benannt (NFR-4) — Abbruch vor der Mutationsphase, keine Mutation"
|
||||
fi
|
||||
echo "--- Probe (Erhaltungs-Invariante §5.9 Pkt. 5): nur log.md ---"; probe
|
||||
assert_invariant "log"
|
||||
echo "RESULT: PASS — CONSIST-1: konsistente Änderungsplanung aus committetem Input (P2-Block, §5.9 Pkt. 6 / §5.13 Pkt. 1); textuelle Capture; Determinismus (AD-17h/A0-19); Plan-Defizit ⇒ Abbruch vor der Mutation (PLAN_BEABSICHTIGT; P2-Element (1) AD-17a isoliert [untracked Datei] + P2-Element (3) EC-1 isoliert [committete Löschung]); Erhaltungs-Invariante"
|
||||
|
||||
# ---- CONSIST-2: PLAN_FREEZE (AC-1; §5.13 Pkt. 2) ----
|
||||
runlabel "CONSIST-2: PLAN_FREEZE (§5.13 Pkt. 2) — Plan-Freeze = Veränderungs-Sperre: eine Mutation ausserhalb der Plan-Pfadmengen (Kandidatenliste ∪ Neu-Anlage ∪ log.md ∪ index.md) wird erkannt und verhindert (Ghost-Diff-Kopplung §5.9 Pkt. 5); Misch-Run (Update + Neu-Anlage): der geplante Neu-Anlage-Pfad ist erlaubter Teil der Plan-Menge (Review-Loop-2-Patch)"
|
||||
isolate consist2
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
### S-4
|
||||
Evidenz v2: Delta-Thema — bisher nicht im Bundle repraesentiert.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
p2_plan "alpha,+delta" "alpha-v2.md" >/dev/null || exit 1
|
||||
log_plan "alpha betroffen + delta neu angelegt via raw/alpha-v2.md (Plan-Freeze aktiv, Misch-Run)"
|
||||
echo "--- Mutationsphase: geplante Mutation von alpha — erlaubt (Plan-Ziel-Pfad) ---"
|
||||
cat >> wiki/alpha.md <<'EOF'
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
grep -q "synchrone Kopplung" wiki/alpha.md || { echo "HARD-FAIL (CONSIST-2): geplante Mutation fehlgeschlagen" >&2; exit 1; }
|
||||
echo "--- Mutationsphase: geplante Neu-Anlage von delta — erlaubt (Neu-Anlage im Plan, §5.13 Pkt. 2) ---"
|
||||
cat > wiki/delta.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-20T00:00:00Z
|
||||
---
|
||||
Delta-Thema (raw/alpha-v2.md#S-4).
|
||||
EOF
|
||||
[ -f wiki/delta.md ] || { echo "HARD-FAIL (CONSIST-2): geplante Neu-Anlage fehlgeschlagen" >&2; exit 1; }
|
||||
echo "--- Freeze-Verletzung simulieren: Ghost-Mutation auf NICHT geplanten Pfad gamma (Plan-Gegenstand ausserhalb der erlaubten Menge) ---"
|
||||
echo "unbefugte gamma-Aenderung" >> wiki/gamma.md
|
||||
if freeze_check "alpha delta log index"; then
|
||||
echo "HARD-FAIL (CONSIST-2): Mutation ausserhalb der Plan-Pfadmengen wurde nicht erkannt (Plan-Freeze, §5.13 Pkt. 2)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "BEFUND: Freeze-Verletzung erkannt (ghost gamma) — Veränderungs-Sperre greift"
|
||||
fi
|
||||
echo "--- Ghost-Diff-Rollback (§5.9 Pkt. 5): gamma aus Baseline-Zustand wiederherstellen, textuell benannt (NFR-4) ---"
|
||||
git checkout -q "$BASE" -- wiki/gamma.md
|
||||
echo "NFR-4-Nennung: Ghost-Diff auf 'gamma' (ausserhalb Plan-Menge) zurueckgerollt — textuell benannt"
|
||||
freeze_check "alpha delta log index" || exit 1
|
||||
echo "--- Voll-Rollback (Abbruch des Runs): Teilzustand {alpha, delta, log} wird komplett zurückgerollt (Zustands-Restaurations-Invariante) ---"
|
||||
rollback
|
||||
assert_restored "alpha gamma index log delta"
|
||||
echo " BEFUND: Neu-Anlage-Pfad delta existiert nach Rollback NICHT (assert_restored-Neu-Anlage-Guard) — Misch-Run-Teilerfolg ist vollständig rückgerollt"
|
||||
echo "RESULT: PASS — CONSIST-2: Plan-Freeze = Veränderungs-Sperre (erlaubte Menge {alpha, delta (Neu-Anlage), log, index}); Verletzung (gamma) erkannt + zurueckgerollt (Ghost-Diff-Rollback §5.9 Pkt. 5); Neu-Anlage-Pfad im Plan ist erlaubter Teil der Menge; Misch-Run-Voll-Rollback: Neu-Anlage nach Rollback nicht existent (Zustands-Restaurations-Invariante); Erhaltungs-Invariante"
|
||||
|
||||
# ---- CONSIST-3: ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2; §5.13 Pkt. 3) ----
|
||||
runlabel "CONSIST-3: ZUSTANDS-RESTAURATIONS-INVARIANTE (AC-2) — fehlgeschlagene Mutation rollt auf exakte Baseline zurueck (Post-Rollback-Diff leer, SHA-256 byte-identisch); Bundle == Baseline; raw/ unverändert (AD-3)"
|
||||
isolate consist3
|
||||
raw_sha_before=$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
echo "--- Planung (konsistent) + Mutationsphase: Teilzustand wird geschrieben (alpha erweitert), dann bricht die Mutation AB (Fehler) ---"
|
||||
p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1
|
||||
log_plan "alpha betroffen via raw/alpha-v2.md"
|
||||
cat >> wiki/alpha.md <<'EOF'
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
echo " (Teilzustand geschrieben: alpha erweitert)"
|
||||
echo "--- Mutations-Abbruch: Rollback (Zustands-Restaurations-Invariante §5.13 Pkt. 3; §5.3-Pkt.-3-/§6-Pkt.-3-Rollback, Ghost-Diff-Rollback §5.9 Pkt. 5) ---"
|
||||
rollback
|
||||
log_abort "Fehler waehrend der Mutationsphase (alpha-Teilzustand)"
|
||||
echo "--- KERN-ASSERTION: Post-Rollback-Diff leer, Bundle == Baseline (SHA-256 je Pfad) ---"
|
||||
assert_restored "alpha gamma index log"
|
||||
echo "--- raw/ unverändert (AD-3) ---"
|
||||
[ "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" = "$raw_sha_before" ] || { echo "HARD-FAIL (CONSIST-3): raw/ verändert (AD-3)" >&2; exit 1; }
|
||||
[ -f raw/alpha-v1.md ] || { echo "HARD-FAIL (CONSIST-3): raw/alpha-v1.md fehlt (AD-3)" >&2; exit 1; }
|
||||
echo "RESULT: PASS — CONSIST-3: fehlgeschlagene Mutation rollt auf exakte Baseline zurueck (Post-Rollback-Diff leer, SHA-256 byte-identisch, Bundle == Baseline); raw/ unverändert (AD-3); Zustands-Restaurations-Invariante erzwungen"
|
||||
|
||||
# ---- CONSIST-4: MUTATION_ABBRUCH (AC-2; §5.13 Pkt. 3/4) ----
|
||||
runlabel "CONSIST-4: MUTATION_ABBRUCH (AC-2) — Abbruch nach Teilerfolg (mehrere geplante Mutationen, nur ein Teil ausgefuehrt) hinterlaesst KEINEN Teilerfolg als fertige Mutation (Commit-Boundary = Mutations-Boundary, AD-17f); raw/ unberührt"
|
||||
isolate consist4
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
cat > raw/gamma-v2.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v2: Gamma schaerft seine Abgrenzung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
echo "--- Plan: ZWEI Ziel-Pfade (alpha, gamma) — Mutationsphase fuehrt nur alpha aus, dann Abbruch ---"
|
||||
p2_plan "alpha,gamma" "alpha-v2.md,gamma-v2.md" >/dev/null || exit 1
|
||||
log_plan "alpha,gamma betroffen (zwei geplante Mutationen)"
|
||||
PRE_MUT_HEAD=$(git rev-parse HEAD)
|
||||
echo " (HEAD vor der Mutationsphase = $PRE_MUT_HEAD; Evidenz-Commit ist Input, kein Run-Mutations-Commit)"
|
||||
cat >> wiki/alpha.md <<'EOF'
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
echo " (alpha mutiert; gamma geplant aber NOCH NICHT ausgefuehrt — Abbruch)"
|
||||
echo "--- Kein Teilerfolg darf als fertige Mutation veröffentlicht werden (AD-17f): nicht committen, zurueckrollen ---"
|
||||
if [ "$(git log --oneline -1 --format='%H')" != "$PRE_MUT_HEAD" ]; then
|
||||
echo "HARD-FAIL (CONSIST-4): es wurde ein Commit während des Abbruchs erzeugt (Commit-Boundary verletzt, AD-17f)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo " BEFUND: kein Commit während der Teilfolge (HEAD == $PRE_MUT_HEAD; Commit-Boundary = Mutations-Boundary gewahrt)"
|
||||
fi
|
||||
rollback
|
||||
log_abort "Abbruch nach Teilerfolg (alpha ausgefuehrt, gamma nicht) — kein Teilerfolg committet"
|
||||
echo "--- KERN-ASSERTION: Post-Rollback-Diff leer, Bundle == Baseline (kein Teilerfolg sichtbar) ---"
|
||||
assert_restored "alpha gamma index log"
|
||||
echo "--- raw/ unberührt (AD-3): Baseline-raw-Dateien byte-identisch nach Rollback ---"
|
||||
# Setup-Existenz-Assert: die Baselines müssen im Baseline-Commit existieren, sonst wäre der
|
||||
# SHA-Vergleich leerer-Input (der stille `|| sha256sum...`-Fallback wäre toter Pfad — kein pipefail).
|
||||
git cat-file -e "$BASE:raw/alpha-v1.md" || { echo "HARD-FAIL (Setup): raw/alpha-v1.md fehlt im Baseline-Commit" >&2; exit 1; }
|
||||
git cat-file -e "$BASE:raw/gamma-v1.md" || { echo "HARD-FAIL (Setup): raw/gamma-v1.md fehlt im Baseline-Commit" >&2; exit 1; }
|
||||
rawv1_a=$(git show "$BASE:raw/alpha-v1.md" | sha256sum | cut -d' ' -f1)
|
||||
rawv1_g=$(git show "$BASE:raw/gamma-v1.md" | sha256sum | cut -d' ' -f1)
|
||||
[ "$rawv1_a" = "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-4): raw/alpha-v1.md verändert (AD-3)" >&2; exit 1; }
|
||||
[ "$rawv1_g" = "$(sha256sum raw/gamma-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-4): raw/gamma-v1.md verändert (AD-3)" >&2; exit 1; }
|
||||
echo " BEFUND: raw/alpha-v1.md + raw/gamma-v1.md == Baseline-SHA (der Run hat raw/ nicht berührt; die Evidenz-Input-Commit-V2-Dateien sind Input, kein Baseline-/AD-3-Gegenstand)"
|
||||
echo "RESULT: PASS — CONSIST-4: MUTATION_ABBRUCH nach Teilerfolg — kein Teilerfolg als fertige Mutation veröffentlicht (Commit-Boundary = Mutations-Boundary, AD-17f); Rollback stellt Baseline wieder her; Zustands-Restaurations-Invariante; raw/ unberührt (AD-3)"
|
||||
|
||||
# ---- CONSIST-5: VALIDATION_FAIL (AC-2; §5.13 Pkt. 5) ----
|
||||
runlabel "CONSIST-5: VALIDATION_FAIL (AC-2) — §6-Pkt.-3-Rollback bei Validierungs-FAIL: keine weiteren Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante); raw/ unverändert (AD-3)"
|
||||
isolate consist5
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1
|
||||
log_plan "alpha betroffen via raw/alpha-v2.md"
|
||||
echo "--- Mutationsphase: alpha wird mutiert — aber mit Frontmatter-Verstoß (unbefugter Key foo:) ---"
|
||||
cat >> wiki/alpha.md <<'EOF'
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
printf '\nfoo: bar\n' >> wiki/alpha.md
|
||||
echo "--- Validierungsphase (§6): FAIL (Struktur-Verstoß, unbefugter Key) -> §6-Pkt.-3-Rollback, KEINE weiteren Mutationen ---"
|
||||
if grep -q '^foo:' wiki/alpha.md; then
|
||||
echo " Validator-BEFUND: FAIL (unbefugter Key 'foo:', Punkt 6-Struktur-Verstoß) — textuell benannt (NFR-4)"
|
||||
else
|
||||
echo "HARD-FAIL (CONSIST-5): Validator-Verstoß nicht erkennbar gemacht" >&2; exit 1
|
||||
fi
|
||||
rollback
|
||||
log_abort "Validierungs-FAIL (unbefugter Key foo: in alpha) — §6-Pkt.-3-Rollback, keine weiteren Mutationen"
|
||||
echo "--- KERN-ASSERTION: Endzustand konsistent (Baseline wiederhergestellt) ---"
|
||||
assert_restored "alpha gamma index log"
|
||||
# (Review-Loop-2-Patch: gg. den Baseline-Commit vergleichen — der Selbstvergleich wäre
|
||||
# tautologisch und bewiese nichts; Muster CONSIST-4.)
|
||||
rawv1_b=$(git show "$BASE:raw/alpha-v1.md" | sha256sum | cut -d' ' -f1)
|
||||
[ "$rawv1_b" = "$(sha256sum raw/alpha-v1.md | cut -d' ' -f1)" ] || { echo "HARD-FAIL (CONSIST-5): raw/ verändert (AD-3)" >&2; exit 1; }
|
||||
echo "RESULT: PASS — CONSIST-5: VALIDATION_FAIL — §6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent (Zustands-Restaurations-Invariante); raw/ unverändert (AD-3)"
|
||||
|
||||
# ---- CONSIST-6: VALIDATION_SUCCESS (AC-3; §5.13 Pkt. 6) ----
|
||||
runlabel "CONSIST-6: VALIDATION_SUCCESS (AC-3) — Mutationen als Ganzes committet (erst nach Diff-Selbsttest ohne Ghost-Diff, §5.9 Pkt. 5); Diff gg. Plan-Menge gedeckt; §6-Pkt.-4-Nachweis; kein Wanduhr-Trigger (A0-20)"
|
||||
isolate consist6
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
p2_plan "alpha" "alpha-v2.md" >/dev/null || exit 1
|
||||
log_plan "alpha betroffen via raw/alpha-v2.md"
|
||||
echo "--- Mutationsphase: geplante Mutation von alpha (regelkonform) — §5.9-Pkt.-2-Update-Form: Body-Erweiterung + sources-Zuwachs + generated.at-Bump ---"
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s3
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-20T00:00:00Z
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
echo "--- Diff-Selbsttest VOR dem Commit (§5.9 Pkt. 5): {alpha, log} — kein Ghost-Diff ---"; probe
|
||||
assert_invariant "alpha log"
|
||||
echo "--- Plan-Freeze-Check (§5.13 Pkt. 2): Diff gg. Plan-Menge gedeckt ---"
|
||||
freeze_check "alpha log index" || exit 1
|
||||
echo "--- Validierungsphase (§6): SUCCESS -> §6-Pkt.-4-Nachweis und SUCCESS-Bullet VOR dem Commit (§5.13 Pkt. 6) ---"
|
||||
echo "--- §6-Pkt.-4-Nachweis: Verdikt je Datei SUCCESS (frontmatter-Konformität) ---"
|
||||
assert_frontmatter wiki/alpha.md "raw/alpha-v1.md" "raw/alpha-v2.md"
|
||||
echo " (§5.13-demo: der committete Zustand ist gültig — alpha frontmatter-konform (kein unbefugter Key, generated.at volles Datetime), sources-Nachführung der Update-Evidenz vollzogen (§5.9 Pkt. 2); der Body zitiert raw/alpha-v2.md#S-3 nur mit existierendem sources-Eintrag (EC-1/Punkt-3-konform))"
|
||||
log_commit "Diff gg. Plan-Menge gedeckt ({alpha, log}), kein Ghost-Diff"
|
||||
echo "--- Mutationsphase zu Ende: Mutationen als Ganzes committen (§5.13 Pkt. 6, Commit-Boundary AD-17f) — Commit enthält alpha + log.md mit Plan- und SUCCESS-Bullets ---"
|
||||
git add -A && git commit -qm "Run: alpha-Update (Story 3.7 CONSIST-6)"
|
||||
grep -qF 'Änderungsplanung (P2-Block' wiki/log.md || { echo "HARD-FAIL (CONSIST-6): Plan-Nachweis fehlt (Pkt. 1)" >&2; exit 1; }
|
||||
grep -qF 'Validierung SUCCESS' wiki/log.md || { echo "HARD-FAIL (CONSIST-6): §6-Pkt.-4-Nachweis fehlt (Pkt. 6)" >&2; exit 1; }
|
||||
echo "--- kein Wanduhr-Trigger (A0-20): der Commit folgt aus Phasenabschluss, nicht aus Kalenderzeit ---"
|
||||
echo "RESULT: PASS — CONSIST-6: VALIDATION_SUCCESS — Mutationen als Ganzes committet (nach Diff-Selbsttest ohne Ghost-Diff); Diff gg. Plan-Menge gedeckt; §6-Pkt.-4-Nachweis; Aktivierungs-Befund zu Wanduhr-Trigger siehe CONSIST-7"
|
||||
|
||||
# ---- CONSIST-7: DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4; §5.13 Pkt. 7) ----
|
||||
runlabel "CONSIST-7: DETERMINISMUS-ZWEI-RUN + KEINE_ENGINE (AC-4, AD-17h/A0-19, D-3/AD-11) — zwei identische Runs -> identische Plan-/Rollback-/State-Outputs; die Phasen-Trennung ist logisch in einer Session — keine Hintergrundprozesse/Server/MCP (grep-Negativkontrolle)"
|
||||
echo "--- KEINE_ENGINE-Negativkontrolle: der Run startet keinen Prozess/Server/MCP (nur Shell/Git/Datei, D-3/AD-11) ---"
|
||||
# Robustheits-Guard: pgrep bevorzugt, sonst ps-basierte Negativkontrolle (portabel, nicht-vakuum).
|
||||
if command -v pgrep >/dev/null 2>&1; then
|
||||
engine_proc=$(pgrep -f "wow-flow-engine|wow-server|wow-mcp" 2>/dev/null || true)
|
||||
else
|
||||
command -v ps >/dev/null 2>&1 || { echo "HARD-FAIL (KEINE_EIGENE_ENGINE): weder pgrep noch ps verfügbar — Negativkontrolle kann nicht greifen" >&2; exit 1; }
|
||||
engine_proc=$(ps aux 2>/dev/null | grep -E "wow-flow-engine|wow-server|wow-mcp" | grep -v "grep" || true)
|
||||
fi
|
||||
if [ -n "$engine_proc" ]; then
|
||||
echo "HARD-FAIL (KEINE_EIGENE_ENGINE §5.13 Pkt. 7 / D-3 / AD-11): ein Workflow-Engine-Prozess wurde gestartet" >&2
|
||||
echo "$engine_proc" | sed 's/^/ /' >&2
|
||||
exit 1
|
||||
else
|
||||
echo " BEFUND: keine Workflow-Engine-Prozesse (wow-flow-engine/wow-server/wow-mcp) — keine eigene Engine (AC-4)"
|
||||
fi
|
||||
consist7_run() { # deterministischer Run auf isoliertem Zustand (identische Plan-/Rollback-/State-Outputs)
|
||||
local branch="$1"
|
||||
isolate "$branch"
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
local plan cap_commit
|
||||
plan=$(p2_plan "alpha" "alpha-v2.md") || return 99
|
||||
# Plan-Capture-Disclaimer (BH-13): plan-run.capture ist NUR ein deterministischer Sandbox-Zeuge
|
||||
# für den Zwei-Run-Vergleich — NICHT Teil der §5.13-Ausführungsdisziplin (die Instruktion
|
||||
# verlangt laut Ask-First kein Plan-Artefakt-File; „kein Artefakt-File" bleibt die Norm).
|
||||
echo "$plan" > plan-run.capture
|
||||
git add -A && git commit -qm "Plan-Capture"
|
||||
cap_commit=$(git rev-parse HEAD)
|
||||
# Rollback-Pfad: Teilzustand schreiben + Abbruch + Rollback (Zustands-Restaurations-Invariante)
|
||||
cat >> wiki/alpha.md <<'EOF'
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
rollback >/dev/null
|
||||
# Plan-Capture aus dem committeten Baum lesen: rollback () resettet auf BASE und entfernt so
|
||||
# die Arbeitskopie von plan-run.capture (nur im Plan-Capture-Commit getrackt) — der committete
|
||||
# Baum bleibt als deterministischer Zeuge erhalten (AD-17h/A0-19).
|
||||
plan_state=$(git show "$cap_commit:plan-run.capture")
|
||||
plan_sha=$(git show "$cap_commit:plan-run.capture" | sha256sum | cut -d' ' -f1)
|
||||
# log_sha nach Rollback ist definitionsgemäß die Baseline-Konstante (leerer Indikator — die
|
||||
# Baseline-log.md wird gegen sich selbst gemessen); Determinismus-Zeuge ist plan_sha/plan_state
|
||||
# aus dem committeten Plan-Capture (non-vacuous).
|
||||
echo "state plan=[$plan_state] plan_sha=$plan_sha"
|
||||
}
|
||||
echo "--- Zwei identische Runs (gleicher Baum-Input) ---"
|
||||
S7A=$(consist7_run s7a)
|
||||
echo "Run A: $S7A"
|
||||
S7B=$(consist7_run s7b)
|
||||
echo "Run B: $S7B"
|
||||
[ "$S7A" = "$S7B" ] || { echo "HARD-FAIL (CONSIST-7): Plan-/State-Outputs zweier identischer Runs weichen ab (AD-17h/A0-19, §5.13 Pkt. 1/7)" >&2; exit 1; }
|
||||
echo "--- Zustands-Restaurations-Invariante im Zwei-Run-Pfad (Rollback >> Baseline leer) ---"
|
||||
isolate s7c
|
||||
[ "$(inv_set)" = "" ] || { echo "HARD-FAIL (CONSIST-7): kein sauberer Zustand nach Rollback erwartet (leer)" >&2; exit 1; }
|
||||
echo "RESULT: PASS — CONSIST-7: Zwei identische Runs -> identische Plan-/Rollback-/State-Outputs (AD-17h/A0-19); keine Workflow-Engine gestartet (D-3/AD-11, KEINE_EIGENE_ENGINE §5.13 Pkt. 7, AC-4)"
|
||||
|
||||
echo
|
||||
echo "===== Sandbox abgeschlossen (CONSIST-1..CONSIST-7) ====="
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
@@ -0,0 +1,572 @@
|
||||
#!/usr/bin/env bash
|
||||
# Story 3.8 — Sandbox-Tests des Determinismus-Vertrags (AD-17h/FT-10/A0-19) als
|
||||
# Agent-Instruktions-Validator (§5.14, Revision 3.3)
|
||||
# Re-executierbar: bash run-sandbox.sh (ab Workspace-Root; Sandbox unter /tmp/sb38)
|
||||
# Zweck: die deterministische Bestätigungs-Mechanik (§5.14) als re-executierbarer
|
||||
# Run-Demonstrator durchspielen —
|
||||
# DET-1 BUNDLE_STATE_DEFINITION: ein Mini-Bundle erzeugen und die Bundle-State-
|
||||
# Projektion deterministisch definieren/extrahieren (wiki/-Dateien + Plan-/
|
||||
# Kandidaten-/Reihenfolge-Outputs; Extraktion zweimal -> identisch)
|
||||
# DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum): zwei unabhängige Läufe über demselben
|
||||
# committeten Baum -> identische Bundle-States bis auf at; die Assertion
|
||||
# vergleicht echte Content-Hashes (nicht nur Vorhandensein); der Witness
|
||||
# (wiki/alpha.md) weicht byte-weise von der Baseline ab (kein Vakuum)
|
||||
# DET-3 ZWEI_RUN_ABWEICHUNG: künstlich divergenter Lauf -> als AD-16-Klassifikations-
|
||||
# defekt klassifiziert und textuell benannt (kein Rauschen); korrigierter zweiter
|
||||
# Lauf == erster Lauf (erneut bestätigt)
|
||||
# DET-4 AT_GAP_AUSNAHME: generated.at-Wanduhr-Gap ist die einzige benannte Ausnahme
|
||||
# (diff zwischen zwei Runs ausschließlich auf der at-Zeile; nach at-Maskierung
|
||||
# byte-identisch); jede andere Differenz liegt ausserhalb der Ausnahme =
|
||||
# AD-16-Klassifikationsdefekt
|
||||
# DET-5 EM_DASH: Em-Dash — kollabiert auf dieselbe canonische Form wie En-Dash/
|
||||
# Bindestrich/Unterstrich/Leerzeichen (§3.2 Pkt. 1b, [-–— _] -> -)
|
||||
# DET-6 KOLLAPS: Läufe (a--b), führende (-x), trailende (x-) -> Kollaps auf
|
||||
# genau ein - + Trim (Reichweite: jedes Vorkommen -> genau ein -)
|
||||
# DET-7 MATCH_SCOPE: Stufe a matcht ganze Wörter über den Body, exklusive YAML-
|
||||
# Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen)
|
||||
# DET-8 ORPHAN: neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer -> unzugeordnet,
|
||||
# log.md-verwaist protokolliert (Datumsgruppe, <Baseline-Commit>), kein Banner,
|
||||
# keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default)
|
||||
# Determinismus-Vertrag (AD-17h/A0-19) als HARDE Assertion je Szenario; KEINE_EIGENE_ENGINE-
|
||||
# Negativkontrolle (D-3/AD-11) in DET-1; Frontmatter-/log.md-Konformitaet (Vertrag §3.3/§3.4, §5).
|
||||
# Ubuntu-Sandbox-Semantik auf einem /tmp-Baum — NIE der reale wiki/- oder raw/-Baum.
|
||||
set -u
|
||||
ROOT=$(mktemp -d /tmp/sb38-XXXXXX)
|
||||
SB="$ROOT/sb"
|
||||
mkdir -p "$SB/wiki" "$SB/raw"
|
||||
cd "$SB"
|
||||
git init -q
|
||||
# Determinismus vs. Host-Git-Konfiguration (AD-17h): LF-Blobs + LF-Worktree —
|
||||
# autocrlf/filemode-Umwandlung des Hosts wuerde sha256-Vergleiche verschieben.
|
||||
git config core.autocrlf false
|
||||
git config core.filemode false
|
||||
git config user.email "sandbox@test"
|
||||
git config user.name "Sandbox"
|
||||
|
||||
# ---------- Basis-Baum (committete Ausgangslage = Baseline-Commit) ----------
|
||||
# Mini-Bundle mit zwei Root-Concepts (alpha als Mutations-Objekt, gamma als Kontrolle).
|
||||
cat > wiki/index.md <<'EOF'
|
||||
# Index
|
||||
- [Alpha](alpha.md)
|
||||
- [Gamma](gamma.md)
|
||||
EOF
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
EOF
|
||||
cat > wiki/gamma.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/gamma-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Gamma beschreibt ein anderes, hier nicht betroffenes Thema.
|
||||
EOF
|
||||
cat > wiki/log.md <<'EOF'
|
||||
# Log
|
||||
EOF
|
||||
cat > raw/alpha-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: deterministische Init-Sequenz.
|
||||
### S-2
|
||||
Evidenz v1: ausschließlich lokale Netze.
|
||||
EOF
|
||||
cat > raw/gamma-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz v1: Gamma-Thema.
|
||||
EOF
|
||||
git add -A
|
||||
git commit -qm "Baseline"
|
||||
BASE=$(git rev-parse HEAD)
|
||||
echo "BASELINE-COMMIT (Merge-Base, eindeutiger Commit-Object-Wert): $BASE"
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
echo
|
||||
|
||||
# ---------- Helfer: Run-Label / Isolation (kein Carry-over zwischen Szenarien) ----------
|
||||
runlabel() { echo; echo "########## $1 ##########"; }
|
||||
isolate() {
|
||||
git checkout -qf -B "$1" "$BASE"
|
||||
git reset -q --hard "$BASE"
|
||||
git clean -qfd wiki raw lease registry scratch plan-run
|
||||
}
|
||||
|
||||
# ---------- Deterministische Normalisierung (§3.2 Pkt. 1b, Story 3.8) ----------
|
||||
# lowercasing + Binde-Varianten-Kollaps [-–— _] -> - (En-Dash –, Em-Dash —,
|
||||
# Bindestrich -, Unterstrich _, Leerzeichen), Kollaps-Reichweite: jedes Vorkommen ->
|
||||
# genau ein -; Läufe kollabieren; führende/trailende Separatoren werden getrimmt.
|
||||
norm() { # $1 = Begriff (Kollaps-Form, NICHT registry-aufloesend — Kollaps-Scope isoliert)
|
||||
printf '%s' "$1" | sed \
|
||||
-e 's|[–—]|-|g' \
|
||||
-e 's|_| |g' \
|
||||
-e 's| |-|g' \
|
||||
-e 's|--*|-|g' \
|
||||
-e 's|^-*||' \
|
||||
-e 's|-*$||' \
|
||||
| tr 'A-Z' 'a-z'
|
||||
}
|
||||
# Stufe-a-Match-Scope (§3.2 Pkt. 2a, Story 3.8): matcht ganze Wörter über den Body,
|
||||
# exklusive YAML-Frontmatter. Implementierung: Frontmatter (zwischen ersten beiden ---)
|
||||
# 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
|
||||
for file in "${@:2}"; do
|
||||
# Body = alles nach dem zweiten "---" (Frontmatter exkludiert, deterministisch)
|
||||
body=$(awk 'BEGIN{n=0} /^---$/{n++; next} n>=2{print}' "$file")
|
||||
# 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])${term}([^A-Za-z0-9]|$)"; then
|
||||
basename "$file" .md
|
||||
fi
|
||||
done
|
||||
}
|
||||
# log.md-Eintrag (datumsgruppiert, neueste zuerst; Vertrag §5). Deterministisch fester Tag.
|
||||
log_bullet() { # $1 = Bullet-Text (eine Zeile)
|
||||
local day="2026-08-20"
|
||||
local header="## $day" line="$1"
|
||||
if grep -qxF "$header" wiki/log.md; then
|
||||
tmp=$(mktemp)
|
||||
awk -v h="$header" -v l="$line" '
|
||||
BEGIN{p=0}
|
||||
{ if (!p && $0==h) { print; print l; p=1; next } print }
|
||||
' wiki/log.md > "$tmp" && mv "$tmp" wiki/log.md
|
||||
else
|
||||
tmp=$(mktemp)
|
||||
{ printf '%s\n' "$header"; printf '%s\n' "$line"; cat wiki/log.md; } > "$tmp" && mv "$tmp" wiki/log.md
|
||||
fi
|
||||
}
|
||||
|
||||
# =====================================================================================
|
||||
# DET-1 BUNDLE_STATE_DEFINITION (§5.14 Pkt. 1)
|
||||
# =====================================================================================
|
||||
runlabel "DET-1: BUNDLE_STATE_DEFINITION — Mini-Bundle erzeugen; Bundle-State-Projektion (wiki/-Dateien + Plan-/Kandidaten-/Reihenfolge-Outputs) deterministisch definieren/extrahieren; KEINE_EIGENE_ENGINE (D-3/AD-11)"
|
||||
if command -v pgrep >/dev/null 2>&1; then
|
||||
engine_proc=$(pgrep -f "wow-flow-engine|wow-server|wow-mcp" 2>/dev/null || true)
|
||||
else
|
||||
command -v ps >/dev/null 2>&1 || { echo "HARD-FAIL (KEINE_EIGENE_ENGINE): weder pgrep noch ps verfügbar" >&2; exit 1; }
|
||||
engine_proc=$(ps aux 2>/dev/null | grep -E "wow-flow-engine|wow-server|wow-mcp" | grep -v "grep" || true)
|
||||
fi
|
||||
if [ -n "$engine_proc" ]; then
|
||||
echo "HARD-FAIL (KEINE_EIGENE_ENGINE D-3/AD-11, §5.14): ein Workflow-Engine-Prozess wurde gestartet" >&2
|
||||
echo "$engine_proc" | sed 's/^/ /' >&2
|
||||
exit 1
|
||||
else
|
||||
echo " BEFUND: keine Workflow-Engine-Prozesse (wow-flow-engine/wow-server/wow-mcp) — der Agent-Instruktions-Validator ist die Instruktion selbst (kein Prozess/Server/MCP)"
|
||||
fi
|
||||
isolate det1
|
||||
# Der Run erzeugt einen definierten Bundle-State: neue committete Evidenz (Input) ->
|
||||
# Plan-/Kandidaten-Output -> Mutations-Commit (deterministischer Run-Ausgangszustand).
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
plan=$(printf 'cand=alpha;form=update;baseline=%s' "$BASE")
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s3
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-20T00:00:00Z
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
log_bullet "- Determinismus-Bestätigung (Bundle-State-Definition, §5.14 Pkt. 1): $plan"
|
||||
git add -A && git commit -qm "Run: alpha-Update (DET-1)"
|
||||
# Bundle-State-Extraktion (§5.14 Pkt. 1): (a) committeter Baum gg. Baseline,
|
||||
# (b) Kandidatenliste in Zuwachs-Sicht-Ordnung, (c) Ausführungs-Entscheidung (Form-Wahl).
|
||||
# Deterministische Projektion = (a)+(b)+(c) — zweimal extrahiert, byte-identisch.
|
||||
# Dev-Demo-Notiz: die (b)-/(c)-Kandidaten-/Form-Outputs sind für die Wiederholungs-Probe
|
||||
# eigentliche Run-Feststellungen (hier redaktionell verdichtet; die Tendenz "die Bundle-State
|
||||
# wiederholt extrahieren ist deterministisch" wird von der zweimaligen Wiederholung bewiesen,
|
||||
# die Ausführungs-Verdikte selbst belegen die DET-Szenarien DET-3/DET-4/DET-7).
|
||||
bs_a() {
|
||||
{ git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } \
|
||||
| sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u
|
||||
# Kandidatenliste (Stufe a, deterministisch; Zuwachs-Ordnung) — hier textuell festgehalten:
|
||||
match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md
|
||||
# Form-Wahl (Ausführungs-Entscheidung): alpha ist betroffen, gamma nicht
|
||||
echo "form=update"
|
||||
echo "baseline=$BASE"
|
||||
}
|
||||
B1=$(bs_a)
|
||||
B2=$(bs_a)
|
||||
[ "$B1" = "$B2" ] || { echo "HARD-FAIL (DET-1): Bundle-State-Projektion nicht deterministisch (gleiche Extraktion zweimal ungleich)" >&2; exit 1; }
|
||||
case "$B1" in
|
||||
*alpha*) ;;
|
||||
*) echo "HARD-FAIL (DET-1): Bundle-State-Projektion enthaelt Wiki-Pfad-Projektion nicht (alpha fehlt)" >&2; exit 1;;
|
||||
esac
|
||||
echo " Bundle-State-Projektion (deterministisch, wiederextrahierbar):"
|
||||
echo "$B1" | sed 's/^/ /'
|
||||
echo "RESULT: PASS — DET-1: BUNDLE_STATE_DEFINITION — Bundle-State = deterministische Projektion des committeten Git-States ((a) Baum gg. Baseline, (b) Kandidaten-, (c) Plan-/Reihenfolge-/Entscheidungs-Outputs); Extraktion zweimal byte-identisch (AD-17h/A0-19); keine eigene Engine (D-3/AD-11)"
|
||||
|
||||
# =====================================================================================
|
||||
# DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum) — §5.14 Pkt. 2
|
||||
# =====================================================================================
|
||||
runlabel "DET-2: ZWEI_RUN_IDENTISCH (nicht-vakuum) — zwei unabhängige Läufe über demselben committeten Baum -> identische Bundle-States bis auf at; echte Content-Hashes (Witness weicht byte-weise von Baseline ab)"
|
||||
run2() { # $1 = Run-Name ; führt die Instruktion über dem committeten Baum aus
|
||||
isolate "$1"
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
# Plan-/Kandidaten-Output (§5.14 Pkt. 2: identische Plan-/Kandidaten-/Reihenfolge-Outputs)
|
||||
local plan
|
||||
plan="cand=alpha;form=update;graphic=aktualisieren;baseline=$BASE"
|
||||
# Mutations-Commit als ein commitierter Run-Ausgangszustand (unterschiedliches at je Run:
|
||||
# A0-20-Konvention — der at-Wert ist Wanduhr, NICHT Teil des deterministischen Vergleichs)
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s3
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: RUNAT
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
# at-Maskierung für den deterministischen Vergleich (§5.14 Pkt. 3): DET-2 demonstriert
|
||||
# die Identität der NICHT-at-Bestandteile (Plan-, log-, index-, alpha-Content + TREE) —
|
||||
# dafür wird die at-Zeile vor dem Hash auf ein festes Token normalisiert (die at-Varianz
|
||||
# selbst ist die benannte Ausnahme, A0-20; ihre Behandlung demonstriert DET-4 positiv und
|
||||
# negativ). Die Ausnahme ist dokumentiert, kein stiller Ausschluss; alle übrigen
|
||||
# Bestandteile gehen byte-identisch in den Hash ein.
|
||||
sed -i "s/^ at: .*/ at: AT/" wiki/alpha.md
|
||||
log_bullet "- Determinismus-Bestätigung (Zwei-Run, §5.14 Pkt. 2): $plan; Baseline $BASE"
|
||||
git add -A && git commit -qm "Run: alpha-Update ($1)"
|
||||
# --- Bundle-State projizieren (deterministisch): echte Content-Hashes (nicht-vakuum) ---
|
||||
echo "PLAN=$plan"
|
||||
echo "SHA_ALPHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)"
|
||||
echo "ABS_BASELINE=$(git show "$BASE:wiki/alpha.md" | sha256sum | cut -d' ' -f1)"
|
||||
echo "SHA_LOG=$(git show 'HEAD:wiki/log.md' | sha256sum | cut -d' ' -f1)"
|
||||
echo "SHA_INDEX=$(git show 'HEAD:wiki/index.md' | sha256sum | cut -d' ' -f1)"
|
||||
echo "TREE=$(git rev-parse 'HEAD:')"
|
||||
}
|
||||
echo "--- Lauf A (unabhängig) ---"
|
||||
SA=$(run2 run2a)
|
||||
echo "$SA"
|
||||
echo "--- Lauf B (unabhängig, über demselben committeten Baum) ---"
|
||||
SBV=$(run2 run2b)
|
||||
echo "$SBV"
|
||||
# Zwei unabhängige Läufe -> identische Bundle-States bis auf at
|
||||
[ "$(echo "$SA" | grep '^PLAN=')" = "$(echo "$SBV" | grep '^PLAN=')" ] || { echo "HARD-FAIL (DET-2): Plan-/Kandidaten-Outputs weichen ab (AD-17h/A0-19, §5.14 Pkt. 2)" >&2; exit 1; }
|
||||
[ "$(echo "$SA" | grep '^SHA_ALPHA=')" = "$(echo "$SBV" | grep '^SHA_ALPHA=')" ] || { echo "HARD-FAIL (DET-2): alpha-content-Hash weicht zwischen Runs ab (Byte-Differenz ausserhalb at, §5.14 Pkt. 3)" >&2; exit 1; }
|
||||
[ "$(echo "$SA" | grep '^SHA_LOG=')" = "$(echo "$SBV" | grep '^SHA_LOG=')" ] || { echo "HARD-FAIL (DET-2): log.md-Hash weicht ab" >&2; exit 1; }
|
||||
[ "$(echo "$SA" | grep '^SHA_INDEX=')" = "$(echo "$SBV" | grep '^SHA_INDEX=')" ] || { echo "HARD-FAIL (DET-2): index.md-Hash weicht ab" >&2; exit 1; }
|
||||
[ "$(echo "$SA" | grep '^TREE=')" = "$(echo "$SBV" | grep '^TREE=')" ] || { echo "HARD-FAIL (DET-2): Bundle-Baum nach at-Maskierung weicht ab (Soll: byte-identisch, §5.14 Pkt. 2)" >&2; exit 1; }
|
||||
# Nicht-vakuum-Witness: der produzierte Content weicht byte-weise von der Baseline ab
|
||||
ABS_A=$(echo "$SA" | grep '^ABS_BASELINE=' | cut -d= -f2)
|
||||
SHA_A=$(echo "$SA" | grep '^SHA_ALPHA=' | cut -d= -f2)
|
||||
[ -n "$ABS_A" ] && [ -n "$SHA_A" ] || { echo "HARD-FAIL (DET-2): Witness-Hashes leer (Vakuum — Assertion wertlos)" >&2; exit 1; }
|
||||
[ "$ABS_A" != "$SHA_A" ] || { echo "HARD-FAIL (DET-2): Witness == Baseline (vakuum — der Vergleich bewiese nichts; AD-17h/A0-19: echte Content-Identität gefordert)" >&2; exit 1; }
|
||||
echo " WITNESS: Baseline-alpha-SHA ($ABS_A) != Run-alpha-SHA ($SHA_A) — der Zwei-Run-Vergleich ist nicht-vakuum (Content weicht von Baseline ab, echte Hashes)"
|
||||
echo "RESULT: PASS — DET-2: ZWEI_RUN_IDENTISCH — zwei unabhängige Läufe über demselben committeten Baum produzieren identische Bundle-States (Plan-, log-, index-, alpha-Content-Hashes und Bundle-TREE byte-identisch, jeweils bis auf die benannte at-Ausnahme, die für den Vergleich maskiert ist); Vergleich nicht-vakuum (Witness vs. Baseline, echte Content-Hashes, §5.14 Pkt. 2, FT-10/AD-17h AC-1)"
|
||||
|
||||
# =====================================================================================
|
||||
# DET-3 ZWEI_RUN_ABWEICHUNG (§5.14 Pkt. 4)
|
||||
# =====================================================================================
|
||||
runlabel "DET-3: ZWEI_RUN_ABWEICHUNG — künstlich divergenter Lauf -> als AD-16-Klassifikationsdefekt klassifiziert und textuell benannt (kein Rauschen); korrigierter zweiter Lauf == erster Lauf (erneut bestätigt)"
|
||||
# Referenz-Run (deterministische Bundle-State-Projektion)
|
||||
run3_ref() {
|
||||
isolate run3ref
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s3
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: AT
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
git add -A && git commit -qm "Run: alpha-Update (Referenz)"
|
||||
echo "SHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)"
|
||||
}
|
||||
# Divergenter Run: veränderter Body-Content (RAUSCHEN-fremde Abweichung — ein deterministischer
|
||||
# Bestandteil weicht ab, hier eine andere Formulierung der Aussage) trotz gleichem Git-State+Input.
|
||||
run3_div() {
|
||||
isolate run3div
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s3
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: AT
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
Alpha wird um eine SYNCHRONE KOPPLUNG erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
git add -A && git commit -qm "Run: alpha-Update (divergent)"
|
||||
echo "SHA=$(git show 'HEAD:wiki/alpha.md' | sha256sum | cut -d' ' -f1)"
|
||||
}
|
||||
R3=$(run3_ref)
|
||||
D3=$(run3_div)
|
||||
echo "Referenz-Run: $R3"
|
||||
echo "Divergenter Lauf: $D3"
|
||||
SHA_REF=$(echo "$R3" | cut -d= -f2)
|
||||
SHA_DIV=$(echo "$D3" | cut -d= -f2)
|
||||
[ -n "$SHA_REF" ] && [ -n "$SHA_DIV" ] || { echo "HARD-FAIL (DET-3): Hashes leer (Vakuum)" >&2; exit 1; }
|
||||
if [ "$SHA_REF" = "$SHA_DIV" ]; then
|
||||
echo "HARD-FAIL (DET-3): der divergente Lauf blieb inspiziert ununterscheidbar (Abweichung nicht erkannt; §5.14 Pkt. 4 verlangt Klassifikation)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "NFR-4-Benennung (textuell): ZWEI_RUN_ABWEICHUNG — Lauf 'divergent' weicht ausserhalb der benannten at-Ausnahme vom Referenz-Lauf ab (unterschiedlicher Content-Hash $SHA_DIV != $SHA_REF bei gleichem Git-State + gleichem Eingabeset) => AD-16-Klassifikationsdefekt (FT-10/AD-17h-AC), kein Rauschen; der Run wird als Fehler im Klassifikations-Mechanismus behandelt, textuell benannt und korrigiert/zurückgerollt (§5.14 Pkt. 4, Zustands-Restaurations-Invariante §5.13 Pkt. 3)"
|
||||
echo "--- Korrigierter zweiter Lauf: rollt zurueck und wiederholt deterministisch (== Referenz) ---"
|
||||
rollback3() {
|
||||
git checkout -q "$BASE" -- wiki/ 2>/dev/null || true
|
||||
git reset -q --hard "$BASE"
|
||||
git clean -qfd wiki
|
||||
}
|
||||
rollback3
|
||||
R3B=$(run3_ref)
|
||||
SHA_REFB=$(echo "$R3B" | grep '^SHA=' | cut -d= -f2)
|
||||
[ "$SHA_REF" = "$SHA_REFB" ] || { echo "HARD-FAIL (DET-3): korrigierte Wiederholung weicht von der Referenz ab (AD-17h verletzt)" >&2; exit 1; }
|
||||
echo " BEFUND: korrigierter Lauf == Referenz-Lauf (SHA $SHA_REF) — der Vertrag ist nach Korrektur erneut bestätigt (Pkt. 4: 'Befund wird (ggf. als korrigierter zweiter Lauf) erneut bestätigt')"
|
||||
echo "RESULT: PASS — DET-3: ZWEI_RUN_ABWEICHUNG — künstlich divergenter Lauf wurde als AD-16-Klassifikationsdefekt erkannt (Content-Hash-Differenz bei gleichem State+Input — kein Rauschen, keine Umgebungs-Streuung) und textuell benannt (NFR-4); korrigierter zweiter Lauf reproduziert den Referenz-Bundle-State (Zustands-Restaurations-Invariante, §5.13 Pkt. 3; §5.14 Pkt. 4)"
|
||||
|
||||
# =====================================================================================
|
||||
# DET-4 AT_GAP_AUSNAHME (§5.14 Pkt. 3)
|
||||
# =====================================================================================
|
||||
runlabel "DET-4: AT_GAP_AUSNAHME — generated.at-Wanduhr-Gap ist die einzige benannte Ausnahme; zwei Runs mit unterschiedlichem at -> diff ausschließlich auf der at-Zeile; nach at-Maskierung byte-identisch; jede andere Differenz = AD-16-Klassifikationsdefekt"
|
||||
isolate det4
|
||||
cat > raw/alpha-v2.md <<'EOF'
|
||||
### S-3
|
||||
Evidenz v2: Alpha erweitert um eine synchrone Kopplung.
|
||||
EOF
|
||||
git add -A && git commit -qm "Evidenz v2"
|
||||
mkrun4() { # $1 = at-Wert — schreibt alpha mit EXAKT unterschiedlichen at-Werten
|
||||
cat > wiki/alpha.md <<EOF
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/alpha-v1.md
|
||||
id: s1
|
||||
- resource: raw/alpha-v2.md
|
||||
id: s3
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: $1
|
||||
---
|
||||
Das Alpha-Protokoll definiert eine deterministische Init-Sequenz (raw/alpha-v1.md#S-1).
|
||||
Alpha verwendet ausschließlich lokale Netze (raw/alpha-v1.md#S-2).
|
||||
Alpha wird um eine synchrone Kopplung erweitert (raw/alpha-v2.md#S-3).
|
||||
EOF
|
||||
}
|
||||
mkrun4 "2026-08-16T09:00:00Z"
|
||||
git add -A && git commit -qm "Run at=A"
|
||||
mkrun4 "2026-08-19T23:59:59Z"
|
||||
git add -A && git commit -qm "Run at=B"
|
||||
# diff zwischen beiden Run-Zuständen: ausschließlich die at-Zeile
|
||||
DABC=$(git diff HEAD~1 HEAD -- wiki/alpha.md)
|
||||
[ -n "$DABC" ] || { echo "HARD-FAIL (DET-4): die zwei Runs sind identisch — at-Gap nicht demonstriert (Vakuum)" >&2; exit 1; }
|
||||
non_at=$(echo "$DABC" | grep '^[-+][^-+]' | grep -v '^[-+] at: ' | grep -v '^[+-][+-]')
|
||||
if [ -n "$non_at" ]; then
|
||||
echo "HARD-FAIL (DET-4): der diff zwischen den zwei Runs betrifft BESTANDTEILE ausserhalb der at-Ausnahme:" >&2
|
||||
echo "$non_at" | sed 's/^/ /' >&2
|
||||
exit 1
|
||||
else
|
||||
echo " BEFUND: diff ausschließlich auf der at-Zeile (einzige benannte Ausnahme, §5.14 Pkt. 3)"
|
||||
fi
|
||||
echo "--- Nach at-Maskierung byte-identisch (restliche Bundle-Bestandteile) ---"
|
||||
H1=$(git show HEAD~1:wiki/alpha.md | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1)
|
||||
H2=$(git show HEAD:wiki/alpha.md | sed 's/^ at: .*/ at: AT/' | sha256sum | cut -d' ' -f1)
|
||||
[ "$H1" = "$H2" ] || { echo "HARD-FAIL (DET-4): nach at-Maskierung weichen die Runs ab (bestandteil über at hinaus; §5.14 Pkt. 3 verlangt byte-Identität der übrigen Teile)" >&2; exit 1; }
|
||||
echo " BEFUND: nach at-Maskierung byte-identisch (SHA $H1) — dokumentierte Ausnahme, kein stiller Ausschluss"
|
||||
echo "--- Negative Kontrolle: eine Differenz AUSSERHALB der Ausnahme ist ein AD-16-Klassifikationsdefekt ---"
|
||||
mkrun4 "2026-08-16T09:00:00Z"
|
||||
printf '\nAndere Aussagenformulierung.\n' >> wiki/alpha.md
|
||||
git add -A && git commit -qm "Run at=B' (Differenz im Body)"
|
||||
non_at2=$(git diff HEAD~1 HEAD -- wiki/alpha.md | grep '^[-+][^-+]' | grep -v '^[-+] at: ' | grep -v '^[+-][+-]')
|
||||
[ -n "$non_at2" ] || { echo "HARD-FAIL (DET-4): Body-Differenz wurde nicht erkannt (Negativkontrolle leer)" >&2; exit 1; }
|
||||
echo "NFR-4-Benennung (textuell): Body-Differenz ausserhalb der at-Ausnahme-Menge => AD-16-Klassifikationsdefekt (kein Rauschen), textuell benannt (§5.14 Pkt. 3/4)"
|
||||
echo "RESULT: PASS — DET-4: AT_GAP_AUSNAHME — generated.at-Wanduhr-Gap ist die einzige benannte Differenz im Bundle-State-Vergleich (A0-20-Konvention, §5.9 Pkt. 2/§5.10 Pkt. 8): zwei Runs diffieren ausschließlich in der at-Zeile, nach at-Maskierung byte-identisch; jede andere Differenz liegt ausserhalb der Ausnahme = AD-16-Klassifikationsdefekt (Negativkontrolle, §5.14 Pkt. 3)"
|
||||
|
||||
# =====================================================================================
|
||||
# DET-5 EM_DASH (§3.2 Pkt. 1b, §5.14 Pkt. 5)
|
||||
# =====================================================================================
|
||||
runlabel "DET-5: EM_DASH — Em-Dash — normalisiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen (§3.2 Pkt. 1b: Kollaps-Klasse [-–— _] -> -)"
|
||||
# Term-Basis mit Em-Dash / En-Dash / Bindestrich / Unterstrich / Leerzeichen-Separator
|
||||
EM=$(norm "wissen — relevanz")
|
||||
EN=$(norm "wissen – relevanz")
|
||||
HY=$(norm "wissen - relevanz")
|
||||
US=$(norm "wissen_relevanz")
|
||||
SP=$(norm "wissen relevanz")
|
||||
[ "$EM" = "$EN" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != En-Dash-Form" >&2; exit 1; }
|
||||
[ "$EM" = "$HY" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Bindestrich-Form" >&2; exit 1; }
|
||||
[ "$EM" = "$US" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Unterstrich-Form" >&2; exit 1; }
|
||||
[ "$EM" = "$SP" ] || { echo "HARD-FAIL (DET-5): Em-Dash-Form != Leerzeichen-Form" >&2; exit 1; }
|
||||
case "$EM" in
|
||||
*"—"*) echo "HARD-FAIL (DET-5): Em-Dash blieb in der canonischen Form erhalten (Kollaps-Klasse greift nicht)" >&2; exit 1;;
|
||||
esac
|
||||
echo " canonische Form (alle fünf Separatoren): '$EM'"
|
||||
echo " Em-Dash-Term kollabiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen (kein stiller Ausschluss, §3.2 Pkt. 1b)"
|
||||
echo "RESULT: PASS — DET-5: EM_DASH — Em-Dash — ist in der Kollaps-Klasse [-–— _] -> - (§3.2-Pkt.-1b-Ergänzung, §5.14 Pkt. 5): Term mit Em-Dash erhält dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen-Varianten — deterministisch, keine unterschiedliche canonische Form je Separator"
|
||||
|
||||
# =====================================================================================
|
||||
# DET-6 KOLLAPS (§3.2 Pkt. 1b, §5.14 Pkt. 5)
|
||||
# =====================================================================================
|
||||
runlabel "DET-6: KOLLAPS — Kollaps-Reichweite: Läufe (a--b), führende (-x), trailende (x-) -> Kollaps auf genau ein - + Trim (§3.2 Pkt. 1b)"
|
||||
[ "$(norm "a--b")" = "a-b" ] || { echo "HARD-FAIL (DET-6): Lauf a--b kollabiert nicht auf a-b (norm='$(norm "a--b")')" >&2; exit 1; }
|
||||
[ "$(norm "-x")" = "x" ] || { echo "HARD-FAIL (DET-6): führender Separator -x wird nicht getrimmt (norm='$(norm "-x")')" >&2; exit 1; }
|
||||
[ "$(norm "x-")" = "x" ] || { echo "HARD-FAIL (DET-6): trailender Separator x- wird nicht getrimmt (norm='$(norm "x-")')" >&2; exit 1; }
|
||||
[ "$(norm "a--b")" = "a-b" ] && [ "$(norm "a---b")" = "a-b" ] && [ "$(norm "---x---")" = "x" ] || { echo "HARD-FAIL (DET-6): Läufe/Trim-Kombination inkonsistent" >&2; exit 1; }
|
||||
echo " Läufe a--b -> '$(norm "a--b")'; führend -x -> '$(norm "-x")'; trailend x- -> '$(norm "x-")'"
|
||||
echo "RESULT: PASS — DET-6: KOLLAPS — Kollaps-Reichweite deterministisch (§3.2 Pkt. 1b, §5.14 Pkt. 5): Läufe kollabieren auf genau ein -, führende/trailende Separatoren werden getrimmt — glaubhaft re-executierbar, keine offene Reichweiten-Frage"
|
||||
|
||||
# =====================================================================================
|
||||
# DET-7 MATCH_SCOPE (§3.2 Pkt. 2a, §5.14 Pkt. 5)
|
||||
# =====================================================================================
|
||||
runlabel "DET-7: MATCH_SCOPE — Stufe a matcht ganze Wörter über den Body, exklusive YAML-Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen, §3.2 Pkt. 2a)"
|
||||
isolate det7
|
||||
# Concept: Body enthält "kopplung" als GANZES Wort; Frontmatter enthält "kopplung" als
|
||||
# Substring im Quell-Pfad raw/kopplung-v1.md (Frontmatter-Treffer-Wächter).
|
||||
cat > wiki/alpha.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/kopplung-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Alpha als Wissenseinheit: hier steht einzig das ganz Wort kopplung ohne weitere Bindung.
|
||||
EOF
|
||||
cat > wiki/gamma.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/gamma-v1.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Gamma: das Wort entkopplung belegt nur einenerweiternden Teil — kein eigener ganzer Treffer.
|
||||
EOF
|
||||
git add -A && git commit -qm "Stufe-a-Flex"
|
||||
# 1) Substring-Kontrolle: 'kopplung' (ganz) trifft alpha; 'opplung' (Substring) trifft nichts
|
||||
HIT1=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md)
|
||||
[ "$HIT1" = "alpha" ] || { echo "HARD-FAIL (DET-7): ganzes-Wort-Match 'kopplung' liefert '$HIT1' (Soll: alpha; Substring-/Gamma-Treffer unerlaubt)" >&2; exit 1; }
|
||||
HIT2=$(match_stufe_a "opplung" wiki/alpha.md)
|
||||
[ -z "$HIT2" ] || { echo "HARD-FAIL (DET-7): Substring-Match 'opplung' liefert einen Kandidaten (Stufe a matcht ganze Wörter, §3.2 Pkt. 2a)" >&2; exit 1; }
|
||||
# 2) Frontmatter-Exklusion: Suchbegriff exakt gleich einem Frontmatter-RESOURCE-Substring
|
||||
# (raw/kopplung-v1.md enthält 'kopplung') — zugleich im Body von alpha steht 'kopplung'
|
||||
# nur als ganzes Wort => Treffer genau EINMAL (alpha). Der Frontmatter ist exkludiert,
|
||||
# ein zusätzlicher Body-Treffer erzeugt keinen zweiten Kandidaten — wir prüfen, dass
|
||||
# das Frontmatter-Vorkommen keinen falschen Kandidaten (z. B. durch RegExp über die ganze
|
||||
# Datei inkl. Frontmatter) erzeugt. Konkret: wir legen einen zweiten Concept-Body VOR, der
|
||||
# 'kopplung' NUR im Frontmatter traegt (Quell-Pfad raw/kopplung-mirror.md) — Stufe a muss
|
||||
# ihn verwerfen, weil der Frontmatter exkludiert ist.
|
||||
cat > wiki/mirror.md <<'EOF'
|
||||
---
|
||||
type: concept
|
||||
sources:
|
||||
- resource: raw/kopplung-mirror.md
|
||||
id: s1
|
||||
generated:
|
||||
by: wow-compiler/0.1.0
|
||||
at: 2026-08-16T09:00:00Z
|
||||
---
|
||||
Gamma-Mirror ohne Kopplungs-Begriff im Bodytext.
|
||||
EOF
|
||||
git add -A && git commit -qm "Frontmatter-only-Kandidat"
|
||||
HIT3=$(match_stufe_a "kopplung" wiki/alpha.md wiki/gamma.md wiki/mirror.md)
|
||||
[ "$HIT3" = "alpha" ] || { echo "HARD-FAIL (DET-7): Frontmatter-Treffer erzeugt Kandidat (mirror) — Stufe a matcht nur Body, exklusive YAML-Frontmatter (§3.2 Pkt. 2a)" >&2; exit 1; }
|
||||
# 3) Direkte Formel-Probe der operativen Stufe-a-Formel (§3.2-Pkt.-2a-Zweistufen-Mechanik):
|
||||
# Sweep mit der Formel -> dann deterministischer Scope-Filter (ganze Wörter, Body, after second ---).
|
||||
SWEEP=$(grep -rl 'kopplung' --exclude=log.md wiki/ | LC_ALL=C sort)
|
||||
# Sweep-muss alpha, gamma (Substring entkopplung) und mirror (Frontmatter-only) finden —
|
||||
# der Scope-Filter verwirft gamma+mirror; als Kandidaten bleiben die Body-ganz-Wort-Treffer.
|
||||
FILTERED=$(for f in $SWEEP; do
|
||||
if match_stufe_a "kopplung" "$f" | grep -q .; then basename "$f" .md; fi
|
||||
done | LC_ALL=C sort | tr '\n' ' ')
|
||||
FILTERED="${FILTERED% }"
|
||||
[ "$FILTERED" = "alpha" ] || { echo "HARD-FAIL (DET-7): operative Stufe-a-Zweistufen-Mechanik liefert '$FILTERED' (Soll: alpha) — Scope-Filter unvollständig (§3.2 Pkt. 2a, §5.14 Pkt. 5)" >&2; exit 1; }
|
||||
echo " Direkte Formel-Probe: Sweep (grep -rl) -> Scope-Filter verwirft Substring-/Frontmatter-Treffer -> Kandidat: alpha"
|
||||
echo "RESULT: PASS — DET-7: MATCH_SCOPE — Stufe a matcht ganze Wörter (Wortgrenzen) über den Body, exklusive YAML-Frontmatter: Substring-Treffer ('opplung') und Frontmatter-only-Kandidaten (mirror) liefern keine Kandidaten (§3.2 Pkt. 2a, §5.14 Pkt. 5) — deterministischer Stufe-a-Scope, keine falschen Kandidaten; operative Zweistufen-Mechanik (Sweep+Filter) abgesichert"
|
||||
|
||||
# =====================================================================================
|
||||
# DET-8 ORPHAN (§5.10 Pkt. 8, §5.14 Pkt. 5)
|
||||
# =====================================================================================
|
||||
runlabel "DET-8: ORPHAN — neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer -> unzugeordnet, log.md-verwaist protokolliert (Datumsgruppe, <Baseline-Commit>), kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default)"
|
||||
isolate det8
|
||||
cat > raw/orphan-v1.md <<'EOF'
|
||||
### S-1
|
||||
Evidenz: gänzlich fremdes Thema, das in keinem bestehenden Concept-Body Wort-Treffer hat.
|
||||
EOF
|
||||
git add -A && git commit -qm "Verwaiste Evidenz"
|
||||
# Ziel-Pfad-Treffer-Prüfung: Term über die neuen Quellen enthält 'fremdes-thema' — kein
|
||||
# bestehender Concept-Body trifft (Stufe a, deterministisch); kein Update-Kandidat.
|
||||
CAND=$(match_stufe_a "fremdes" wiki/alpha.md wiki/gamma.md)
|
||||
[ -z "$CAND" ] || { echo "HARD-FAIL (DET-8): verwaiste Evidenz erzielt einen Ziel-Pfad-Treffer (Kein-Orphan erwartet)" >&2; exit 1; }
|
||||
# Reconcile-Orphan-Regel (§5.10 Pkt. 8 / §5.14 Pkt. 5): deterministisch aus dem committeten
|
||||
# Git-State (Zuwachs gg. <Baseline-Commit>) als verwaist befunden -> datumsgruppierter
|
||||
# log.md-Eintrag (Header YYYY-MM-DD, Quell-Pfad + <Baseline-Commit>); keine eigenständige Anlage.
|
||||
log_bullet "- Reconcile-Orphan (Story 3.8; §5.10 Pkt. 8 / §5.14 Pkt. 5): raw/orphan-v1.md (Zuwachs gg. <Baseline $BASE>) ohne Ziel-Pfad-Treffer — unzugeordnet, verwaist protokolliert; kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default, Epic-4-Interface)"
|
||||
echo "--- Kein Banner / keine stille Vorbearbeitung / keine eigenständige Anlage ---"
|
||||
if [ -f wiki/orphan.md ]; then
|
||||
echo "HARD-FAIL (DET-8): verwaiste Evidenz wurde eigenständig als Concept angelegt (verboten, AD-16-Default)" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Nur log.md ist mutiert (Orphan-Eintrag) — Erhaltungs-Invariante §5.9 Pkt. 5 (log.md ist
|
||||
# erlaubtes Mitglied des Diff-Selbsttest-Satzes); keine weiteren Mutations-Pfade.
|
||||
MUT=$( { git diff --name-only "$BASE" -- wiki/ ; git status --porcelain -- wiki/ | awk '{print $2}'; } | sed -e 's|^wiki/||' -e 's|\.md$||' | LC_ALL=C sort -u)
|
||||
[ "$MUT" = "log" ] || { echo "HARD-FAIL (DET-8): Verwaist-Run mutiert ausserhalb {log} — Pfade: [$MUT]" >&2; exit 1; }
|
||||
grep -qF "Reconcile-Orphan" wiki/log.md || { echo "HARD-FAIL (DET-8): Verwaist-Protokoll fehlt in log.md (§5.10 Pkt. 8)" >&2; exit 1; }
|
||||
grep -qF "## 2026-08-20" wiki/log.md || { echo "HARD-FAIL (DET-8): log.md-Eintrag nicht in Datumsgruppe (Vertrag §5)" >&2; exit 1; }
|
||||
grep -qF "$BASE" wiki/log.md || { echo "HARD-FAIL (DET-8): <Baseline-Commit> fehlt im Verwaist-Eintrag (§5.10 Pkt. 8, deterministisch auflösbar D-2)" >&2; exit 1; }
|
||||
echo " BEFUND: raw/orphan-v1.md bleibt in raw/ unangetastet (AD-3); log.md trägt datumsgruppierten Verwaist-Eintrag mit <Baseline-Commit>"
|
||||
echo "RESULT: PASS — DET-8: ORPHAN — neu committete raw/-Evidenz ohne Ziel-Pfad-Treffer bleibt unzugeordnet und wird als datumsgruppierter log.md-Eintrag (Quell-Pfad + <Baseline-Commit>) verwaist protokolliert (§5.10 Pkt. 8 / §5.14 Pkt. 5): kein Banner, keine stille Vorbearbeitung, keine eigenständige Anlage (AD-16-Default) — Erhaltungs-Invariante gewahrt (nur log.md mutiert)"
|
||||
|
||||
echo
|
||||
echo "===== Sandbox abgeschlossen (DET-1..DET-8) ====="
|
||||
echo "Sandbox-Root: $ROOT (loeschbar: rm -rf $ROOT)"
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
---
|
||||
title: 'Leasing & Dirty-Tree-Schutz für konkurrierende Producer operationalisieren (Story 3.5)'
|
||||
type: 'feature'
|
||||
created: '2026-08-19'
|
||||
status: 'done'
|
||||
review_loop_iteration: 0
|
||||
baseline_commit: fae648e688781d281fa668310e179fd002ba812f
|
||||
context:
|
||||
- _bmad-output/implementation-artifacts/epic-3-context.md
|
||||
---
|
||||
|
||||
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
|
||||
|
||||
## Intent
|
||||
|
||||
**Problem:** §0/§5.3 verankern die Commit-Boundary = Mutations-Boundary (AD-17f) als einzigen bisherigen Schutz. Es fehlt die **Koordinations-Dimension für konkurrierende Producer** (AD-17a..f, A0-12..A0-16, FR-2/FR-12): wie zwei Producer denselben Concept-Pfad nicht stillschweigend überschreiben, wie eine Lease auf `lease/<area>/<id>`-Branches mit Root-Scope und Merge-Base-Disziplin erworben wird, und wie fremde uncommittete Änderungen (Dirty Tree) geschützt statt als Nebenwirkung gelöscht werden. Der §7-Vorbehalt (`:357`) zeigt unbehoben auf Story 3.5/3.6.
|
||||
|
||||
**Approach:** Story 3.5 verankert die Leasing-/Dirty-Tree-Mechanik als neue Sektion **§5.11 „Leasing & Dirty-Tree-Schutz für konkurrierende Producer (Story 3.5)"** (nach §5.10, vor §6, Revision 3.0): deterministische Regeln für Lease-Akquise (Branch-Form `lease/<area>/<id>`, Lockfile, Merge-Base-Disziplin), Root-Scope-Lease (inkl. `log.md`, `index.md`, Root-Dateien), Dirty-Tree-Schutz (Pre-Mutation-Prüfung, Stash/Scratch-Zone, `log.md`-Dokumentation) und compiler-vermittelten Merge als AD-16-Pfad (kein stiller textueller Auto-Merge). Zusätzlich eine **re-executierbare Sandbox** (`sandbox-3-5/run-sandbox.sh`, Muster Story-3.4): demonstriert Lease-Akquise + Dirty-Tree-Schutz + Merge-Verweigerung an echten Git-Szenarien und assertet die Invarianten hart. Keine Inhalts-Mutation des realen Bundles (AD-3); kein neues `raw/`-Material; kein Standalone (D-3).
|
||||
|
||||
## Boundaries & Constraints
|
||||
|
||||
**Always:**
|
||||
- **Instruktions-Story (D-3):** Verankerung ausschließlich in `schema/compiler.md` §5.11 als deterministischer Text. Kein executable, kein Standalone, keine neue §7-Invaliditätsklasse, kein Change an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3).
|
||||
- **Leasing-Modell (AD-17a, A0-12):** Producer arbeiten auf `lease/<area>/<id>`-Branches; die Lease wird gegen einen **eindeutigen Commit-Object-Wert** (Merge-Base-Disziplin) akquiriert; ein **Lockfile** realisiert semantisch identisch in jedem Adapter — die Realisierung ist nicht pro Adapter frei wählbar.
|
||||
- **Root-Scope-Lease (AD-17b, A0-13):** Die Lease umfasst `wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien — nicht nur den mutierten Concept-Pfad.
|
||||
- **Kein textueller Auto-Merge (AD-17c, A0-14):** Zwei Branches mit ungleichem Inhalt am selben Concept-Pfad werden nie textuell automatisch gemerged; der Merge ist compiler-vermittelt und durchläuft die AD-16-Klassifikation mit explizitem `log.md`-Eintrag (Interface zu Epic 4).
|
||||
- **Dirty-Tree-Schutz (AD-17e/f, A0-16):** Vor jeder Mutation wird die Working Copy auf den mutierten Bereich geprüft; fremde uncommittete Änderungen werden geschützt (Stash/Scratch-Zone) und in `log.md` dokumentiert; Mutationen operieren nur auf Directory-/Commit-Ebene — Commit-Boundary = Mutations-Boundary.
|
||||
- **Determinismus (AD-17h/A0-19):** Lease-Akquise, Lockfile-Inhalte und Merge-Klassifikation folgen deterministisch aus dem committeten Git-State; gleicher Git-State + gleiche Eingabemenge → identische Koordinationsentscheidung.
|
||||
- **Struktur-Erhaltung (FR-6/AC-2, Story 3.3):** Die §5.9-Pkt.-2-Struktur-Erhaltungsregeln gelten je berührtem Pfad unverändert; Leasing erzwingt keine neuen Concept-Felder (Vertrag §3.1–§3.7-Subset unverändert).
|
||||
- **Erhaltungs-Invariante (AD-5/FT-6):** Der §5.9-Pkt.-5-Diff-Selbsttest gilt für Leasing-fähige Runs unverändert (Pfad-Menge ⊆ Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ Index).
|
||||
- `sprint-status.yaml`: Key `3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz` → **in-progress** (bei Implementierung).
|
||||
|
||||
**Ask First:** Inhalts-Mutation des realen Bundles (Demo per Sandbox) · AD-7d-Renames · §5.6-Linkform-Änderung · Validator-/Vertrags-/`raw/`-Change · Lease-Staleness/Recovery-Scope (3.6) · Lease-Bereich jenseits `wiki/` (AD-17b-Root-Scope) · neuer Concept-Frontmatter-Key für Lease-Metadaten.
|
||||
|
||||
**Never:** Änderungen an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3) · neue §7-Invaliditätsklasse · Standalone (D-3) · stiller textueller Auto-Merge (AD-17c) · stille Löschung fremder uncommitteter Änderungen (AD-17e) · Leasing außerhalb von Git-Branches/Lockfile · Embeddings/Vector im Compiler-Kern (AD-13) · Scope-Ausweitung auf Lease-Staleness/Recovery (Story 3.6).
|
||||
|
||||
## I/O & Edge-Case Matrix
|
||||
|
||||
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|
||||
|----------|--------------|---------------------------|----------------|
|
||||
| LEASE_AKQUISE | Producer bearbeitet Area, keine Lease vorhanden | Arbeits-Branch `lease/<area>/<id>` auf Merge-Base; Lockfile geschrieben; Root-Scope-Lease dokumentiert | Lockfile existiert bereits → Lease-Hold, kein Überschreiben |
|
||||
| LEASE_KONFLIKT | Zweiter Producer, Lockfile von Producer A vorhanden/Konflikt am selben Pfad | Kein stiller textueller Auto-Merge; compiler-vermittelter AD-16-Pfad mit `log.md`-Eintrag | Unentscheidbar → menschliche Eskalation (AD-17g) |
|
||||
| DIRTY_TREE_SCHUTZ | Fremde uncommittete Änderung im mutierten Bereich | Schutz (Stash/Scratch-Zone) + `log.md`-Dokumentation; keine Löschung | Screen-Artefakte beim Schutz → textuell benannt |
|
||||
| UNCOMMITTED_INPUT | `raw/`/`wiki/`-Working-Copy weicht von HEAD ab | Benannter Abbruch „published/committed Input erforderlich" (AD-17a) vor jeder Mutation | Keine Mutation, Bundle konsistent |
|
||||
| COMMIT_BOUNDARY | Run nach Mutation + Validierung | Commit-Boundary = Mutations-Boundary; Zwischenstände nie veröffentlicht | Validierungs-FAIL → Rollback §5.3 |
|
||||
|
||||
</frozen-after-approval>
|
||||
|
||||
## Code Map
|
||||
|
||||
- `schema/compiler.md` — **primär mutiert** (D-3):
|
||||
- Neue Sektion **§5.11** (nach §5.10, vor §6): Lease-Akquise (Branch-Form `lease/<area>/<id>`, Lockfile, Merge-Base-Disziplin — deterministisch, AD-17h), Root-Scope-Lease (AD-17b, A0-13), Dirty-Tree-Schutz (Pre-Mutation-Prüfung, Stash/Scratch-Zone, `log.md`-Dokumentation, AD-17e/f), compiler-vermittelter Merge als AD-16-Pfad (kein textueller Auto-Merge, AD-17c/A0-14), Commit-Boundary = Mutations-Boundary (§0/§5.3-Verweis unverändert), `log.md`-Eintragspflicht (Lease-Akquise, Schutz-Dokumentation, Merge-Klassifikation), Determinismus-Vertrag.
|
||||
- §7 (`:357`): Lease-Vorbehalt **auflösen** („→ §5.11 verankert (Story 3.5)").
|
||||
- §8 Revisionslog: **Revision 3.0** mit Abschlussklausel (AD-3, keine neue §7-Klasse, kein Standalone, kein Vertrags-Change; Lease-Staleness/Recovery bleibt → Story 3.6).
|
||||
- `_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh` — **neu** (re-executierbar, Muster Story-3.4-Sandbox): Mini-Git-Repo mit `wiki/`+`raw/`; Szenarien L1–L6 (Lease-Akquise auf `lease/<area>/<id>`-Branch mit Lockfile · Lockfile-Konflikt → Lease-Hold · Root-Scope-Lease inkl. `log.md`/`index.md` · Dirty-Tree-Schutz mit Stash/Scratch-Zone + `log.md`-Dokumentation · kein textueller Auto-Merge bei ungleichem Pfad-Inhalt · Commit-Boundary-Verletzung → HARD-FAIL) + **Negativ-Kontrollen** (fremde uncommittete Änderung wird nie gelöscht; `??`-Sicht auf Ghost-Diffs). Harte Pass/Fail-Assertionen (exit 1), Erhaltungs-Invariante, `assert_frontmatter` (Muster Story-3.4).
|
||||
- `wiki/log.md` — **append** (append-only, Vertrag §5): Story-3.5-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
|
||||
- `_bmad-output/implementation-artifacts/sprint-status.yaml` — **mutiert**: Key `3-5-…` → in-progress (→ done bei Story-Abschluss).
|
||||
- `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/…`, `raw/…` — **read-only** (AD-3). Keine Concept-Inhalts-Mutation (Demonstration per Sandbox).
|
||||
|
||||
## Tasks & Acceptance
|
||||
|
||||
**Execution:**
|
||||
- [x] `schema/compiler.md` — §5.11 Leasing-/Dirty-Tree-Sektion einfügen (Branch-Form, Lockfile, Merge-Base-Disziplin, Root-Scope, Dirty-Tree-Schutz, AD-17c-Merge-Pfad, Commit-Boundary-Verweis, log.md, Determinismus) · §7-Vorbehalt `:357` auflösen · §8 Revision 3.0; ohne Change an `wiki-compiler.md`/`validator.md`/`raw/`.
|
||||
- [x] `sandbox-3-5/run-sandbox.sh` — Leasing-Run-Demonstrator (Akquise, Konflikt, Root-Scope, Dirty-Tree-Schutz, Merge-Verweigerung, Commit-Boundary); harte Pass/Fail-Assertionen; Exit 0.
|
||||
- [x] `wiki/log.md` — Story-3.5-Eintrag (append-only): Verankerung, Sandbox-Nachweis, Statuswechsel, per-Datei-Validator-Verdikt.
|
||||
- [x] `sprint-status.yaml` — Key `3-5-…` → in-progress.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Given ein Producer, when er einen Bereich bearbeitet, then arbeitet er auf einem `lease/<area>/<id>`-Branch und akquiriert die Lease gegen einen eindeutigen Commit-Object-Wert (AC-1; AD-17a, A0-12) — §5.11, Sandbox L1.
|
||||
- Given eine vergebene Lease, when sie aktiv ist, then umfasst sie die Root-Scope inklusive `log.md`, `index.md` und aller Root-Dateien (AC-2; AD-17b, A0-13) — §5.11, Sandbox L3.
|
||||
- Given eine vorliegende uncommittete Fremdänderung im mutierten Bereich, when ein Producer mutieren will, then schützt er sie (Stash/Scratch-Zone) und dokumentiert den Vorgang in `log.md` (AC-3; AD-17e, A0-16) — §5.11, Sandbox L4, Negativ-Kontrolle „nie gelöscht".
|
||||
- Given zwei Branches mit ungleichem Inhalt am selben Concept-Pfad, when gemerged werden soll, then erfolgt kein stiller textueller Auto-Merge; die Auflösung ist compiler-vermittelt über AD-16 mit explizitem `log.md`-Eintrag (AC-4; AD-17c, A0-14) — §5.11, Sandbox L5.
|
||||
- Given die Instruktion, when geprüft, then bleiben `schema/validator.md`/`schema/wiki-compiler.md`/`raw/` unverändert (AD-3), keine neue §7-Klasse, kein Standalone (D-3), kein neuer Frontmatter-Key, Lease-Staleness/Recovery bleibt an Story 3.6 — Validator auf Ist-Bundle SUCCESS.
|
||||
|
||||
## Spec Change Log
|
||||
|
||||
- **Review-Loop 1 (2026-08-19, step-04; 3 Layer: blind-hunter / edge-case-hunter / verification-gap):** Autofix-`patch`-Findings in `schema/compiler.md` (§5.11) und `sandbox-3-5/run-sandbox.sh`; **kein bad_spec/intent_gap**, `review_loop_iteration` bleibt 0. **Text-Fixes (§5.11):** (1) Pkt.-1-Querverweis „Pkt. 3" → „Pkt. 2" (Root-Scope-Lease; dangling ref); (2) `UNCOMMITTED_INPUT`-Name als I/O-Matrix-Szenario-Deklaration geführt und **explizit mit der §5.9-P2-Element-(1)-`INPUT_UNCOMMITTED`-Prüfung gleichgesetzt** (kein zweiter, separater Abbruch-Pfad — Namens-Kollision aufgelöst); (3) Pkt. 6 um „neueste zuerst" (Vertrag-§5-Datumsgruppe, wie §5.9 Pkt. 4) ergänzt; (4) **Lease-Freigabe (Release)** als deterministischer Pkt.-1-Sub-Bullet + Pkt.-6-Eintragstyp (e) verankert — Lockfiles häufen sich nicht (Staleness-/Recovery-Aspekte der Freigabe verbleiben Story 3.6); (5) **testbares 3.5/3.6-Seam-Kriterium** (3.5 = committed-state-deterministisch / 3.6 = Zeit- bzw. Umgebungs-Zustands-Frage) ergänzt; (6) §7-Enum: AD-17d/A0-15 als **Norm-Rückverweis** gekennzeichnet (Staleness ist nicht Teil der §5.11-Auflösung); (7) §8-Revision 3.1. **Sandbox-Fixes:** (a) log-Helfer: Header-Bullet-**Paar** je Eintrag statt `\n##`-je-Zeile (Datumsgruppen-Format, Vertrag §5; kumulativer Append — neue D2-Szenario „Kumulative Koordinations-Aufzeichnung": 4 Entscheidungen in EINEM log.md, neueste zuerst); (b) L2-Lease-Hold hart über Exit-Status assertet (kein toter Code); (c) L4: `UNCOMMITTED_INPUT`-Abbruch-**Wirkung** hard asserted (HEAD unverändert, nur der fremde alpha-Pfad weicht ab, keine Mutation) + **Restore-Weg** (Scratch→Bundle) demonstriert; `^ M `-porcelain-Form werkzeugunabhängig; (d) L6: `BROKEN_HEAD`/`COMMIT_AFTER_RUN`-Tautologie beseitigt (`BUILD_HEAD`), Frontmatter-Validierungs-FAIL via Subshell (Exit-Propagation), Restore-Assertion korrekt; (e) L5: `log.md`-Klassifikations-Label als Header-Bullet asserted (kein bloßer Substring); (f) L3: `??`-Sicht nicht vacuous — neue legal angelegte `wiki/beta.md` als erlaubte Neu-Anlage durch die Invariante klassifiziert; (g) N1: „Pkt. 8" auf §5.10 Pkt. 8 (Orphan-Nachbarschaft) statt §5.9-Pkt.-8 quasi-finalisiert. **Defers** → `deferred-work.md` (holder_id-Quelle; baseline_commit-Resolver; eigener `# Log`-Stand vs. Akkumulator; Revisionsnummer-1.0-Defer). **Nachweis:** Sandbox Exit 0, 21 harte PASS-Assertionen (L1–L6, N1, D1, D2); AD-3 unverändert.
|
||||
|
||||
## Design Notes
|
||||
|
||||
**Warum §5.11, nicht §0-Erweiterung:** Die Commit-Boundary (§0/§5.3) ist der *Schutz bei Einzel-Validierung*; Leasing/Dirty-Tree ist die *Koordination vor/nach der Mutation an Branches* — eine eigenständige Dimension, die die Branch-/Lockfile-/Stash-Semantik beschreibt. §5.9/§5.10 (Update/Synthese) beschreiben die *Inhalts-Mechanik*; §5.11 beschreibt die *Werkzeug-Koordination*, die um jede Mutations-Form herum läuft. Das spiegelt den §7-Vorbehalt (`:357`) als eigenes Thema.
|
||||
|
||||
**Grenzfläche zu 3.6:** Story 3.5 deckt Akquise, Konflikt-Hold, Root-Scope und Dirty-Tree-Schutz. Staleness (TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery) bleibt der Story 3.6 vorbehalten — §5.11 verweist auf 3.6 und mutiert deren Mechanik nicht.
|
||||
|
||||
**Lease-Schutz ohne neue Fields:** Die Lease lebt in **Git/Datei-Ebene** (Branch-Name, Lockfile) — nicht in Concept-Frontmatter (Vertrag §3.1–§3.7 unverändert, keine neue §7-Klasse). Ein Lockfile realisiert semantisch identisch in jedem Adapter (A0-12); die Satzform präzisiert das Format, damit deterministisch (AD-17h).
|
||||
|
||||
## Verification
|
||||
|
||||
**Commands (re-executierbar, ab Workspace-Root):**
|
||||
1. `bash _bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh` — expected: alle Szenarien L1–L6 + Negativ-Kontrollen mit harten Pass/Fail-Assertionen, Erhaltungs-Invariante erzwungen, Exit 0.
|
||||
2. `grep -n "Leasing & Dirty-Tree-Schutz\|Revision 3.0" schema/compiler.md` — liefert die Leasing-Sektion + Revisionslog-Eintrag.
|
||||
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).
|
||||
|
||||
**Manual checks:**
|
||||
- §5.11 trägt Branch-Form `lease/<area>/<id>`, Lockfile, Merge-Base-Disziplin, Root-Scope-Lease, Dirty-Tree-Schutz (Stash/Scratch-Zone + log.md), AD-17c-Merge-Pfad, Commit-Boundary-Verweis, Determinismus; §7-Vorbehalt `:357` auf §5.11 aufgelöst; §8-Rev-3.0 mit Abschlussklausel; kein `validator.md`/`wiki-compiler.md`/`adapters/`/`raw/`-Diff; kein neuer Standalone (D-3) / keine neue §7-Klasse / kein neuer Frontmatter-Key; Lease-Staleness bleibt an 3.6; `wiki/log.md`-Eintrag datiert mit Story-3.5-Semantik + Sandbox-Nachweis + Statuswechsel + Verdikt; `sprint-status.yaml` konsistent.
|
||||
|
||||
## Suggested Review Order
|
||||
|
||||
**Leasing-Koordinations-Dimension (§5.11)**
|
||||
|
||||
- Einstiegspunkt: die verbindliche Verankerung der Koordinations-Dimension — Lease-Akquise, Root-Scope, Dirty-Tree-Schutz, Merge- und Determinismus-Vertrag
|
||||
[`compiler.md:303`](../../schema/compiler.md#L303)
|
||||
|
||||
- Pkt. 1 Lease-Akquise: Branch-Form, Lockfile, Merge-Base-Disziplin, deterministische Lease-Freigabe
|
||||
[`compiler.md:307`](../../schema/compiler.md#L307)
|
||||
|
||||
- Pkt. 2/3 Root-Scope-Lease + Dirty-Tree-Schutz: Stash/Scratch-Zone, UNCOMMITTED_INPUT-Abbruch (nicht-vacuous)
|
||||
[`compiler.md:312`](../../schema/compiler.md#L312)
|
||||
|
||||
- Pkt. 4–7: compiler-vermittelter Merge (AD-16), Commit-Boundary, log.md-Pflicht, Determinismus-Vertrag & 3.5/3.6-Seam
|
||||
[`compiler.md:317`](../../schema/compiler.md#L317)
|
||||
|
||||
- §8-Revisionslog „Revision 3.0" mit Abschlussklausel (AD-3, keine neue §7-Klasse, kein Standalone)
|
||||
[`compiler.md:416`](../../schema/compiler.md#L416)
|
||||
|
||||
**Nachweis: Sandbox & Story-Protokoll**
|
||||
|
||||
- Szenario-Matrix L1–L6 + Negativ-Kontrollen als re-executierbarer Beweis der Invarianten
|
||||
[`run-sandbox.sh:6`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L6)
|
||||
|
||||
- L1 Lease-Akquise / L2 Lease-Hold: hart assertierte Akquise- und Fehlerpfade
|
||||
[`run-sandbox.sh:269`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L269)
|
||||
|
||||
- L4 Dirty-Tree-Schutz: Pre-Mutation-Prüfung, Restore-Weg, „nie gelöscht"-Assertion
|
||||
[`run-sandbox.sh:379`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L379)
|
||||
|
||||
- L6 Commit-Boundary: Zwischenstand nie veröffentlicht, Validierungs-FAIL → Rollback
|
||||
[`run-sandbox.sh:476`](../../_bmad-output/implementation-artifacts/sandbox-3-5/run-sandbox.sh#L476)
|
||||
|
||||
- Story-3.5-`wiki/log.md`-Eintrag: Verankerung, Sandbox-Nachweis, Statuswechsel, Verdikt (append-only)
|
||||
[`log.md:3`](../../wiki/log.md#L3)
|
||||
|
||||
- 4 Defers (holder_id-Quelle, baseline_commit-Merge-Base, Sandbox-Akkumulator, native `git stash`) — Homes Story 3.6/3.8
|
||||
[`deferred-work.md:416`](../../_bmad-output/implementation-artifacts/deferred-work.md#L416)
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
---
|
||||
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)
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
---
|
||||
title: 'Story 3.7 — Reason/Mutate-Trennung & Konsistenz-Endzustand sicherstellen'
|
||||
type: 'feature'
|
||||
created: '2026-08-20'
|
||||
status: 'done'
|
||||
baseline_commit: '861e65f628001ffe8da05d0fd6834b7bb2689202'
|
||||
review_loop_iteration: 1
|
||||
context:
|
||||
- '_bmad-output/implementation-artifacts/epic-3-context.md'
|
||||
---
|
||||
|
||||
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
|
||||
|
||||
## Intent
|
||||
|
||||
**Problem:** AD-6/A0-7 trennen einen Compilation Run **logisch** in Analyse → Änderungsplanung → Mutation → Validierung, doch die Compiler-Instruktion führt die Phasen-Trennung nirgends explizit als **durchsetzbare Disziplin** — die feste Ablaufstruktur in §0 zählt nur Phasen (0)–(5), und die Plan-Vorprüfung (P2-Block) ist ein Baustein in §5.9 Pkt. 6, ohne als **Änderungsplanungs-Phase mit Endzustands-Garantie** ausgewiesen zu sein. Folge: Ein teilweise fehlgeschlagener Run könnte Zwischenstände hinterlassen, deren Konsistenz (AD-6: „Der beobachtbare Endzustand muss ein konsistentes Bundle sein") nur implizit über §6-Rollback abgesichert ist. A0-7-AC-2: „**Given** ein Fehler während der Mutation, **When** der Run abbricht, **Then** bleibt der beobachtbare Endzustand des Bundles konsistent".
|
||||
|
||||
**Approach:** Neue Sektion **§5.13 „Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)"** (nach §5.12, vor §6): die **logische Phasen-Disziplin** von AD-6 verbindlich in die Instruktion heben — der P2-Vorprüf-Block (§5.9 Pkt. 6) wird als **Änderungsplanung** (Analyse-Ergebnis → konsistenter Plan: Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheits-Liste, Struktur-Erhaltung) institutionalisiert; die §§5/§5.9/§5.10-Mutationsmechanik wird als **Mutationsphase** unter **Veränderungs-Sperre nach Phasenabschluss** (Plan freeze), die §§6/§5.3-Rollback-Maschinerie als **Validierungsphase** mit **Zustands-Restaurations-Invariante** (Post-Rollback-Zustand == Baseline) geschlossen. **Ohne eigene Workflow Engine** (D-3, AD-6): die Trennung ist eine textuelle Ausführungs-Disziplin in einer Session. Endzustands-Konsistenz wird als **beobachtbare, über Git-State überprüfbare Eigenschaft** fixiert — nach einem abgebrochenen/fehlgeschlagenen Run ist der Bundle-Zustand der Baseline (oder der einer abgeschlossenen, valide committeten Mutation).
|
||||
|
||||
## Boundaries & Constraints
|
||||
|
||||
**Always:**
|
||||
- Nur `schema/compiler.md` (neue §5.13 + §7-Auflösung + §8 Revisionslog-Revision 3.2) mutiert (D-3); `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` read-only (AD-3). Kein Standalone (D-3), keine eigene Workflow Engine (AD-6), keine neue §7-Invaliditätsklasse, kein Vertrags-Change, kein neuer Prädikat-/Format-Key, kein neuer Frontmatter-Key.
|
||||
- Deterministisch aus dem committeten Git-State ableitbar (AD-17h/A0-19): Plan-Inhalt, Phasen-Reihenfolge, Rollback-Trigger und Post-Zustand; **keine Wanduhr/`now`-Steuerung** der Phasen-Disziplin (weder Planung noch Rollback hängen von Kalenderzeit ab).
|
||||
- Endzustands-Konsistenz ist eine **beobachtbare, über Git prüfbare** Eigenschaft: Post-Rollback-Diff leer gg. Baseline bzw. Bundle == valide committeter Zustand des aktuellen Runs (Commit-Boundary = Mutation-Boundary, AD-17f, §0/§5.3).
|
||||
- Die **vier** AD-6-Phasen decken sich **logisch** mit der §0-Ablaufstruktur (Analyse ≈ §1/§3.2, Änderungsplanung ≈ §5.9 Pkt. 6-P2-Block, Mutation ≈ §§4–5, Validierung ≈ §6); keine neue $Phasen-Nummerierung im §0-Listentext, §0 bleibt die deterministische Ausführungs-Folge.
|
||||
|
||||
**Ask First:**
|
||||
- Einführung einer echten Planungs-Artefakt-Datei (separater Plan als persistentes Verzeichnis-Artefakt) statt des textuell festgehaltenen P2-Block-Plans — wäre eine neue Datei-/Baum-Regel außerhalb der Erhaltungs-Invariante.
|
||||
- Änderung des §0-Phasen-Listentextes (Umformulierung der festen Folge) oder zusätzliche Phase — §0 bleibt Takt-Quelle; §5.13 ordnet nur logisch zu.
|
||||
- Plan-freeze mit maschinellem Vergleich geplanter vs. tatsächlicher Mutationen über den §5.9-Pkt.-5-Diff-Selbsttest hinaus.
|
||||
|
||||
**Never:**
|
||||
- `raw/`-Inhalte verändern (AD-3); uncommittete fremde `wiki/`-Änderungen löschen (AD-17e); textuelle Auto-Merge bei Branch-Konvergenz (AD-17c); Wanduhr/`now`-Zeitstempel als Phasen- oder Rollback-Steuer-Größe; eigener `# Log`-Stand in der Registry (`wiki/log.md`-Akkumulator ist alleiniger Aufzeichnungs-Ort, Vertrag §5); Standalone-Compiler/eigene LLM-Runtime (D-3, AD-11); Änderung an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`.
|
||||
|
||||
## I/O & Edge-Case Matrix
|
||||
|
||||
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|
||||
|----------|--------------|---------------------------|----------------|
|
||||
| PLAN_BEABSICHTIGT | erkannte Erkenntnis + bestehende Reconcile-Kandidaten; P2-Block-Ausführung | konsistente Änderungsplanung: Input-Zustand ⊇ Zuwachs committet, Ziel-Pfade ⊆ Kandidaten∪Neu-Anlage, Quellen-Existenz (EC-1), Betroffenheits-Liste (§3 Pkt. 2), Struktur-Erhaltung; Plan wird textuell festgehalten | Plan-Defizit benannt (NFR-4); fehlgeschlagene Vorprüfung ⇒ keine Mutation (Kette: Abbruch vor Mutationsphase) |
|
||||
| PLAN_FREEZE | Mutationsphase beginnt | Änderungsplanung ist abgeschlossen; **Veränderungs-Sperre**: kein Plan-Gegenstand außerhalb der erlaubten Pfad-Menge (Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ `index.md`) wird mutiert | Verletzung = Ghost-Diff (§5.9 Pkt. 5): zurückrollen, textuell benannt |
|
||||
| MUTATION_ABBRUCH | Fehler während der Mutationsphase | Run bricht ab; beobachtbarer Endzustand: **Zustands-Restaurations-Invariante** — Post-Rollback-Diff gg. Baseline leer / Zustand == Baseline; `raw/` unverändert (AD-3) | §5.3-Pkt.-3/§6-Pkt.-3-Rollback-Mechanik greift; Ghost-Diff-Rollback (§5.9 Pkt. 5) greift; beides deterministisch aus Git-Teilen |
|
||||
| MUTATION_TEILFOLGE | mehrere geplante Mutationen, nur ein Teil ausgeführt | Rollback stellt Baseline wieder her; kein Teilerfolg wird als fertige Mutation veröffentlicht (Commit-Boundary = Mutation-Boundary) | Textuelle Failure-Benennung (NFR-4); Phasen-Disziplin unverändert |
|
||||
| VALIDATION_FAIL | Validierung meldet FAIL | Run als gescheitert; §6-Pkt.-3-Rollback; **keine** weiteren Mutationen; Endzustand konsistent | Rollback gemäß §5.3/§6; `raw/` unverändert |
|
||||
| VALIDATION_SUCCESS | alle `wiki/`-Dateien SUCCESS | Mutationen als Ganzes committen; Konsistenz = valide committeter Zustand; §6-Pkt.-4-Ausführungs-Nachweis | Commit erst nach Diff-Selbsttest ohne Ghost-Diff (§5.9 Pkt. 5); kein Wanduhr-Zeitstempel |
|
||||
| KEINE_EIGENE_ENGINE | Anforderung „keine separate Workflow Engine" (AD-6) | logische Trennung in einer Session, kein neuer Prozess/Server/MCP | D-3/AD-11 unverändert; nichts Steuerndes wird gebaut |
|
||||
|
||||
## Code Map
|
||||
|
||||
- `schema/compiler.md` — **primär mutiert** (D-3): neue Sektion **§5.13** „Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)" (nach §5.12, vor §6; Fixierung: §5.12-Ende Z. 341, §6-Start Z. 364 — neue Sektion zwischen beide, §5.13 Z. 343–362); §0-Phasen-Listentext **unverändert** (Takt-Quelle; §5.13 ordnet nur logisch zu); §5.9-Pkt.-6-P2-Block **Wortlaut-unverändert**, wird als Änderungsplanung referenziert (Rückverweis, keine Doppel-Instruktion); §6-Pkt.-3/§5.3-Rollback **Wortlaut-unverändert**, als Validierungsphase + Zustands-Restaurations-Invariante geschlossen; §7 (`Z. 390ff.`) Reason/Mutate-Vorbehalt **als neuer §7-Bullet** „in §5.13 verankert (Story 3.7)" (AD-6, A0-7, AC-1/2/3/4); §8-Normreferenz-`AD-6` (`Z. 430`) von reiner Story-Zuordnung auf **§5.13-Anker** angehoben. Anker (IST-Zeilen nach Implementierung): §5.13-Sektion Z. **343**; §7-Bullet nach Z. **420** (nach „Relevanzbestimmung"-Bullet); §8-AD-6 in Z. **430**; Revisionslog-Eintrag **Revision 3.2** nach Z. **461**.
|
||||
- `wiki/log.md` — **append** (append-only, Vertrag §5): Story-3.7-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
|
||||
- `_bmad-output/implementation-artifacts/sandbox-3-7/run-sandbox.sh` — **neu** (re-executierbar, Muster sandbox-3-6, Exit 0): Szenarien CONSIST-1..CONSIST-7 (s. Design Notes), harte Pass/Fail-Assertionen, Erhaltungs-/Restaurations-Invariante erzwungen, keine Berührung des realen Ist-Baums.
|
||||
- `_bmad-output/implementation-artifacts/sprint-status.yaml` — **mutiert**: `3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell` `backlog` → `review` (Implementierungs-Flip `in-progress` + Review-Start-Flip `review` im selben Commit; finaler `done`-Flip im Step-05-Status-Sync); `last_updated` (Format `MM-DD-YYYY HH:MM`, HEAD-Präzision).
|
||||
- `_bmad-output/implementation-artifacts/deferred-work.md` — **append** (append-only): ggf. Story-3.7-Defers (z. B. maschineller Plan-vs-Ist-Vergleich über den §5.9-Pkt.-5-Diff-Selbsttest hinaus), nach Prüfung der vorhandenen Defer-Liste (keine bereits auf Story 3.7 gedeuteten Einträge; Stand: keine Home-3.7-Defers vorhanden).
|
||||
- `_bmad-output/implementation-artifacts/epic-3-context.md`, `spec-3-6-…md` — **read-only** (continue-context; Kette unverändert).
|
||||
|
||||
## Tasks & Acceptance
|
||||
|
||||
**Execution:**
|
||||
- [x] `schema/compiler.md` — §5.13 einfügen (nach §5.12, vor §6); §0/§5.9-Pkt.-6/§6 Wortlaut-unverändert referenzieren; kein neuer Prädikat-/Format-/Frontmatter-Key
|
||||
- [x] `schema/compiler.md` — Revision 3.2 in §8 belegen (AD-6/A0-7 auf §5.13-Anker, Abschlussklausel) und §7-Bullet Reason/Mutate in §5.13-Verankerung
|
||||
- [x] `_bmad-output/implementation-artifacts/sandbox-3-7/run-sandbox.sh` — Szenarien CONSIST-1..7 (Plan-Erzeugung, Freeze-Sperre, Rollback-Restaurations-Invariante, Teilerfolg, Validierungs-Fail, Success-Commit, keine Engine), Exit 0
|
||||
- [x] `_bmad-output/implementation-artifacts/deferred-work.md` — ggf. Story-3.7-Defers append-only; keine Spuren in bereits vorhandenen Einträgen (Stand: keine Home-3.7-Defers — nichts anzuhängen, wie per README)
|
||||
- [x] `wiki/log.md` — (Implementierung) Story-3.7-Eintrag, `sprint-status.yaml` → in-progress; Review-Abschluss `done` im Review-Schritt (Workflow-Konvention)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Given ein Run, when er Änderungen plant, then erzeugt er zunächst eine konsistente Änderungsplanung (Analyse → Reconcile → Plan Changes → Mutate → Validate), textuell festgehalten (AD-6, A0-7; AC-1).
|
||||
- Given ein Fehler während der Mutation, when der Run abbricht, then bleibt der beobachtbare Endzustand des Bundles konsistent — Post-Rollback-Diff gg. Baseline leer, `raw/` unverändert (AD-6; AC-2, I/O-Matrix MUTATION_ABBRUCH).
|
||||
- Given ein Run, when er abgeschlossen ist, then wurden alle geplanten Mutations-Validierungen erfolgreich durchlaufen und die Mutationen als Ganzes committet (A0-7; AC-3, VALIDATION_SUCCESS).
|
||||
- Given die Architektur-Anforderung, when umgesetzt, then ist die Trennung logisch, ohne eigene Workflow Engine (AD-6; AC-4, KEINE_EIGENE_ENGINE).
|
||||
|
||||
## Spec Change Log
|
||||
|
||||
- **2026-08-20 (Step-04-Review, Review-Loop 2, bmad-code-review 4 Layer; Nutzer D1/D2/D3 = 1/1/1):** keine Änderung am `<frozen-after-approval>`-Intent (keine intent_gap/bad_spec-Verdikt). Doku-Stellen nachgeführt: Code-Map `sprint-status`-Eintrag `backlog → in-progress` → **`backlog → review`** (Impl.-Flip + Review-Start-Flip im selben Commit, D2); SRO-Anker-Text entsprechend. `### Review Findings` (3 Decision / 7 Patch / 3 Defer) angehängt; alle Decision- und Patch-Items nach der Patch-Runde abgehakt. Sandbox-Patch-Nachweise und die 3 Defers siehe `wiki/log.md`-Review-Loop-2-Bullet (2026-08-20) und `deferred-work.md`-Sektion „Deferred from: code review of … (Story 3.7, 2026-08-20)". `review_loop_iteration: 1`.
|
||||
|
||||
</frozen-after-approval>
|
||||
|
||||
### Review Findings (bmad-code-review, 2026-08-20 — Review-Loop 2, 4 Layer: blind-hunter / edge-case-hunter / verification-gap / acceptance-auditor)
|
||||
|
||||
**[Decision]** (offen — Nutzer-Entscheidung erforderlich):
|
||||
- [x] [Review][Decision] (gelöst: 1) log.md-Protokoll-Widerspruch + fehlender deferred-work-Append — das done-Bullet (log.md:4) behauptet eine „Delokalisierungs-Doku (deferred-work.md append-only: Terminologie-Drift `INPUT_UNCOMMITTED`/`UNCOMMITTED_INPUT` … Misch-Run-Coverage Neu-Anlage+Update → Story 3.8)", in `deferred-work.md` existiert aber **kein** Story-3.7-Eintrag (grep-verifiziert); das in-progress-Bullet (log.md:5) sagt „Keine Story-3.7-Defers" — die beiden Aufzeichnungen widersprechen sich. Optionen: (1) deferred-work-Append nachtragen (done-Bullet wird wahr); (2) done-Bullet umformulieren (keine Delokalisierungs-Doku behaupten, „keine Defers" bleibt wahr). [wiki/log.md:4-5]
|
||||
- [x] [Review][Decision] (gelöst: 1) sprint-status `backlog → review` im Diff vs. dokumentiertes `backlog → in-progress` — `git diff 861e65f..HEAD` zeigt Key `3-7-…` = `review`; Code-Map, SRO-Anker-Text, log.md-in-progress-Bullet und §8-Revision-3.2-Klausel dokumentieren alle `backlog → in-progress`. Optionen: (1) `review` behalten (Review-Start-Flip, File-Header-Konvention) + Dokumentation korrigieren; (2) auf `in-progress` setzen (Diff-Getreue) — der Step-04/05-Status-Sync setzt den Endwert ohnehin neu. [sprint-status.yaml:60]
|
||||
- [x] [Review][Decision] (gelöst: 1) p2_plan-Element-(1)-Scope — Wiedererwägung der dev-intern abgelehnten E1: `p2_plan` prüft unscoped `git status --porcelain` (ganzer Tree), §5.9 Pkt. 6 Element (1) scope-t auf „Working-Copy von `raw/` und `wiki/` gegen HEAD". Das done-Bullet dokumentiert die Ablehnung („Sandbox läuft nur auf sauberen Bäumen") — in den IST-Szenarien ist der Scope-Unterschied wirkungslos; bei einer Sandbox-Erweiterung (untracked-State außerhalb raw/+wiki/) würde die Demo-Funktion abbrechen, wo die Instruktion es zuließe. Optionen: (1) Scope-Fix `git status --porcelain -- raw wiki` (E1 wiedereröffnen); (2) Ablehnung aufrechterhalten (dismiss); (3) Defer an Sandbox-Härtungsrunde. [run-sandbox.sh:253]
|
||||
|
||||
**[Patch]** (offen):
|
||||
- [x] [Review][Patch] (angewendet) CONSIST-1-EC-1-Negativkontrolle nicht isoliert: `rm -f raw/alpha-v2.md` verschmutzt `raw/` (untracked deletion) → Element (1) (uncommitteter Input) feuert zuerst, Element (3) (Quellen-Existenz) wird nie allein geübt — die BEFUND-Zeile benennt EC-1, der Trigger ist aber Element (1). Fix: Löschung committen (`git rm -q` + commit), damit Element (3) am sauberen Baum isoliert feuert [run-sandbox.sh:343-350]
|
||||
- [x] [Review][Patch] (angewendet) CONSIST-5-`raw/`-SHA-Assertion tautologisch: `raw_sha=$(sha256sum raw/alpha-v1.md …)` wird mit derselben Datei verglichen — immer wahr, die AD-3-Prüfung beweist nichts. Fix: gg. `git show "$BASE:raw/alpha-v1.md"` vergleichen (Muster CONSIST-4) [run-sandbox.sh:481-482]
|
||||
- [x] [Review][Patch] (angewendet) CONSIST-6-SUCCESS-Demo committiert einen Zustand, der die §5.9-Update-Disziplin verletzt: die Body-Zeile zitiert `(raw/alpha-v2.md#S-3)`, die `sources`-Nachführung fehlt (im Kommentar als „gehört §5.9/§5.10" eingeräumt) — als Referenz-Demo eines VALIDATION_SUCCESS-Runs misleitet. Fix: Demo-Mutation um den `sources`-Eintrag ergänzen, damit der committete Zustand gültig ist [run-sandbox.sh:496-506]
|
||||
- [x] [Review][Patch] (angewendet: Doku-Block — Voll-Rollback = §6-Pkt.-3-Teilzustand, Sandbox-Abweichung begründet) `rollback()` löscht **alle** untracked `wiki/`-Dateien (`git reset --hard` + `git clean -qfd wiki`), während der Ghost-Diff-Rollback (§5.9 Pkt. 5) nur die betroffenen Pfade wiederherstellt und AD-17e fremde uncommittete Änderungen schützt (Spec-Never: „uncommittete fremde wiki/-Änderungen löschen"). In den IST-Szenarien wirkungslos, aber die demonstrierte Rollback-Mechanik widerspricht der Instruktion, die sie demonstrieren soll. Fix: `git clean` auf Nicht-erlaubte Pfade begrenzen oder die bewusste Sandbox-Abweichung dokumentieren [run-sandbox.sh:299-313]
|
||||
- [x] [Review][Patch] (angewendet: CONSIST-2 Misch-Run + `+`-Neu-Anlage in `p2_plan` + Absenz-Assertion) Neu-Anlage-Zweig ungetestet: kein Szenario übt den Plan-Freeze-Eintrag „∪ Neu-Anlage-Zielpfade"; der `assert_restored`-Neu-Anlage-Guard (`git cat-file -e "$BASE:wiki/$p"`-Negativzweig) ist toter Code. Fix: CONSIST-2 um einen geplanten Neu-Anlage-Pfad (`p2_plan` unterstützt den Marker `neu`) erweitern, Post-Rollback-Absenz asserten [run-sandbox.sh:166-169]
|
||||
- [x] [Review][Patch] (angewendet) Tippfehler: „porcelan" (Z. 151), „NFR-4-texuelle Benennung" (Z. 237), „Ferien-Zustand" (Z. 571) [run-sandbox.sh:151,237,571]
|
||||
- [x] [Review][Patch] (angewendet) §5.13 Pkt. 7: „der Determinismus-Vertrag (AD-17h/A0-19) gilt für … und Post-Zustand" ohne den dokumentierten `generated.at`-Wanduhr-Gap — §5.10 Pkt. 8 hält das Gap ausdrücklich als offene A0-20-Konvention (zwei unabhängige Runs → verschiedene `at` → Post-Zustand weicht ab). Fix: Ausnahme nachtragen („… Post-Zustand — mit dem dokumentierten `generated.at`-Wanduhr-Gap, §5.10 Pkt. 8") [schema/compiler.md:362]
|
||||
|
||||
**[Defer]**:
|
||||
- [x] [Review][Defer] CONSIST-7-Label überschätzt: „identische Plan-/Rollback-/State-Outputs" — im Zwei-Run-Vergleich werden nur Plan-Outputs (`plan_sha`/`plan_state`) verglichen; der Rollback-/State-Nachweis (s7c) läuft einmalig, `log_sha` ist dokumentierte Baseline-Konstante. Die non-vakuum-Kern-Assertion (`plan_sha` byte-identisch) ist real; das Label ist breiter als die Assertion [run-sandbox.sh:516,572] — deferred (Label-Präzisierung; Home: Sandbox-Härtungsrunde)
|
||||
- [x] [Review][Defer] A0-20-Post-Zustand-Negativkontrolle in der Sandbox fehlt: §5.13 Pkt. 3/7 bindet den Determinismus-Vertrag an den Post-Zustand, der `generated.at`-Wanduhr-Gap macht den Post-Zustand über unabhängige Runs nicht byte-identisch — die Sandbox testet den Gap weder negativ noch positiv [run-sandbox.sh:532-568] — deferred (Home: Story 3.8, A0-20-Home, §5.10-Pkt.-8-Präzedenz)
|
||||
- [x] [Review][Defer] Pre-existing-Zeitpunktswort-Fuge: §5.9 Pkt. 6 „am **Anfang der Mutationsphase**" (Z. 287) vs. gefrorene I/O-Matrix + §5.13 „Abbruch **vor** der Mutationsphase" — §5.9 ist in Story 3.7 Wortlaut-unverändert (Rückverweis-Vertrag), die Fuge ist hier nicht ohne Ask-First schließbar [schema/compiler.md:287,356] — deferred, pre-existing (Home: spätere Instruktionsrunde)
|
||||
|
||||
## Design Notes
|
||||
|
||||
**Warum §5.13 als eigene Sektion, nicht §0-Umbau?** §0 (Z. 13) ist die **deterministische Takt-Folge** — (0)..(5) in fester Reihenfolge, das Rückgrat der Instruktion. AD-6 trennt **logisch**, ohne neue Workflow-Engine; die **vier** AD-6-Phasen sind eine **konsolidierende Klassifikation** derselben Ablaufstruktur (Analyse ≈ §1/§3.2, Änderungsplanung ≈ §5.9 Pkt. 6-P2-Block, Mutation ≈ §§4–5, Validierung ≈ §6). Ein Umbau von §0 allein würde die bestehenden Story-Statements (§5.9/§5.10/§5.11/§5.12: „dieselben Phasen §0: Interpretieren → Reconcile → Synthetisieren → Mutieren → Validieren") brechen. Deshalb: §5.13 referenziert den §0-Wortlaut **unverändert** und bindet die AD-6-Nomenklatur als Ausführungs-Disziplin an die bestehende Struktur — Review-Layer prüfen die Fugen-Identität (keine Um-Nummerierung im §0-Listentext, keine neue Phase).
|
||||
|
||||
**Der P2-Block (§5.9 Pkt. 6) ist die natürliche Änderungsplanung.** Bereits vorhanden und reproduzierbar: (1) Input-Zustand (AD-17a), (2) Ziel-Pfade, (3) Quellen-Existenz, (4) Betroffenheits-Liste (§3 Pkt. 2), (5) index.md-V-1, (6) Struktur-Erhaltungs-Check. §5.13 macht diese Vorprüfung zur **Änderungsplanung der AD-6-Kette** und ergänzt zwei institutionalisierte Eigenschaften: den **Plan-Freeze** (Veränderungs-Sperre — nach Phasenabschluss wird kein Pfad außerhalb der erlaubten Menge berührt, Kopplung an die §5.9-Pkt.-5-Diff-Probe) und den **Plan-Nachweis** (der Plan wird als Teil der textuellen Ausführungsdisziplin festgehalten — kein Artefakt-File, keine Erhaltungs-Invarianten-Ausweitung).
|
||||
|
||||
**Die Konsistenz-Garantie ist bereits vorhanden — §5.13 macht sie beobachtbar.** §6-Pkt.-3/§5.3-Pkt.-3-Rollback und der Ghost-Diff-Rollback (§5.9 Pkt. 5, „AD-6-Backstop") existieren; die Anforderung „Endzustand konsistent" ist aber bislang eine erzählte Eigenschaft, keine **git-überprüfbare Invariante**. §5.13 definiert die **Zustands-Restaurations-Invariante**: nach Abbruch/FAIL ist `git diff` gegen die Baseline leer (Bundle == Baseline) **oder** das Bundle ist der valide committete Zustand des Runs (Erfolgsfall; Commit-Boundary = Mutation-Boundary, AD-17f). Beide Pfade sind deterministisch und ohne Wanduhr.
|
||||
|
||||
**Sandbox (CONSIST-1..CONSIST-7, `bash run-sandbox.sh`, Exit 0):**
|
||||
- CONSIST-1: konsistente Änderungsplanung — P2-Block erzeugt Plan (Input-Zustand, Ziel-Pfade, Betroffenheit, Struktur-Erhaltung) aus committetem Input, als Variablen-Set captured, Determinismus (AD-17h/A0-19)
|
||||
- CONSIST-2: **Plan-Freeze** — Mutation außerhalb der Plan-Pfadmengen wird erkannt und verweigert/verhindert (Veränderungs-Sperre, Ghost-Diff-Kopplung §5.9 Pkt. 5)
|
||||
- CONSIST-3: **Rollback-Restaurations-Invariante** — fehlgeschlagene Mutation rollt auf exakte Baseline zurück (SHA-256 byte-identisch), Bundle-Zustand == Baseline
|
||||
- CONSIST-4: MUTATION_ABBRUCH — Abbruch nach Teilerfolg hinterlässt keinen Teilerfolg als fertige Mutation (Commit-Boundary), `raw/` unberührt
|
||||
- CONSIST-5: VALIDATION_FAIL — §6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent
|
||||
- CONSIST-6: VALIDATION_SUCCESS — Mutationen als Ganzes committet, Diff gg. Plan-Menge gedeckt, §6-Pkt.-4-Nachweis
|
||||
- CONSIST-7: **Determinismus-Zwei-Run + keine Engine** — gleicher Baum-Input → identische Plan-/Rollback-/State-Outputs; Trennung ohne Workflow-Engine (nur Shell/Git/Datei, D-3)
|
||||
|
||||
(Code-/Zahlgenauigkeiten: 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-7/run-sandbox.sh` — expected: CONSIST-1..CONSIST-7 harte PASS/Fail, Restaurations-Invariante erzwungen, Exit 0.
|
||||
2. `grep -n "§5.13\|Revision 3.2" schema/compiler.md` — liefert §5.13-Sektion + Revisionslog-Eintrag; `grep -n "Reason/Mutate-Phasen-Trennung" schema/compiler.md` — die §5.13-Überschrift wortgleich (inkl. §5.12-Seam-Satz in §5.13-Intro).
|
||||
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-7/` liegen außerhalb `wiki/` und sind nicht Teil der Diff-Probe (wie §5.9 Pkt. 5-Doku, Story-3.6-Präzedenz).
|
||||
|
||||
**Zu beachten (beim step-04-Review):** (a) §0-Phasen-Listentext, §5.9-Pkt.-6-P2-Block und §6-Pkt.-3/§5.3-Referenz müssen textuell **unverändert** bleiben — §5.13 verweist auf sie (Fugen-Identität: Um-Nummerierung oder neue Phase => Ask-First); (b) „die **vier** AD-6-Phasen decken sich logisch mit der §0-Struktur" ersetzt **keine** §0-Normierungen — sie ist eine Zuordnungstabelle, kein Parsing-Muss; (c) die vorhandene §8-Revisionslog-Nummer ist **3.1** (Story 3.6); **Revision 3.2 ist für Story 3.7 frei** (grep-verifiziert: keine 3.2 im Revisionslog); (d) kein neuer §7-Bullet ersetzt einen bestehenden — der Reason/Mutate-Bullet wird **ergänzt**, die bestehenden Story-Bullets (2.2/2.3/2.4/2.5/3.2/3.4/3.5/3.6) bleiben unverändert.
|
||||
|
||||
## Suggested Review Order
|
||||
|
||||
**§5.13-Reason/Mutate-Trennung-Instruktion**
|
||||
|
||||
- Einstieg: §5.13-Sektion — logische Phasen-Disziplin (AD-6/A0-7), Kern der Story, §5.12-Seam und §0-Zuordnungstabelle.
|
||||
[`compiler.md:343`](../../schema/compiler.md#L343)
|
||||
- Änderungsplanung: P2-Block (§5.9 Pkt. 6) als Phase institutionalisiert, Plan-Freeze (Veränderungs-Sperre, Ghost-Diff-Kopplung §5.9 Pkt. 5).
|
||||
[`compiler.md:356`](../../schema/compiler.md#L356)
|
||||
- Zustands-Restaurations-Invariante: Post-Rollback-Diff gg. Baseline leer / Bundle == valide committet; §6-Pkt.-3/§5.3-Rückverweis unverändert.
|
||||
[`compiler.md:358`](../../schema/compiler.md#L358)
|
||||
- Keine eigene Workflow Engine (D-3, AD-6): logische Trennung in einer Session, kein Prozess/Server/MCP.
|
||||
[`compiler.md:362`](../../schema/compiler.md#L362)
|
||||
|
||||
**§7/§8-Nachweis**
|
||||
|
||||
- §7-Bullet Reason/Mutate-Trennung — „in §5.13 verankert (Story 3.7)", AD-6/A0-7, bestehende Bullets unverändert.
|
||||
[`compiler.md:420`](../../schema/compiler.md#L420)
|
||||
- §8-Revisionslog Revision 3.2 — Verankerung, Abschlussklausel, AD-6 auf §5.13-Anker.
|
||||
[`compiler.md:461`](../../schema/compiler.md#L461)
|
||||
|
||||
**Sandbox-Nachweis**
|
||||
|
||||
- Sandbox CONSIST-1..CONSIST-7 — re-executierbar, harte PASS/Fail, Zustands-Restaurations-Invariante (Exit 0)
|
||||
[`run-sandbox.sh:1`](../../_bmad-output/implementation-artifacts/sandbox-3-7/run-sandbox.sh#L1)
|
||||
|
||||
**Story-Protokoll/Defers**
|
||||
|
||||
- log.md — Story-3.7-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel).
|
||||
[`log.md:1`](../../wiki/log.md#L1)
|
||||
- sprint-status.yaml — `3-7-…` = `review` im Diff (Implementierungs-Flip `in-progress` + Review-Start-Flip `review` im selben Commit); das `done`-Transition ist nicht Teil dieses Diffs (Step-05-Status-Sync).
|
||||
[`sprint-status.yaml:60`](../../_bmad-output/implementation-artifacts/sprint-status.yaml#L60)
|
||||
- deferred-work.md — ggf. Story-3.7-Defers append-only.
|
||||
[`deferred-work.md:1`](../../_bmad-output/implementation-artifacts/deferred-work.md#L1)
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
---
|
||||
title: 'Story 3.8 — Determinismus-Vertrag (AD-17h) als Agent-Instruktions-Validator umsetzen'
|
||||
type: 'feature'
|
||||
created: '2026-08-20'
|
||||
status: 'in-progress'
|
||||
baseline_commit: 'c4cdf4b93e17b3f84dff03a4bd5608251be56268'
|
||||
review_loop_iteration: 0
|
||||
course_correction: '_bmad-output/planning-artifacts/sprint-change-proposal-2026-08-20.md'
|
||||
context:
|
||||
- '_bmad-output/implementation-artifacts/epic-3-context.md'
|
||||
---
|
||||
|
||||
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
|
||||
|
||||
## Intent
|
||||
|
||||
**Problem:** AD-17h/FT-10/A0-19 verlangen: Über denselben Git-State + dasselbe Eingabeset erzeugen **zwei unabhängige Runs denselben Bundle-State**; eine Abweichung ist ein Fehler der AD-16-Klassifikation, kein Rauschen. Das **Enforcement** lebt im MVP (D-3, Q-6) als **Agent-Instruktions-Validator** und muss **mechanisch bestätigt** sein, bevor es tragend wird. Der Ist-Zustand der Instruktion verankert den Vertrag zwar an vielen Stellen (AD-17h-Rückverweise in §3.2/§5.9/§5.10/§5.11/§5.12/§5.13), aber es fehlt die **geschlossene, aus dem committeten Git-State ableitbare Bestätigungs-Mechanik**: Es gibt keinen normativen Ort, der definiert, (a) was genau der „Bundle-State" ist, (b) wie die Zwei-Run-Bestätigung mechanisch abläuft, (c) welche **Ausnahmen** vom byte-identischen Vergleich gelten (namentlich der dokumentierte `generated.at`-Wanduhr-Gap, A0-20) und (d) wie eine festgestellte Abweichung klassifiziert wird (AD-16-Klassifikationsfehler). Mehrere bekannte Lücken sind hierher gewandert (Em-Dash-Normalisierung, Kollaps-Reichweite, Match-Scope, `at`-Gap, Orphan-Politik, Misch-Run-Coverage).
|
||||
|
||||
**Approach:** Neue Sektion **§5.14 „Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)"** (nach §5.13, vor §6): definiert (1) den **Bundle-State** als deterministische Projektion des committeten Git-States (vergleichbare Zustandsmenge, `at`-Feld als benannte Ausnahme), (2) die **Zwei-Run-Bestätigungs-Mechanik** als textuell festgehaltenes, re-executierbares Prüfverfahren (Vergleichs-Operandum = committeter Baum; identische Plan-/Kandidaten-/Reihenfolge-Outputs; kein neuer Prozess/Server/MCP, D-3), (3) die **Ausnahme-Menge** des Bundle-State-Vergleichs (der `generated.at`-Wanduhr-Gap als dokumentierte A0-20-Konvention; sonst byte-identisch) und (4) die **Abweichungs-Klassifikation** (jede Abweichung außerhalb der Ausnahme = AD-16-Klassifikationsdefekt, textuell benannt, NFR-4). Die Zwei-Run-Bestätigung wird als **Sandbox-Szenario** mechanisch demonstriert; die bekannten Lücken (Em-Dash, Kollaps-Reichweite, Match-Scope, Orphan) werden als **geschlossene, deterministische Regeln** in die Instruktion gehoben — keine offenen Fragen verbleiben, die die Zwei-Run-Identität gefährden.
|
||||
|
||||
## Boundaries & Constraints
|
||||
|
||||
**Always:**
|
||||
- Nur `schema/compiler.md` (neue §5.14 + §7-Auflösung des Determinismus-Vorbehalts + §8 Revisionslog **Revision 3.3** + gegebenenfalls Kollaps-/Match-Scope-/Orphan-Präzisierungen in §3.2/§5.10) mutiert (D-3). `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` **read-only** (AD-3). Kein Standalone (D-3), keine eigene Workflow-Engine/kein neuer Prozess/Server/MCP (AD-6, AD-11). Keine neue §7-Invaliditätsklasse, kein Vertrags-Change, kein neuer Prädikat-/Format-/Frontmatter-Key.
|
||||
- Deterministisch aus dem committeten Git-State ableitbar (AD-17h/A0-19): Bundle-State-Vergleich, Zwei-Run-Bestätigungs-Mechanik, Ausnahme-Menge, Abweichungs-Klassifikation. **Keine Wanduhr/`now`-Steuerung** der Bestätigungs-Mechanik.
|
||||
- Der `generated.at`-Wanduhr-Gap **bleibt als dokumentierte Ausnahme** bestehen (A0-20-Konvention, §5.9 Pkt. 2, §5.10 Pkt. 8, unverändert bindend bis Ask-First-Wechsel). §5.14 **definiert** die Behandlung von `at` im Bundle-State-Vergleich (Ausnahmemenge), ändert aber **nicht** das `generated.at`-Verhalten selbst.
|
||||
- Bei einer Abweichung zweier Runs: die Abweichung **ist** ein AD-16-Klassifikationsfehler (kein akzeptables Rauschen, FT-10/AD-17h-AC); die textuelle Benennung (NFR-4) ist verpflichtend.
|
||||
- Die **normalisierenden Lücken-Schließungen** (Em-Dash in der Kollaps-Klasse, Kollaps-Reichweite bei Läufen/führenden/trailenden Separatoren, Wortgrenzen-/Frontmatter-Scope der Stufe-a-Erhebung) werden als **deterministische Regel-Ergänzungen** in §3.2 umgesetzt — **append-only/kein Umbruch** des bestehenden §3.2-Wortlauts; die Orphan-Politik (§5.10 Pkt. 8) wird zu einer deterministischen Reconcile-Orphan-Regel präzisiert.
|
||||
- Die Bestätigung ist **agent-instruction-basiert (D-3, Q-6)**: ein Agent führt die Instruktion aus und bestätigt in einer Session die Zwei-Run-Identität — kein dedizierter Validator-Prozess, keine extra Runtime.
|
||||
|
||||
**Ask First:**
|
||||
- **Änderung des `generated.at`-Verhaltens** selbst (z. B. Ableitung `at` aus dem Git-State statt Wanduhr) — §5.14 definiert nur seine Behandlung im Vergleich, der Konventionswechsel wäre Ask-First.
|
||||
- Einführung einer **eigenständigen, ausführbaren Validator-Datei** (separates Skript/Programm außerhalb der Instruktion als Enforcer) statt der textuellen Agent-Instruktions-Disziplin — wäre ein Realisierungswechsel weg vom D-3/Q-6-Modell.
|
||||
- Kollaps-Reichweite bei Läufen/führenden/trailenden Separatoren **anders** zu definieren als „Kollaps auf genau ein `-` + Trim" (z. B. Doppel-Bindestrich-Erhaltung) — jede andere Wahl bricht bestehende Sandbox-Erwartungen.
|
||||
- Umbenennung/Neudefinition der vorhandenen §3.2-Erhebungsstufen (a/b/c) oder deren Reihenfolge.
|
||||
|
||||
**Never:**
|
||||
- `raw/`-Inhalte verändern (AD-3); uncommittete fremde `wiki/`-Änderungen löschen (AD-17e); textueller Auto-Merge bei Branch-Konvergenz (AD-17c); Wanduhr/`now`-Zeitstempel als Bestätigungs-Steuergröße; eigener `# Log`-Stand in der Registry; Standalone-Compiler/eigene LLM-Runtime (D-3, AD-11); Änderung an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`; neuer Frontmatter-Key; neue §7-Invaliditätsklasse; neuer Prädikat-/Format-Key; eigene Workflow-Engine; neuer dedizierter Prozess/Server/MCP als Enforcer.
|
||||
|
||||
## I/O & Edge-Case Matrix
|
||||
|
||||
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|
||||
|----------|--------------|---------------------------|----------------|
|
||||
| BUNDLE_STATE_DEFINITION | gleicher committeter Git-State + gleiche Eingabemenge | Bundle-State definiert als deterministische Projektion: Datei-Gesamtheit unter `wiki/`/`raw/` gg. Baseline, Plan-/Kandidaten-/Reihenfolge-Outputs; `generated.at` als benannte Ausnahme | textuell begründet (NFR-4), keine Lücke |
|
||||
| ZWEI_RUN_IDENTISCH | zwei unabhängige Runs, gleicher Git-State + Eingabeset | identische Bundle-States bis auf benannte `at`-Ausnahme; Sandbox-Assertion nicht-vakuum (echte Content-Identität) | PASS; bei Abweichung außerhalb Ausnahme: AD-16-Klassifikationsdefekt |
|
||||
| ZWEI_RUN_ABWEICHUNG | zwei Runs, gleicher State, verschiedene Ausgabe | Abweichung = Fehler der AD-16-Klassifikation (kein Rauschen); textuell benannt, Run korrigiert/rollt zurück | NFR-4-Benennung; AD-17h-AC |
|
||||
| AT_GAP_AUSNAHME | zwei unabhängige Runs, gleicher Input | `generated.at` darf abweichen (A0-20-Konvention, §5.9 Pkt. 2) — einzige benannte Ausnahme im Bundle-State-Vergleich; übrige Teile byte-identisch | dokumentiert (kein stiller Ausschluss) |
|
||||
| NORMALISIERUNG_EM_DASH | Term mit Em-Dash `—` | Em-Dash in die Kollaps-Klasse aufgenommen (`[-–— _]`→`-`); identische canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen | deterministisch, kein stiller Ausschluss |
|
||||
| NORMALISIERUNG_KOLLAPS | Term mit Läufen/führenden/trailenden Separatoren | Kollaps-Reichweite definiert: Kollaps auf genau ein `-` + Trim (führende/trailende entfernt), Läufe auf ein `-` | deterministisch, glaubhaft re-executierbar |
|
||||
| MATCH_SCOPE_STUFE_A | Stufe-a-grep-Treffer mit/ohne Wortgrenze, Frontmatter-Treffer | Match-Semantik fixiert: ganzes Wort (Wortgrenzen), Frontmatter exkludiert (nur Body) | deterministisch, keine falschen Kandidaten |
|
||||
| ORPHAN_POLITIK | neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer | Orphan bleibt unzugeordnet, wird in `log.md` als verwaist protokolliert, kein Banner, keine stille Vorbearbeitung | deterministische Reconcile-Orphan-Regel (AD-16-Default) |
|
||||
|
||||
## Code Map
|
||||
|
||||
- `schema/compiler.md` — **primär mutiert** (D-3): neue Sektion **§5.14 „Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)"** (nach §5.13, vor §6; Fixierung: §5.13-Ende Z. 362, §6-Start Z. 364 — neue Sektion zwischen beide, §5.14 Z. 364–381 ca.); §0-Phasen-Listentext **unverändert**; §5.13 Pkt. 7 „Home Story 3.8" — wird auf §5.14-**Anker** umgehängt (die `generated.at`-Gap-Ausnahme ist jetzt in §5.14 definiert statt nur deklariert; Home-Verweis bleibt, wird aber auf die §5.14-Definition verlagert); §3.2 Normalisierungs-Lücken (Em-Dash in Kollaps-Klasse Z. 51; Kollaps-Reichweite; Match-Scope Z. 55) — als Regel-Ergänzungen aufgelöst; §5.10 Pkt. 8 Orphan-Politik Z. 301 — zur deterministischen Reconcile-Orphan-Regel präzisiert; §7 (`Z. 409ff.`) Determinismus-Vorbehalt „Em-Dash-Lücke an Story 3.8 übergeben" — **aufgelöst**, Verankerung in §5.14 benannt; §8-Normreferenz `AD-17h` (`Z. 430`) auf **§5.14-Anker** angehoben (mit der `at`-Ausnahme-Nennung); Revisionslog-Eintrag **Revision 3.3** nach Z. 461. Anker (IST-Zeilen nach Implementierung): §5.14-Sektion Z. ≈364; §7-Determinismus-Bullet nach Z. 420; §8-AD-17h in Z. 430; Revisionslog **Revision 3.3** nach Z. 461.
|
||||
- `wiki/log.md` — **append** (append-only, Vertrag §5): Story-3.8-Eintrag (Verankerung, Sandbox-Nachweis, Statuswechsel, Validator-Verdikt); bestehende Bullets unverändert.
|
||||
- `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh` — **neu** (re-executierbar, Muster sandbox-3-7, Exit 0): Szenarien DET-1..DET-8 (s. Design Notes), harte Pass/Fail-Assertionen, Zwei-Run-Identität nicht-vakuum, keine Berührung des realen Ist-Baums.
|
||||
- `_bmad-output/implementation-artifacts/sprint-status.yaml` — **mutiert**: `3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato` `backlog` → `in-progress` (Implementierungs-Flip) → `review` (Review-Start-Flip, Step-04; finaler `done`-Flip im Step-05); `last_updated` (Format `MM-DD-YYYY HH:MM`, HEAD-Präzision).
|
||||
- `_bmad-output/implementation-artifacts/deferred-work.md` — **append** (append-only): ggf. Story-3.8-Defers; die bestehenden „Home: Story 3.8"-Einträge werden **aufgegriffen/geschlossen** — nicht als Nur-Read-markiert, sondern als aufgehobene Homes gekennzeichnet (Status-Eintragung). Stand: mehrere determinismus-bezogene Defer-Homes (Em-Dash, Kollaps, Match-Scope, Orphan, `at`-Gap, Misch-Run-Coverage) werden in §5.14/§3.2/§5.10 geschlossen; verbleibende Homes (falls nicht schließbar) bleiben offen mit verbleibendem Ziel.
|
||||
- `_bmad-output/implementation-artifacts/epic-3-context.md`, `spec-3-7-…md` — **read-only** (continue-context; Kette unverändert).
|
||||
|
||||
**Read-only evidence (AD-3):** `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` (inkl. `raw/architecture-spine/…md` AD-17h/Q-6/FT-10/D-3, `raw/epics/epics-2026-08-14.md` A0-19/A0-20 — normativer Rückbezug, keine Mutation). `schema/canonical-terms.md` bleibt append-only-Registry (keine Änderung an Bestandseinträgen; die Em-Dash-/Kollaps-Schließung lebt als Regel in `compiler.md` §3.2, nicht als Registry-Edits — §3.2-Pkt.-1b-Kollaps-Klasse ist die zentrale Normalisierungsregel).
|
||||
|
||||
## Tasks & Acceptance
|
||||
|
||||
**Execution:**
|
||||
- [x] `schema/compiler.md` — §5.14 einfügen (nach §5.13, vor §6): Bundle-State-Definition, Zwei-Run-Bestätigungs-Mechanik (agent-Instruktions-basiert, D-3/Q-6, re-executierbare Formeln), `at`-Ausnahme-Menge, Abweichungs-Klassifikation als AD-16-Klassifikationsdefekt; §0/§5.13/§6 Wortlaut-unverändert referenzieren; kein neuer Prädikat-/Format-/Frontmatter-Key
|
||||
- [x] `schema/compiler.md` — §3.2-Normalisierungs-Lücken schließen (Em-Dash-Klasse, Kollaps-Reichweite, Match-Scope der Stufe a) — als deterministische Regel-Ergänzungen ohne Umbruch des bestehenden §3.2-Wortlauts; Orphan-Politik §5.10 Pkt. 8 zur deterministischen Reconcile-Orphan-Regel präzisieren
|
||||
- [x] `schema/compiler.md` — Revision 3.3 in §8 belegen (AD-17h auf §5.14-Anker, `at`-Ausnahme-Nennung, Abschlussklausel); §7-Determinismus-Vorbehalt auflösen (Verankerung in §5.14)
|
||||
- [x] `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh` — Szenarien DET-1..DET-8 (Bundle-State-Definition, Zwei-Run-identisch nicht-vakuum, Zwei-Run-Abweichung klassifiziert, `at`-Gap-Ausnahme, Em-Dash-, Kollaps-, Match-Scope-, Orphan-Szenario), Exit 0
|
||||
- [x] `_bmad-output/implementation-artifacts/sprint-status.yaml` — Key `3-8-…` → `in-progress` (Implementierung) → `review` (Review-Start); `_bmad-output/implementation-artifacts/deferred-work.md` — Story-3.8-Defers bzw. aufgegriffene/geschlossene „Home: Story 3.8"-Einträge kennzeichnen
|
||||
- [x] `wiki/log.md` — (Implementierung) Story-3.8-Eintrag, `sprint-status.yaml` → in-progress (Implementierung) bzw. `review` (Review-Start, Step-04); Review-Abschluss `done` im Review-Schritt (Workflow-Konvention)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Given einen Fixieren Git-State + feste Eingabemenge, when zwei unabhängige Runs ausgeführt werden, then produziert §5.14-Wortlaut identische Bundle-States bis auf die benannte `at`-Ausnahme (FT-10, AD-17h, AC-1).
|
||||
- Given eine Abweichung bei zwei solchen Runs, when sie festgestellt wird, then wird sie als Fehler im AD-16-Klassifikations-Mechanismus behandelt und textuell benannt (nicht als akzeptables Rauschen) (AD-17h, AC-2).
|
||||
- Given der MVP (D-3), when die Determinismus-Enforcement fehlt, then lebt sie als Agent-Instruktions-Validator (§5.14) und ist als mechanisch durch die Sandbox bestätigt (Q-6, A0-19; AC-3).
|
||||
- Given der Validator, when umgesetzt, then hält er keine Embedding-/Vector-Infrastruktur vor (AD-13; FT-3, FT-4; AC-4) und erzeugt keine eigene Runtime/kein neues Werkzeug (D-3, AD-11).
|
||||
|
||||
## Spec Change Log
|
||||
|
||||
<!-- Append-only. Populated by step-04 during review loops. Do not modify or delete existing entries. -->
|
||||
|
||||
### Review-Loop-1 (2026-08-20, Step-04)
|
||||
|
||||
**Ergebnis:** keine intent_gap / kein bad_spec; 0 Loopback. Review-Schicht 3 Subagenten (blind-hunter / edge-case-hunter / verification-gap, synchron); Triage: 14 Patch / 0 defer / Rest Reject (ausführliche Reject-Begründung im Story-Protokoll `wiki/log.md`). Alle Patches rein dokumentarischer Natur (Klarstellung/Verweis/Status); Semantik unverändert, keine neue Vertragsentscheidung.
|
||||
|
||||
**Patch-Stapel (in dieser Runde angewandt):**
|
||||
- §3.2-Pkt.-1b (b): paketierte Kollaps-Klasse in der Operativ-Klausel auf die geschlossene `[-–— _]`-Klasse angehoben; Verweis auf den Append-Block als maßgeblich (KEEP: die append-only-Schließung bleibt unverändert; der Operativ-Satz ist jetzt konsistent).
|
||||
- §3.2-Pkt.-2a: Match-Scope (ganze Wörter über den Body, exklusiv Frontmatter) in die operative Stufe-a-Formel gewirkt — Einzel-Term-Klausel + Mehrfach-Term-Vereinigung (Pkt. 1c) verdeutlicht; Body-Abgrenzung (nach der zweiten `---`-Zeile) bestimmt.
|
||||
- §3-Pkt.-2-Kandidatenliste (und damit §5.9-Worked Example): Stufe-a-Scope-Verweis ergänzt.
|
||||
- §5.14-Pkt.-2: Zwei-Run-Abgleich als Vergleich der beiden Run-Ausgänge gegeneinander (nicht nur je Lauf gg. Baseline) verdeutlicht — Beispiel `git diff <Run-A> <Run-B>` ergänzt.
|
||||
- §5.14-Pkt.-1-Intro: AD-3-Schutzliste um `adapters/` ergänzt.
|
||||
- §5.10-Pkt.-8: die pre-existing-Orphan-Obligationsformulierung als vorbestehend markiert (Verweis auf die neue deterministische Regel unten; keine Staffelung an Story 3.8/Epi-4 mehr).
|
||||
- `deferred-work.md`: Bezugs-Eintrag-Nummern der Story-3.2-review-Defers korrigiert (Kollaps Eintrag 3 → 2; Match-Scope Eintrag 2 → 1).
|
||||
- `wiki/log.md`: „8 `### Aufgegriffen`-Blöcke" → „7 `### Aufgegriffen`-Blöcke + 1 `### Aufgegriffen (teilweise)`".
|
||||
- `sandbox-3-8/run-sandbox.sh`: DET-2-at-Maskierungszweck als Kommentar klargestellt (belegt die Nicht-at-Bestandteile; at-Varianz demonstriert DET-4); DET-1-Demo-Kommentar (Kandidaten-/Form-Outputs als redaktionell verdichtete Run-Feststellungen).
|
||||
- `sprint-status.yaml`: Key `3-8-…` → `review` (Review-Start-Flip; finaler `done`-Flip im Step-05).
|
||||
- `spec`-Frontmatter: `status: 'in-review'` (Review-Start per Step-04), `review_loop_iteration: 0` unverändert; SRO-Review-Loop-1-Hinweis ergänzt.
|
||||
|
||||
**Rejected-Findings (Kurznotation, nicht Teil der Patches):** Stufe-a-Formel-Änderung nicht nötig (die Formel `rg -l`/`grep -rl` + Scope-Klausel bleibt die Instruktionsform; die Wortgrenzen-/Frontmatter-Semantik reist über die Pkt.-2a-Klausel, nicht über ein Flag); DET-2-Plan-Literal (dev-demo, echte Content-Hashes decken; Kandidaten-Ableitung über DET-1/DET-8); DET-8-hardcodierter Verwaist-Befund (Mechanik textuell, Existenz-negativ-Kontrolle hart); §5.14-`raw/`-Input-Verdacht (Pkt.-1a nennt `raw/`-Zuwächse bereits explizit „als Input"); §5.14-„kommt erstellt der Diff" — keine Formel-Verschmelzung; DET-2-maskierte-`at` (Display-Maske, DET-4 behandelt Varianz); Log-Overcount (Patch 9, siehe oben); Umlaut-Aufschlag (behandelt als Patch-7-Fundament); Sprint-Last-Updated-Timing (Konvention); §3.2-„keine offene Frage"(Umlaut bleibt dokumentiert offen, Wortlaut angepasst).
|
||||
|
||||
## Design Notes
|
||||
|
||||
**Warum §5.14 als eigene Sektion, nicht §6.5-Erweiterung?** §6.5 ist die „Determinismus- & Selbsttest-Norm" mit **drei** Nachprüf-Kriterien (Vollständige §3-Subset-Konformität, `at`-Normalform, `sources`-Existenz) — die projektions-seitige Content-Prüfung je erzeugtem Concept. Der Determinismus-*Vertrag* (AD-17h/FT-10) ist die **Cross-Run-Eigenschaft** (zwei unabhängige Runs → gleicher Bundle-State), kein Einzel-Concept-Kriterium. §5.14 ist die geschlossene Verankerung dieser Cross-Run-Eigenschaft: (a) **Bundle-State-Definition** (welche Zustandsmenge ist „der Bundle-State" — die committete Baum-Projektion unter `wiki/`/`raw/` zzgl. Plan-/Kandidaten-/Reihenfolge-Outputs; `generated.at` als benannte Ausnahme), (b) **Zwei-Run-Bestätigungs-Mechanik** (agent-Instruktions-basiert; der Produzent führt die Instruktion zweimal über demselben committeten Git-State aus und vergleicht die Bundle-States; re-executierbare Formeln), (c) **Ausnahme-Menge** (allein `generated.at`-Wanduhr-Gap, dokumentiertes A0-20; Nichts sonst), (d) **Abweichungs-Klassifikation** (jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsfehler; textuell benannt NFR-4; kein stiller Vorbeilass). Damit ist die bisher „offen verankerte" AD-17h-Eigenschaft (viele Rückverweise, kein normativer Ort der Bestätigung) zu einem **instruierten, mechanisch bestätigbaren Verfahren** verdichtet.
|
||||
|
||||
**Die Normalisierungs- und Match-Lücken werden deterministisch geschlossen, nicht nur notiert.** Die Kollaps-Klasse in §3.2 Pkt. 1b ist `[-–_ ]` (En-Dash –, Bindestrich -, Unterstrich _, Leerzeichen) — der Em-Dash `—` fehlt (bekannte Story-3.2-Lücke mit Home Story 3.8). Die Schließung ist eine **Ergänzung der Kollaps-Klasse** um den Em-Dash (`[-–— _]` → `-`) sowie die Festlegung der **Kollaps-Reichweite** (jedes Vorkommen → genau ein `-`; führende/trailende Separatoren werden getrimmt — die bereits in der Sandbox etablierte Semantik) und des **Match-Scope** der Stufe a (Stufe-a-grep-matcht **ganze Wörter** über den Body, exklusive YAML-Frontmatter — `rg -l '<term>' -g '!log.md' wiki/` erhält einen deterministischen Scope; Substring- und Frontmatter-Treffer werden ausgeschlossen). §5.10 Pkt. 8 (Orphan) erhält eine **deterministische Reconcile-Orphan-Regel**: neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer bleibt unzugeordnet, wird in `log.md` als verwaist protokolliert (Datumsgruppe, `<Baseline-Commit>`), kein Banner/keine stille Vorbearbeitung. Diese Schließungen machen die relevanz-/synthese-seitigen Erhebungen über den committeten Zustand vollständig pinbar.
|
||||
|
||||
**Die Sandbox (DET-1..DET-8, `bash run-sandbox.sh`, `/tmp`-Baum, Exit 0) demonstriert die Bestätigung mechanisch.**
|
||||
- DET-1 BUNDLE_STATE_DEFINITION: erzeugt ein Mini-Bundle, definiert/extrahiert die Bundle-State-Projektion (wiki/-Dateien + Plan-/Kandidaten-/Reihenfolge-Outputs) deterministisch
|
||||
- DET-2 ZWEI_RUN_IDENTISCH (nicht-vakuum): zwei unabhängige Läufe über demselben committeten Baum → identische Bundle-States bis auf `at`; die Assertion vergleicht echte Content-Hashes (nicht nur Vorhandensein)
|
||||
- DET-3 ZWEI_RUN_ABWEICHUNG: künstlich divergenter Lauf → wird als AD-16-Klassifikationsdefekt klassifiziert und textuell benannt (kein Rauschen)
|
||||
- DET-4 AT_GAP_AUSNAHME: zeigt, dass `generated.at`-Wanduhr-Gap die einzige benannte Ausnahme ist (übrige Teile byte-identisch)
|
||||
- DET-5 EM_DASH: Term mit Em-Dash `—` normalisiert auf dieselbe canonische Form wie En-Dash/Bindestrich/Unterstrich/Leerzeichen
|
||||
- DET-6 KOLLAPS: Läufe (`a--b`), führende (`-x`), trailende (`x-`) → Kollaps auf genau ein `-` + Trim
|
||||
- DET-7 MATCH_SCOPE: Stufe-a-matcht ganze Wörter über Body, exklusive Frontmatter (Substring-/Frontmatter-Treffer ausgeschlossen)
|
||||
- DET-8 ORPHAN: neu committete Evidenz ohne Ziel-Pfad-Treffer → unzugeordnet, `log.md`-verwaist protokolliert, kein Banner/keine Mutation
|
||||
|
||||
(Code-/Zahlgenauigkeiten: 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-8/run-sandbox.sh` — expected: DET-1..DET-8 harte PASS/Fail, Zwei-Run-Identität nicht-vakuum, Exit 0.
|
||||
2. `grep -n "§5.14\|Revision 3.3" schema/compiler.md` — liefert §5.14-Sektion + Revisionslog-Eintrag; `grep -n "Determinismus-Vertrag & Agent-Instruktions-Validator" schema/compiler.md` — die §5.14-Überschrift wortgleich (inkl. §5.13-Seam-Satz in §5.14-Intro).
|
||||
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`. `schema/canonical-terms.md` unverändert (append-only-Registry unangetastet).
|
||||
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-8/` liegen außerhalb `wiki/` und sind nicht Teil der Diff-Probe.
|
||||
|
||||
**Zu beachten (beim step-04-Review):** (a) §0-Phasen-Listentext, §5.13-Phasen-Disziplin und §6/§6.5 müssen textuell **unverändert** bleiben — §5.14 verweist auf sie (Fugen-Identität); (b) der `generated.at`-Wanduhr-Gap **bleibt verhaltens-seitig unverändert** (A0-20-Konvention); §5.14 definiert nur seine Behandlung im Bundle-State-Vergleich (Ausnahmemenge) — ein Wechsel des `generated.at`-Verhaltens wäre Ask-First; (c) die vorhandene §8-Revisionslog-Nummer ist **3.2** (Story 3.7); **Revision 3.3 ist für Story 3.8 frei** (grep-verifiziert: keine 3.3 im Revisionslog); (d) kein neuer §7-Bullet ersetzt einen bestehenden — der Determinismus-Bullet wird **ergänzt** (Verankerungs-Verweis), die bestehenden Story-Bullets bleiben unverändert.
|
||||
|
||||
## Suggested Review Order
|
||||
|
||||
<!-- Klickbare Review-Route (Step-05): jede Zeile = [Beschreibung:Zeilennummer](../../relativer-pfad#LZeile) -- Ctrl+Klick (Cmd+Klick auf macOS) öffnet den Anker. Zuerst die §5.14-Bestätigungs-Mechanik lesen, dann die Lückenschließung, dann Nachweis-Orte, Sandbox-Beweis und Protokoll als Peripherie. -->
|
||||
|
||||
**§5.14-Determinismus-Vertrag (Kern, Einstieg)**
|
||||
|
||||
- [compiler.md:365](../../schema/compiler.md#L365) — §5.14-Überschrift, Intro mit §5.13-Seam und AD-3-Schutzliste inkl. `adapters/`.
|
||||
- [compiler.md:369](../../schema/compiler.md#L369) — Pkt. 1 Bundle-State-Definition: committete Baum-Projektion, `generated.at` als benannte Ausnahme (A0-20), FT-10/AD-17h/AC-1.
|
||||
- [compiler.md:370](../../schema/compiler.md#L370) — Pkt. 2 Zwei-Run-Bestätigungs-Mechanik: zweimal über denselben committeten Git-State, Run-A↔Run-B-Abgleich (Beispiel `git diff`), keine Wanduhr-Steuerung.
|
||||
- [compiler.md:374](../../schema/compiler.md#L374) — Pkt. 3 Ausnahme-Menge: allein der `generated.at`-Wanduhr-Gap; alle übrigen Bestandteile byte-identisch (kein stiller Ausschluss).
|
||||
- [compiler.md:375](../../schema/compiler.md#L375) — Pkt. 4 Abweichungs-Klassifikation: jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsdefekt, kein Rauschen (NFR-4, Zustands-Restaurations-Invariante).
|
||||
- [compiler.md:376](../../schema/compiler.md#L376) — Pkt. 5 Normalisierungs-/Match-/Orphan-Schließung: Verankerung auf §3.2-Pkt.-1b / §5.10-Pkt.-8, Erhebungen vollständig pinbar.
|
||||
|
||||
**§3.2/§5.10-Lückenschließung (deterministische Regel-Ergänzungen)**
|
||||
|
||||
- [compiler.md:54](../../schema/compiler.md#L54) — §3.2-Pkt.-1b „Geschlossene Determinismus-Lücken": Em-Dash-Kollaps-Klasse `[-–— _]`, Kollaps-Reichweite (ein `-` + Trim), Match-Scope Stufe a (ganze Wörter, Body-exklusiv).
|
||||
- [compiler.md:56](../../schema/compiler.md#L56) — §3.2-Pkt.-2a operative Stufe-a-Mechanik: `rg -l`/`grep -rl`-Sweep + deterministischer Scope-Filter (zweistufig, tool-portabel), Mehrfach-Term-Vereinigung.
|
||||
- [compiler.md:302](../../schema/compiler.md#L302) — §5.10-Pkt.-8 Erhaltungs-Invariante & Determinismus-Vertrag: Reconcile-Orphan-Regel (Zuwachs gg. Baseline, datumsgruppierter `log.md`-Eintrag, kein Banner, AD-16-Default).
|
||||
|
||||
**§7/§8-Nachweis**
|
||||
|
||||
- [compiler.md:476](../../schema/compiler.md#L476) — Revisionslog Revision 3.3: §5.14-Verankerung, §7-Determinismus-Vorbehalt aufgelöst, §8-AD-17h-Normreferenz auf §5.14-Anker, Abschlussklausel (AD-3/D-3/keine neue §7-Klasse).
|
||||
|
||||
**Sandbox-Beweis (mechanische Bestätigung)**
|
||||
|
||||
- [run-sandbox.sh:152](../../_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh#L152) — DET-1..DET-8-Szenarien (Bundle-State, Zwei-Run-identisch nicht-vakuum, Zwei-Run-Abweichung, `at`-Gap, Em-Dash, Kollaps, Match-Scope, Orphan); Läufe Exit 0 mit 8 harten PASS-Assertionen.
|
||||
|
||||
**Story-Protokoll/Defers (Peripherie, zuletzt)**
|
||||
|
||||
- [log.md:4](../../wiki/log.md#L4) — Story-3.8-Eintrag: Verankerung, Sandbox-Nachweis, Erhaltungs-Invariante, Validator-Verdikt; Revision 3.3.
|
||||
- [deferred-work.md:492](../../_bmad-output/implementation-artifacts/deferred-work.md#L492) — aufgegriffene/geschlossene „Home: Story 3.8"-Defers (7 `### Aufgegriffen` + 1 `### Aufgegriffen (teilweise)`, append-only ab Zeile 492).
|
||||
- [sprint-status.yaml:61](../../_bmad-output/implementation-artifacts/sprint-status.yaml#L61) — Key `3-8-determinismus-…` auf `review` (Review-Start-Flip Step-04); `done`-Transition im Step-05.
|
||||
@@ -29,7 +29,7 @@
|
||||
# - Dev moves story to 'review', then runs code-review (fresh context, different LLM recommended)
|
||||
# - Retrospective appends its action items to action_items; the status view surfaces open ones
|
||||
generated: 08-14-2026 00:00
|
||||
last_updated: 08-19-2026 16:40
|
||||
last_updated: 08-20-2026 12:18
|
||||
project: wow20
|
||||
project_key: NOKEY
|
||||
tracking_system: file-system
|
||||
@@ -55,10 +55,15 @@ development_status:
|
||||
3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip: done
|
||||
3-3-bestehende-concepts-erweitern-präzisieren-korrigieren: done
|
||||
3-4-wissen-aus-mehreren-sources-synthetisieren: done
|
||||
3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz: backlog
|
||||
3-6-lease-staleness-recovery-basis-absichern: backlog
|
||||
3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: backlog
|
||||
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: backlog
|
||||
3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz: done
|
||||
3-6-lease-staleness-recovery-basis-absichern: done
|
||||
3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: done
|
||||
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: in-progress
|
||||
3-9-deterministische-relevanz-und-reconcile-routing-schliessen: backlog
|
||||
3-10-inkrementelle-update-und-synthese-erhaltung-absichern: backlog
|
||||
3-11-root-scope-leasing-atomar-akquirieren: backlog
|
||||
3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen: backlog
|
||||
3-13-epic-3-verifikations-und-abnahmegate: backlog
|
||||
epic-3-retrospective: optional
|
||||
|
||||
epic-4: backlog
|
||||
|
||||
@@ -88,16 +88,17 @@ Aus den Sources entstehen eigenständige, OKF-0.2-konforme Concepts mit claim-gr
|
||||
**AD/A0:** AD-1, AD-4, AD-4a, AD-4b, AD-4c, AD-7, AD-7a..7d, AD-8, AD-9, A0-3, A0-4, A0-5, A0-8, A0-9, A0-10, A0-20
|
||||
|
||||
### Epic 3: Inkrementelle Kompilation & Synthese
|
||||
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Die Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten auf `lease/<area>/<id>`-Branches mit Dirty-Tree-Schutz, Root-Scope-Lease und commit-gebundener Mutation (AD-17a..h).
|
||||
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Die Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten mit atomarer Root-Scope-Lease, Dirty-Tree-Schutz und commit-gebundener Mutation. Klassifikationspflichtige Kollisionen enden bis Epic 4 fail-closed in einem strukturierten Hold; der Determinismus-Vertrag wird unabhängig und mechanisch qualifiziert.
|
||||
**FRs covered:** FR-4, FR-6, FR-7, FR-12
|
||||
**NFRs covered:** NFR-7
|
||||
**AD/A0:** AD-5, AD-6, AD-13, AD-17, AD-17a..17h, A0-6, A0-7, A0-12..A0-18, A0-19, A0-21
|
||||
**AD/A0:** AD-5, AD-6, AD-13, AD-17a, AD-17b, AD-17c (Fail-closed-Sicherheitsgrenze), AD-17d..17f, AD-17h, A0-6, A0-7, A0-12, A0-13, A0-14 (No-Auto-Merge-Sicherheitsgrenze), A0-15, A0-16, A0-18, A0-19, A0-21 (Incrementality-Teil)
|
||||
**Shared boundary:** Epic 3 verantwortet bei AD-17c/A0-14 ausschließlich Erkennung und fail-closed Erhaltung; Epic 4 verantwortet Klassifikation und semantische Auflösung. Bei A0-21 verantwortet Epic 3 die Erhaltung unabhängigen Wissens, Story 4.3 die spezifische Human-Curation-Semantik.
|
||||
|
||||
### Epic 4: Wissenstreue — Widersprüche & menschliche Kuratierung
|
||||
Widersprüche werden nie stillschweigend zur scheinbar eindeutigen Aussage zusammengeführt; relevante Disagreements bleiben als explizite Einträge in `log.md` erhalten; menschlich kuratierte Inhalte werden als bestehendes Wissen respektiert und bleiben über OKF-Trust-Metadaten (`verified: human:...`) von ungeprüftem maschinellem Output unterscheidbar; unvollständiges/ungeprüftes Wissen wird ohne künstliche Gewissheit dargestellt.
|
||||
**FRs covered:** FR-8, FR-13
|
||||
**NFRs covered:** NFR-7
|
||||
**AD/A0:** AD-16, AD-16a, AD-16b, AD-15, A0-11, A0-17, A0-20
|
||||
**AD/A0:** AD-16, AD-16a, AD-16b, AD-15, AD-17c, AD-17g, A0-11, A0-14, A0-17, A0-20, A0-21
|
||||
|
||||
### Epic 5: Consumer-Zugriff & Nachvollziehbarkeit
|
||||
Menschen und beliebige LLM-Agenten (BMAD, Claude Code, Codex, ...) lesen das Knowledge Bundle ohne Wiki-of-Wikis-spezifische Runtime; Änderungen sind über Git-Diffs nachvollziehbar; die kanonischen Compiler-Regeln sind agent-unabhängig mit dünnen Adaptern; es gibt keinen obligatorischen Server, keine Datenbank und keine proprietäre Abhängigkeit.
|
||||
@@ -257,8 +258,9 @@ So that ein Consumer relevantes Wissen schrittweise findet, ohne das gesamte Wik
|
||||
|
||||
## Epic 3: Inkrementelle Kompilation & Synthese
|
||||
|
||||
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten auf `lease/<area>/<id>`-Branches mit Dirty-Tree-Schutz, Root-Scope-Lease und commit-gebundener Mutation (AD-17a..h).
|
||||
**FRs covered:** FR-4, FR-6, FR-7, FR-12 · **NFRs:** NFR-7 · **AD/A0:** AD-5, AD-6, AD-13, AD-17, AD-17a..17h, A0-6, A0-7, A0-12..A0-18, A0-19, A0-21
|
||||
Der Nutzer verarbeitet neue Sources gegen das bestehende Wiki: bestehende Concepts werden erweitert, präzisiert oder korrigiert; mehrere Sources werden zu einer gemeinsamen Wissensrepräsentation synthetisiert; unverändertes Wissen bleibt erhalten. Relevanzbestimmung erfolgt textual-deterministisch (grep/ripgrep/Traversal, AD-13); Producer arbeiten mit atomarer Root-Scope-Lease, Dirty-Tree-Schutz und commit-gebundener Mutation. Klassifikationspflichtige Kollisionen enden bis Epic 4 fail-closed in einem strukturierten Hold; der Determinismus-Vertrag wird unabhängig und mechanisch qualifiziert.
|
||||
**FRs covered:** FR-4, FR-6, FR-7, FR-12 · **NFRs:** NFR-7 · **AD/A0:** AD-5, AD-6, AD-13, AD-17a, AD-17b, AD-17c (Fail-closed-Sicherheitsgrenze), AD-17d..17f, AD-17h, A0-6, A0-7, A0-12, A0-13, A0-14 (No-Auto-Merge-Sicherheitsgrenze), A0-15, A0-16, A0-18, A0-19, A0-21 (Incrementality-Teil)
|
||||
**Shared boundary:** Epic 3 verantwortet bei AD-17c/A0-14 ausschließlich Erkennung und fail-closed Erhaltung; Epic 4 verantwortet Klassifikation und semantische Auflösung. Bei A0-21 verantwortet Epic 3 die Erhaltung unabhängigen Wissens, Story 4.3 die spezifische Human-Curation-Semantik.
|
||||
|
||||
### Story 3.1: Inkrementellen Datenfluss implementieren (Interpret → Reconcile → Synthesize → Update)
|
||||
|
||||
@@ -316,14 +318,14 @@ So dass kein separates Summary pro Quelle entsteht und die gemischte Provenienz
|
||||
|
||||
As a Producer/Compiler,
|
||||
I want auf `lease/<area>/<id>`-Branches mit Root-Scope-Lease und Dirty-Tree-Schutz zu arbeiten,
|
||||
So dass zwei Producer denselben Concept-Pfad nicht stillschweigend überschreiben und Fremdänderungen nie als Nebenwirkung gelöscht werden (AD-17, AD-17a..f, A0-12..A0-16).
|
||||
So dass zwei Producer denselben Concept-Pfad nicht stillschweigend überschreiben und Fremdänderungen nie als Nebenwirkung gelöscht werden (AD-17a, AD-17b, AD-17e/f, A0-12, A0-13, A0-16; semantische Kollisionsauflösung in Epic 4).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** ein Producer, **When** er einen Bereich bearbeitet, **Then** arbeitet er auf einem `lease/<area>/<id>`-Branch und akquiriert die Lease gegen einen eindeutigen Commit-Object-Wert (Merge-Base-Disziplin) (AD-17a, A0-12).
|
||||
**Given** eine Lease, **When** sie vergeben ist, **Then** umfasst sie die Root-Scope inklusive `log.md`, `index.md` und aller Root-Dateien (AD-17b, A0-13).
|
||||
**Given** eine vorliegende uncommittete Fremdänderung im zu mutierenden Bereich, **When** der Producer mutieren will, **Then** schützt er sie (Stash/Scratch-Zone) und dokumentiert den Vorgang in `log.md` (AD-17e, A0-16).
|
||||
**Given** zwei Branches mit Änderungen am selben Concept-Pfad, **When** gemerged werden soll, **Then** erfolgt kein stiller textueller Auto-Merge (AD-17c, A0-14) — Auflösung compiler-vermittelt über AD-16 mit explizitem `log.md`-Eintrag.
|
||||
**Given** zwei Branches mit ungleichen Änderungen am selben Concept-Pfad, **When** vor Epic 4 ein Merge versucht wird, **Then** erfolgt kein textueller Auto-Merge; der Run endet mit einem strukturierten Kollisions-Hold, der beide Commit-Hashes und den betroffenen Scope erhält. AD-16-Klassifikation und semantische Auflösung folgen in Epic 4 (Sicherheitsgrenze von AD-17c; vollständige Auflösung in Story 4.1/4.2).
|
||||
**And** Mutationen operieren nur auf Directory-/Commit-Ebene — Commit-Boundary ist die Mutation-Boundary (AD-17f, A0-16).
|
||||
|
||||
### Story 3.6: Lease-Staleness & Recovery-Basis absichern
|
||||
@@ -356,21 +358,104 @@ So dass ein teilweise fehlgeschlagener Run nie ein inkonsistentes Bundle hinterl
|
||||
|
||||
As a Compiler,
|
||||
I want dass derselbe Git-State + dieselbe Eingabemenge bei zwei unabhängigen Runs denselben Bundle-State erzeugt,
|
||||
So dass die AD-16-Klassifikation deterministisch genug ist (AD-17h, FT-10, A0-19).
|
||||
So dass Relevanz-, Routing-, Planungs- und Mutationsentscheidungen reproduzierbar sind (AD-17h, FT-10, A0-19).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** einen Fixed Git-State und eine feste Eingabemenge, **When** zwei unabhängige Runs ausgeführt werden, **Then** produzieren sie identische Bundle-Zustände (FT-10, AD-17h).
|
||||
**Given** eine Abweichung bei zwei solchen Runs, **When** sie festgestellt wird, **Then** wird sie als Fehler im AD-16-Klassifikations-Mechanismus behandelt (nicht als akzeptables Rauschen) (AD-17h).
|
||||
**Given** der MVP (D-3), **When** die Determinsmus-Enforcement fehlt, **Then** lebt sie als Agent-Instruktions-Validator und ist vor Last tragenden Anspruch als mechanisch bestätigt (Q-6, A0-19).
|
||||
**Given** eine Abweichung bei zwei solchen Runs, **When** sie festgestellt wird, **Then** wird sie als Determinismusfehler behandelt und der Run gilt als fehlgeschlagen — nicht als akzeptables Rauschen (AD-17h).
|
||||
**Given** der MVP (D-3), **When** der Agent-Instruktions-Validator ausgeführt wird, **Then** laufen die beiden Ausführungen in getrennten sauberen Worktrees und frischen Agent-Kontexten; eine zweite Ausführung in derselben Session genügt nicht (Q-6, A0-19).
|
||||
**Given** das kanonische Eingabemanifest, **When** ein Run geplant wird, **Then** hält es Baseline-Commit, geordnete Source-Eingaben und jeden im Bundle sichtbaren Run-/Zeit-/Identitätswert explizit fest; der Run-Receipt enthält Candidate-Liste, Reihenfolge, Plan, Entscheidungen und Output-Hashes außerhalb des Knowledge Bundle.
|
||||
**Given** der Zwei-Run-Nachweis, **When** Pläne und Bundle-State verglichen werden, **Then** dürfen weder erwartete Pläne noch Concept-Bodies im Test hart codiert werden; `verified`-Ereignisse werden niemals pauschal aus dem Vergleich maskiert.
|
||||
**And** der Validator hält keine Embedding-/Vector-Infrastruktur vor (AD-13; FT-3, FT-4).
|
||||
|
||||
### Story 3.9: Deterministische Relevanz- und Reconcile-Routing schließen
|
||||
|
||||
As a Compiler,
|
||||
I want Evidenz deterministisch bestehenden, neuen oder nicht klassifizierbaren Wissenseinheiten zuordnen,
|
||||
So that keine relevante Information still ignoriert, dupliziert oder falsch verwaist wird (FR-4, FR-5-Interaktion, FR-6, FR-12, AD-5, AD-13, A0-6, A0-18).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** einen committeten Git-State und ein kanonisches Eingabemanifest, **When** Candidate-Terme gewonnen werden, **Then** folgt die Gewinnung einem geschlossenen, geordneten Algorithmus oder einem expliziten, persistierten Term-Manifest — keine freie Producer-Auswahl.
|
||||
**Given** semantisch gleiche Schreibweisen mit Groß-/Kleinschreibung, Leerzeichen, Unterstrich, Bindestrich, En-Dash oder Em-Dash, **When** die Stufe-a-Suche läuft, **Then** werden Suchterm und Concept-Body identisch normalisiert und literal-sicher verglichen; `index.md`-Treffer bleiben für die Traversal-Stufe erhalten.
|
||||
**Given** interpretierte Evidenz, **When** Reconcile abgeschlossen wird, **Then** gilt genau eine Routing-Tabelle: bestehender Match → `UPDATE`; eigenständige neue Wissenseinheit → `CREATE`; nicht klassifizierbare Evidenz → expliziter `ORPHAN/HOLD`; bereits vollständig repräsentierte identische Evidenz → `NO_OP`.
|
||||
**Given** eine geänderte oder gelöschte bereits committete Datei unter `raw/`, **When** der Run seine Eingaben prüft, **Then** schlägt er vor jeder Mutation fehl; akzeptiert werden nur neu hinzugefügte oder separat versionierte Sources (AD-3).
|
||||
**Given** ein neues Concept-Ziel, **When** dessen Slug `index` oder `log` beziehungsweise ein anderer reservierter Bundle-Name wäre, **Then** wird das Ziel nicht geschrieben und ein deterministischer Hold verlangt eine disambiguierte Identität.
|
||||
**And** gleicher Git-State plus gleiches Eingabemanifest erzeugt dieselbe Candidate-Liste, Reihenfolge und Routing-Entscheidung (AD-17h, A0-19), belegt durch positive und negative ausführbare Fixtures.
|
||||
|
||||
### Story 3.10: Inkrementelle Update- und Synthese-Erhaltung absichern
|
||||
|
||||
As a Compiler,
|
||||
I want bestehendes Wissen semantisch erweitern und mehrere Sources kohärent synthetisieren,
|
||||
So that neues Wissen integriert wird, ohne gültiges vorhandenes Wissen oder Provenienz zu verlieren (FR-4, FR-6, FR-7, FR-12, AD-4, AD-5).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** eine passende neue Erkenntnis zu einem bestehenden Concept, **When** der Run sie verarbeitet, **Then** wird das bestehende Concept in-place erweitert oder präzisiert und kein thematisches Duplikat angelegt.
|
||||
**Given** explizite aktuelle Evidenz, die eine bestehende Aussage eindeutig korrigiert, ohne dass zwischen weiterhin gültigen Sources ein Widerspruch verbleibt, **When** der Run sie verarbeitet, **Then** wird das bestehende Concept in-place korrigiert und die ersetzte Aussage samt Source-Basis bleibt im Run-Receipt nachvollziehbar; mehrdeutige Fälle gehen in den Hold für Epic 4.
|
||||
**Given** ein betroffenes Concept mit weiterhin gültigen Aussagen und Provenienz, **When** es aktualisiert wird, **Then** bleiben diese geschützten Bestandteile erhalten; nicht betroffene Concepts bleiben byte-identisch.
|
||||
**Given** eine neue Source, die eine bestehende Aussage unabhängig bestätigt, **When** synthetisiert wird, **Then** erscheint die Aussage genau einmal und trägt alle beitragenden Evidenzanker; die neue Source ist kein `NO_OP`.
|
||||
**Given** mehrere Sources mit redundanten und ergänzenden Aussagen, **When** der Run synthetisiert, **Then** entsteht eine gemeinsame Wissensrepräsentation mit claim-granularer gemischter Provenienz — keine Source-A/Source-B-Aneinanderreihung.
|
||||
**Given** eine bereits vollständig repräsentierte identische Source samt Evidenzanker, **When** sie erneut verarbeitet wird, **Then** ist der Run für dieses Wissen byte-erhaltend (`NO_OP`).
|
||||
**Given** der Provenienz- und Link-Selbsttest, **When** erwartete Deltas bestimmt werden, **Then** stammen Baseline und Erwartung aus dem aktuellen Run; kein historischer, fest codierter Commit oder globaler Zählwert ist normativ.
|
||||
**And** klassifikationspflichtige oder widersprüchliche Evidenz wird bis Epic 4 ohne Wissensmutation in einem benannten Hold erhalten; beide Evidenzpfade bleiben im Run-Receipt nachvollziehbar (NFR-7).
|
||||
|
||||
### Story 3.11: Root-Scope-Leasing atomar und worktree-übergreifend akquirieren
|
||||
|
||||
As a Producer,
|
||||
I want pro kanonischem Scope genau eine atomare Lease erwerben,
|
||||
So that verschiedene Run-IDs niemals gleichzeitig Schreibzugriff auf dasselbe Knowledge Bundle erhalten (AD-17a, AD-17b, A0-12, A0-13).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** den Root-Scope `wiki/`, **When** eine Lease akquiriert wird, **Then** existiert genau ein scope-bezogener Lock im clone-geteilten Zustand; die Run-ID ist Lock-Inhalt und nicht Teil des Exklusivitätsschlüssels.
|
||||
**Given** zwei Producer mit verschiedenen IDs und Worktrees, **When** beide denselben Root-Scope akquirieren, **Then** ist die Akquise atomar und genau ein Producer erhält die Lease; der andere erhält `LEASE_HOLD`.
|
||||
**Given** einen abgewiesenen Producer, **When** sein Lauf endet, **Then** verändert er weder `wiki/` noch den bestehenden Lock, erzeugt keinen Compilation Commit und entfernt keine fremde Lease.
|
||||
**Given** einen realen Zwei-Worktree-/Zwei-Prozess-Test, **When** die Akquise zeitlich überlappt, **Then** beweisen Zwischenzustands-Assertions, dass niemals zwei aktive Root-Leases gleichzeitig existieren.
|
||||
**And** ungleiche Änderungen am selben Concept-Pfad werden nicht automatisch gemerged, sondern als strukturierter Kollisions-Hold mit beiden Commit-Hashes an Epic 4 übergeben.
|
||||
|
||||
### Story 3.12: Lease-Lifecycle und Commit-Abschluss transaktional schließen
|
||||
|
||||
As an Operator,
|
||||
I want Akquise, Dirty-Tree-Schutz, Mutation, Rollback und Freigabe als konsistenten Lifecycle ausführen,
|
||||
So that SUCCESS und FAIL jeweils einen sauberen, wiederanlaufbaren Zustand hinterlassen (AD-6, AD-17d..17f, A0-7, A0-15, A0-16).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** einen lebenden Lease-Halter, **When** eine höhere Generation sichtbar wird, **Then** bleibt seine Lease aktiv; Staleness verlangt bestätigten Abbruch oder abgelaufene Liveness sowie atomare Ownership-Prüfung.
|
||||
**Given** eine nachweislich stale Lease, **When** sie übernommen wird, **Then** gelingt die Übernahme genau einmal, nennt die ersetzte Holder-ID und hinterlässt genau eine aktive Root-Lease.
|
||||
**Given** getrackte oder ungetrackte fremde Änderungen im Mutationsbereich, **When** der Preflight läuft, **Then** folgt er einer eindeutigen Abort-/Protect-Zustandsmaschine und stellt geschützte Bytes nach dem Run vollständig wieder her.
|
||||
**Given** einen Fehler nach Mutation oder Staging, **When** Rollback läuft, **Then** restauriert er explizit aus dem bezeichneten Baseline-Commit sowohl Index als auch Worktree; der Post-Rollback-Diff gegen die Baseline ist leer.
|
||||
**Given** einen erfolgreichen Run, **When** er freigegeben wird, **Then** sind Mutation, zulässiger Log-/Koordinationsnachweis und Release dauerhaft, der aktive Lock ist entfernt, der Worktree ist sauber und der unmittelbar folgende Run besteht den Clean-Input-Guard.
|
||||
**Given** `wiki/log.md`, **When** ein Run protokolliert wird, **Then** enthält das kanonische Knowledge Log nur vertragskonforme fachliche Änderungen und notwendige Koordinationsereignisse; Build-, Review-, Story- und Sandbox-Historie liegt außerhalb des Knowledge Bundle.
|
||||
**And** Kill-Point-Tests vor Mutation, nach Mutation, vor Commit und nach Commit beweisen den jeweils konsistenten Endzustand.
|
||||
|
||||
### Story 3.13: Epic-3-Verifikations- und Abnahmegate
|
||||
|
||||
As a Product Owner/Compiler-Verantwortlicher,
|
||||
I want Epic 3 durch einen realen, reproduzierbaren Source→Compilation→Wiki-Lauf qualifizieren,
|
||||
So that seine Fertigstellung durch ausführbare Evidenz statt handgeschriebene Simulationen belegt ist (SM-1, SM-2, FT-6, FT-10).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Given** einen sauberen Checkout, **When** das repositoryweite Epic-3-Gate ausgeführt wird, **Then** führt ein Kommando alle Epic-3-Szenarien fail-fast aus; fehlgeschlagene Assertions, übersprungene Szenarien, fehlende Abhängigkeiten und Kindprozessfehler ergeben einen Non-Zero-Exit.
|
||||
**Given** macOS/BSD- und Linux/GNU-Userland, **When** das Gate dort läuft, **Then** ist es ohne plattformspezifisches `sed -i` reproduzierbar, arbeitet ausschließlich in temporären Repositories und hinterlässt `git status --porcelain` unverändert.
|
||||
**Given** positive und negative OKF-Fixtures, **When** validiert wird, **Then** wird der vollständige Vertrag aus `schema/validator.md` über alle `wiki/`-Dateien ausgeführt; unter anderem fehlendes `generated.by`, kalenderinvalides `generated.at`, unzulässige Source-Pfade und gebrochene Index-Links verhindern SUCCESS und Commit.
|
||||
**Given** der autorisierte Validator-Vertrag oder seine ausführbare Aufrufbarkeit weist dabei ein Defizit auf, **When** das Gate es erkennt, **Then** bleibt Story 3.13 offen und benennt den Bedarf für eine separat autorisierte Epic-1-Remediation; Story 3.13 darf die Schema-Semantik nicht still ändern, verantwortet aber die Integration und vollständige Ausführung des bestehenden Vertrags.
|
||||
**Given** eine repräsentative committete Fixture, **When** ein frischer Agent-Kontext die kanonische Instruktion `schema/compiler.md` ausführt, **Then** darf der Harness nach dem Setup weder erwartete Wiki-Bodies noch Lease-, Log- oder Git-Ausgänge selbst schreiben.
|
||||
**Given** diese Fixture, **When** der Compilation Run endet, **Then** demonstriert er Update eines bestehenden Concepts, Anlage einer neuen Wissenseinheit, kohärente Multi-Source-Synthese, vollständige Multi-Evidenz-Provenienz, Source-Immutabilität und byte-identische Erhaltung unabhängigen Wissens.
|
||||
**Given** dasselbe kanonische Eingabemanifest, **When** zwei frische Agent-Kontexte in getrennt aufgebauten Worktrees laufen, **Then** stimmen Run-Receipts und Bundle-State gemäß Story 3.8 überein; eine perturbierte Entscheidung oder Ausgabe wird erkannt.
|
||||
**Given** nur das erzeugte Knowledge Bundle ohne Planungsartefakte, **When** ein unabhängiger Consumer im Epic-5-Abhängigkeits-Smoke-Test vordefinierte Wissensfragen beantwortet, **Then** benötigt er weder Product Brief, SPEC, Architecture Spine noch Story-Historie zum semantischen Verständnis; solche Referenzen sind höchstens optionale Provenienz/Traceability. Dieser Smoke-Test nimmt die vollständige FR-15/FR-16-Abnahme aus Epic 5 nicht vorweg.
|
||||
**And** erst nach bestandenem Gate und `done`-Status aller Stories 3.8 bis 3.13 wird Epic 3 auf `done` gesetzt; bis dahin bleibt Epic 3 `in-progress`.
|
||||
|
||||
---
|
||||
|
||||
## Epic 4: Wissenstreue — Widersprüche & menschliche Kuratierung
|
||||
|
||||
Widersprüche werden nie stillschweigend zur scheinbar eindeutigen Aussage zusammengeführt; relevante Disagreements bleiben als explizite Einträge in `log.md` erhalten; menschlich kuratierte Inhalte werden als bestehendes Wissen respektiert und bleiben über OKF-Trust-Metadaten (`verified: human:...`) von ungeprüftem maschinellem Output unterscheidbar; unvollständiges/ungeprüftes Wissen wird ohne künstliche Gewissheit dargestellt.
|
||||
**FRs covered:** FR-8, FR-13 · **NFRs:** NFR-7 · **AD/A0:** AD-16, AD-16a, AD-16b, AD-15, A0-11, A0-17, A0-20
|
||||
Epic 4 beginnt nach bestandenem Story-3.13-Abnahmegate und übernimmt die in Epic 3 fail-closed erhaltenen Kollisions-/Widerspruchs-Holds zur semantischen Klassifikation und Auflösung.
|
||||
**FRs covered:** FR-8, FR-13 · **NFRs:** NFR-7 · **AD/A0:** AD-16, AD-16a, AD-16b, AD-15, AD-17c, AD-17g, A0-11, A0-14, A0-17, A0-20, A0-21
|
||||
|
||||
### Story 4.1: Information vor jeder Änderung klassifizieren (NEW/CONFIRMING/CORRECTING/CONTRADICTING/REDUNDANT)
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Sprint Change Proposal — Epic 3 Completion Remediation
|
||||
status: approved
|
||||
created: 2026-08-20
|
||||
approved_by: ProMods
|
||||
approval: "Explizite Chat-Freigabe: Ja"
|
||||
classification: moderate
|
||||
affected_epic: 3
|
||||
---
|
||||
|
||||
# Sprint Change Proposal: Epic 3 belastbar abschließen
|
||||
|
||||
## 1. Issue Summary
|
||||
|
||||
Der unabhängige Read-only-Audit nach Story 3.8 zeigte, dass Epic 3 seine bereits zugesagten Anforderungen und Architekturverträge noch nicht als kohärenten System-Inkrement belegt. Betroffen sind insbesondere deterministische Relevanz und Routing, semantische Update-/Synthese-Erhaltung, Root-Scope-Leasing, transaktionaler Run-Abschluss, AD-17h-Reproduzierbarkeit und die ausführbare End-to-End-Abnahme.
|
||||
|
||||
Der Trigger ist kein neuer Produktwunsch, sondern eine fehlgeschlagene Verifikation bestehender Zusagen aus FR-4, FR-6, FR-7, FR-12, AD-5/6/13/17 und A0-6/7/12..19. Konkrete Evidenz liegt in `schema/compiler.md`, den Story-3-Sandboxes, `wiki/log.md`, dem Sprint-Status und `epic-3-context.md` vor.
|
||||
|
||||
## 2. Impact Analysis
|
||||
|
||||
### Epic Impact
|
||||
|
||||
- Epic 3 bleibt `in-progress`; Story 3.8 wird aus `review` nach `in-progress` zurückgenommen.
|
||||
- Stories 3.9–3.13 werden innerhalb von Epic 3 ergänzt.
|
||||
- Ein neues Epic ist nicht gerechtfertigt: Alle Arbeiten schließen bereits zugesagte Epic-3-Verträge.
|
||||
- Epic 4 beginnt erst nach dem Story-3.13-Abnahmegate.
|
||||
|
||||
### Scope Boundary zu Epic 4
|
||||
|
||||
Epic 3 erkennt klassifikationspflichtige Kollisionen und Widersprüche und hält fail-closed an; dies ist seine Sicherheitsgrenze aus AD-17c/A0-14. Vollständige AD-16-Klassifikation, semantische Kollisionsauflösung (AD-17c/g) und Disagreement-Dokumentation verbleiben in Epic 4. A0-21 ist geteilt: Story 3.10 belegt die Erhaltung unabhängigen Wissens, Story 4.3 das Human-Curation-Verhalten/FT-9. Damit entfällt die bisherige zyklische Behauptung, Epic 3 sei abgeschlossen, obwohl sein Merge-/Resolution-Pfad Epic 4 voraussetzt.
|
||||
|
||||
### Artifact Impact
|
||||
|
||||
- PRD: keine Änderung.
|
||||
- Architecture Spine: keine Änderung; die Story-Zuordnung wird mit den bestehenden Entscheidungen konsistent gemacht.
|
||||
- `epics.md`: Scope-Korrektur, Story-3.8-Präzisierung und Stories 3.9–3.13.
|
||||
- `sprint-status.yaml`: Story 3.8 `in-progress`, neue Stories `backlog`.
|
||||
- `epic-3-context.md`: Story-Liste, technische Grenzen und Abhängigkeiten synchronisiert.
|
||||
- Code, Compiler-Instruktion, Tests und Knowledge Bundle werden durch diese Planungskorrektur nicht verändert.
|
||||
|
||||
## 3. Recommended Approach
|
||||
|
||||
**Gewählt: Direct Adjustment innerhalb von Epic 3.**
|
||||
|
||||
Rollback bereits abgeschlossener Stories würde die vorhandene Arbeit nicht vereinfachen. Eine PRD-/MVP-Neudefinition ist ebenfalls nicht nötig. Die kleinste kohärente Korrektur ist, die offenen systemischen Verantwortungen in klar geschnittene Remediation-Stories zu überführen, Story 3.8 ehrlich wieder zu öffnen und ein finales ausführbares Abnahmegate einzuführen.
|
||||
|
||||
Aufwand: mittel bis hoch. Risiko: mittel bei Umsetzung vor Epic 4; hoch, falls Epic 4 auf dem derzeit unzuverlässigen Lease-/Log-/Compiler-Unterbau beginnt.
|
||||
|
||||
## 4. Detailed Change Proposals
|
||||
|
||||
### Bestehende Story
|
||||
|
||||
- **Story 3.8 — Determinismus-Vertrag:** wieder öffnen; zwei getrennte Worktrees und frische Agent-Kontexte, kanonisches Eingabemanifest, rekonstruierbarer Run-Receipt, keine hart codierten Outputs und keine pauschale Maskierung von Trust-Ereignissen.
|
||||
|
||||
### Neue Stories
|
||||
|
||||
1. **Story 3.9 — Deterministische Relevanz- und Reconcile-Routing schließen**
|
||||
|
||||
Schließt Termgewinnung, symmetrische Normalisierung, literal-sichere Suche, exklusive Routing-Tabelle, Raw-Immutability-Guard und reservierte Zielpfade.
|
||||
|
||||
2. **Story 3.10 — Inkrementelle Update- und Synthese-Erhaltung absichern**
|
||||
|
||||
Beweist In-place-Update, Erhaltung vorhandenen Wissens, vollständige Multi-Evidenz-Provenienz, echten No-op und kohärente Synthese ohne historische Hardcodings.
|
||||
|
||||
3. **Story 3.11 — Root-Scope-Leasing atomar und worktree-übergreifend akquirieren**
|
||||
|
||||
Realisiert einen scope-bezogenen atomaren Lock und echte konkurrierende Worktree-Tests für unterschiedliche Run-IDs.
|
||||
|
||||
4. **Story 3.12 — Lease-Lifecycle und Commit-Abschluss transaktional schließen**
|
||||
|
||||
Schließt Staleness/Liveness, Dirty-Tree-Schutz, Baseline-Rollback, durable Release, Clean-Next-Run und die Grenze des kanonischen Knowledge Logs.
|
||||
|
||||
5. **Story 3.13 — Epic-3-Verifikations- und Abnahmegate**
|
||||
|
||||
Führt portable fail-fast Verifikation, vollständigen OKF-Validator, einen realen Agent-Instruktionslauf, unabhängige Reproduktion und einen Epic-5-Abhängigkeits-Smoke-Test zusammen.
|
||||
|
||||
### Reihenfolge
|
||||
|
||||
```text
|
||||
3.9 → 3.10 ┐
|
||||
├→ 3.8 Abschluss → 3.13 Abnahme → Epic 4
|
||||
3.11 → 3.12┘
|
||||
```
|
||||
|
||||
## 5. Implementation Handoff
|
||||
|
||||
**Klassifikation:** Moderate Backlog-Reorganisation, anschließend Developer-/Review-Ausführung.
|
||||
|
||||
- Product Owner: bestätigt Story-Schnitt und Scope-Grenze — erfolgt am 2026-08-20.
|
||||
- Developer: implementiert Stories in der angegebenen Abhängigkeitsreihenfolge.
|
||||
- Reviewer: prüft jede Story gegen ihre ausführbaren Negativ- und Positivbelege; Story 3.13 ist die Epic-Abnahme.
|
||||
- Epic 3 wird erst `done`, wenn Story 3.8 und Stories 3.9–3.13 `done` sind und das Story-3.13-Gate erfolgreich ist.
|
||||
|
||||
## 6. Checklist Record
|
||||
|
||||
- [x] Trigger und konkrete Audit-Evidenz verstanden
|
||||
- [x] Auswirkungen auf Epic 3, Epic 4 und Epic 5 geprüft
|
||||
- [x] PRD-/Architecture-Konflikte geprüft; keine Änderung erforderlich
|
||||
- [x] Direct Adjustment gegen Rollback und MVP-Neuschnitt bewertet
|
||||
- [x] Story-Schnitt, Reihenfolge und Handoff festgelegt
|
||||
- [x] Explizite Nutzerfreigabe erhalten
|
||||
- [x] Sprint-Status und Epic-Kontext synchronisiert
|
||||
+89
-9
@@ -38,7 +38,7 @@ Bestätigung (Story 1.4): schema/validator.md (mechanische Prüfung, kein L
|
||||
|
||||
1. Vor der Anlage prüfen, ob die erkannte Wissenseinheit **bereits als Concept** im Bundle existiert (deterministisch: Dateikollision über den relativen OKF-Pfad, AD-7a).
|
||||
2. **Update-Routing (statt Kollision-Hold; Story 3.1):** Existiert bereits ein Concept mit dem Ziel-Pfad, wird **nicht** stumm überschrieben und **kein** Duplikat angelegt — die erkannte Wissenseinheit wird als **Update-Kandidat** im **bestehenden Concept-Pfad** aktualisiert (Erweitern/Präzisieren/Korrigieren gemäß §5.9; FR-6). Die Mutationsmechanik für Updates spezifiziert §5.9; der Kollisions-Hold-Schutzprinzip („nicht stumm überschreiben") bleibt als Grundsatz der Erhaltung erhalten (AD-16-Default: bestehende Provenienz/Inhalte werden nie ohne Beleg entfernt). Der **Neu-Anlage-Pfad** dieser Instruktion bleibt für Wissenseinheiten, deren Ziel-Pfad **nicht** belegt ist (§5.1/§5.7).
|
||||
**Kandidatenliste (betroffen-Bestimmung):** Vor jeder Mutation erhebt der Producer die Menge der betroffenen Concepts als **nachvollziehbare Kandidatenliste** (relative OKF-Pfade ohne `.md`) mit **textuell-deterministischen Mitteln** (AD-13): (a) Term-/Konzept-Überschneidung zwischen der neuen Evidenz und den bestehenden Concept-Bodies via `grep`/`ripgrep` über `wiki/` (z. B. `rg -l '<term>' -g '!log.md' wiki/` bzw. GNU-grep-Form `grep -rl '<term>' --exclude=log.md wiki/`); (b) `index.md`-Traversal (Bundleroot- und Area-`index.md`-Dateien, gewurzelte Erreichbarkeit Root → Area → Concept, §5.8) der dadurch betroffenen Bereiche; (c) Link-Following aus bereits betroffenen Concepts (§5.6-Pin) auf weitere Concept-Pfade — **mit besuchter Menge** (ein bereits besuchter Concept-Pfad wird nicht erneut besetzt; keine Schleife bei zyklischen Links). Die Erhebung folgt der feinkörnigen, verbindlichen Mechanik in **§3.2 Relevanzbestimmung (Story 3.2)** — Term-Ziehverfahren (deterministisch aus der neuen Evidenz abgeleitet, kanonischer Schreibweisen-Resolver `schema/canonical-terms.md`, mehrere Terme je Einheit), dreistufige Erhebung mit `log.md`-Exklusion (die Candidate-Liste ist auf Concept-Pfade definiert, `log.md` ist kein Kandidat) und Determinismus-Vertrag (AD-17h/A0-19) — **Erhebung nach §3.2**. Die Kandidatenliste wird textuell festgehalten (Pre-Run-Reconcile-Check-Block, §5.9 Pkt. 6). Keine Embeddings/Vector-Suche (AD-13); §5.9 bindet die Erhebung an die in §3.2 genannten deterministischen Mittel.
|
||||
**Kandidatenliste (betroffen-Bestimmung):** Vor jeder Mutation erhebt der Producer die Menge der betroffenen Concepts als **nachvollziehbare Kandidatenliste** (relative OKF-Pfade ohne `.md`) mit **textuell-deterministischen Mitteln** (AD-13): (a) Term-/Konzept-Überschneidung zwischen der neuen Evidenz und den bestehenden Concept-Bodies via `grep`/`ripgrep` über `wiki/` (z. B. `rg -l '<term>' -g '!log.md' wiki/` bzw. GNU-grep-Form `grep -rl '<term>' --exclude=log.md wiki/`); (b) `index.md`-Traversal (Bundleroot- und Area-`index.md`-Dateien, gewurzelte Erreichbarkeit Root → Area → Concept, §5.8) der dadurch betroffenen Bereiche; (c) Link-Following aus bereits betroffenen Concepts (§5.6-Pin) auf weitere Concept-Pfade — **mit besuchter Menge** (ein bereits besuchter Concept-Pfad wird nicht erneut besetzt; keine Schleife bei zyklischen Links). Die Erhebung folgt der feinkörnigen, verbindlichen Mechanik in **§3.2 Relevanzbestimmung (Story 3.2)** — Term-Ziehverfahren (deterministisch aus der neuen Evidenz abgeleitet, kanonischer Schreibweisen-Resolver `schema/canonical-terms.md`, mehrere Terme je Einheit), dreistufige Erhebung mit `log.md`-Exklusion (die Candidate-Liste ist auf Concept-Pfade definiert, `log.md` ist kein Kandidat) und Determinismus-Vertrag (AD-17h/A0-19) — **Erhebung nach §3.2**. Die Kandidatenliste wird textuell festgehalten (Pre-Run-Reconcile-Check-Block, §5.9 Pkt. 6). Keine Embeddings/Vector-Suche (AD-13); §5.9 bindet die Erhebung an die in §3.2 genannten deterministischen Mittel; **der Stufe-a-Match-Scope (ganze Wörter über den Body, exklusive YAML-Frontmatter, §3.2-Pkt.-1b-Schließung/§5.14 Pkt. 5) gilt für die Stufe-a-Form hier unverändert**.
|
||||
Löst eine erkannte Wissenseinheit auf **keinen** bestehenden Concept-Pfad auf (kein Update-Kandidat), wird sie als **neue** Einheit über den Neu-Anlage-Pfad (§5.1/§5.7, §3-Pkt.-1/-2-Kollisionsprüfung ist damit erstbestanden) behandelt.
|
||||
3. Der Run prüft zusätzlich, ob `wiki/index.md` als Bundleroot existiert (V-1-Vorbedingung des Validators); fehlt sie, darf kein Concept erzeugt oder aktualisiert werden (Run-FAIL, Vertrag §2).
|
||||
|
||||
@@ -48,11 +48,12 @@ Diese Sektion ist der **einzige Instruktions-Ort** der feinkörnigen, **textuell
|
||||
|
||||
1. **Term-Ziehverfahren (deterministisch):** Die Candidate-Terme werden deterministisch aus der neuen Evidenz (committete `raw/`-Dateien, §1 Pkt. 1/2) abgeleitet — nicht freie LLM-Auswahl:
|
||||
- **(a) Bedeutungstragende Token-Folgen:** Der Producer benennt die bedeutungstragenden Fachbegriffe der Wissenseinheit gemäß der §2-Interpretation (fachliche Signifikanz; kein Stoppwort-Abgleich nötig, aber auch kein freies Urteil). Der Umfang „bedeutungstragend" ist die Auswahl derjenigen Begriffe, die das erkannte Thema identifizieren — als Token-Folgen über eine sprachliche Einheit hinweg zulässig (z. B. `quanten-protocol-schlüssel`).
|
||||
- **(b) Normalisierung über den kanonischen Schreibweisen-Resolver:** Jeder gezogene Term wird durchgängig normalisiert: (i) lowercasing; (ii) Binde-Varianten-Kollaps `[-–_ ]` → `-` (En-Dash `–`, Bindestrich `-`, Unterstrich `_`, Leerzeichen — jedes Vorkommen wird in einen einzelnen Bindestrich kollabiert); (iii) Auflösung über die committete, append-only Registry **`schema/canonical-terms.md`** (ein Eintrag = canonische Form + erlaubte Schreibvarianten; der Resolver ist damit Bestandteil des Git-States und die Auflösung pinbar). **Genau eine canonische Form je Semantik** (A0-18). **Kein stiller Ausschluss:** ist eine Variante nicht in der Registry auflösbar, wird der Term **wie notiert** verwendet (Kollaps-normalisiert) — niemals still verworfen.
|
||||
- **(b) Normalisierung über den kanonischen Schreibweisen-Resolver:** Jeder gezogene Term wird durchgängig normalisiert: (i) lowercasing; (ii) Binde-Varianten-Kollaps `[-–— _]` → `-` (En-Dash `–`, Em-Dash `—`, Bindestrich `-`, Unterstrich `_`, Leerzeichen — jedes Vorkommen wird in einen einzelnen Bindestrich kollabiert; die Kollaps-Reichweite und die übrigen Schließungen definiert der Append-Block weiter unten — **maßgeblich für den Kollaps ist die geschlossene Kollaps-Klasse `[-–— _]`**); (iii) Auflösung über die committete, append-only Registry **`schema/canonical-terms.md`** (ein Eintrag = canonische Form + erlaubte Schreibvarianten; der Resolver ist damit Bestandteil des Git-States und die Auflösung pinbar). **Genau eine canonische Form je Semantik** (A0-18). **Kein stiller Ausschluss:** ist eine Variante nicht in der Registry auflösbar, wird der Term **wie notiert** verwendet (Kollaps-normalisiert) — niemals still verworfen.
|
||||
- **(c) Mehrere Terme je Einheit erlaubt:** Eine Wissenseinheit kann mehrere bedeutungstragende Terme tragen; die Candidate-Liste ist dann die **Vereinigung** der Treffer über alle Terme, bereinigt über die besuchte Menge (Pkt. 3c — ein Pfad nur einmal).
|
||||
- **Bekannte Determinismus-Lücke (aufgezeichnet, nicht still hinzugefügt):** Die Kollaps-Klasse `[-–_ ]` deckt den Em-Dash `—` **nicht** ab (nur En-Dash `–`). Em-Dash-Varianten fallen damit nicht unter den Kollaps — eine erkannte Synonym-Lücke, die **Story 3.8** als offene Determinismus-Frage übergeben ist (s. `deferred-work.md`; nicht stillschweigend in §3.2 ergänzt).
|
||||
- **Geschlossene Determinismus-Lücken (Story 3.8; deterministische Regel-Ergänzungen, §5.14):** (a) **Em-Dash in der Kollaps-Klasse** — die Kollaps-Klasse ist um den Em-Dash `—` **ergänzt** (`[-–— _]` → `-`): En-Dash `–`, Em-Dash `—`, Bindestrich `-`, Unterstrich `_` und Leerzeichen kollabieren identisch auf **genau einen** Bindestrich — eine Schreibvariante mit Em-Dash (z. B. `wissen — relevanz`) erhält damit dieselbe canonische Form wie ihre En-Dash-/Bindestrich-/Unterstrich-/Leerzeichen-Variante (kein stiller Ausschluss, keine unterschiedliche canonische Form je Separator; §5.14-Abweichungs-Klassifikation). (b) **Kollaps-Reichweite (deterministisch):** jedes Separator-Vorkommen wird auf **genau ein `-`** kollabiert; Läufe (`a--b`) kollabieren auf ein `-` (`a-b`); führende (`-x` → `x`) und trailende (`x-` → `x`) Separatoren werden getrimmt. (c) **Match-Scope der Stufe a (Pkt. 2a):** Stufe a matcht **ganze Wörter** über den **Body** des Concepts, **exklusive YAML-Frontmatter** — Substring-Treffer und Frontmatter-Treffer (z. B. in `sources[].resource`/`generated.by`) liefern **keine** Kandidaten (deterministischer Stufe-a-Scope, §5.14; fixiert das §3.2-Pkt.-2a-grep ohne Wortgrenzen-/Frontmatter-Klausel). Die Schließung erhebt **keinen** neuen Prädikat-/Format-/Frontmatter-Key und ändert **keinen** bestehenden §3.2-Wortlaut (append-only-Regel-Ergänzung; AD-3, D-3).
|
||||
2. **Term-übergreifende Erhebung über `wiki/` (drei Stufen):** Der Producer erhebt die betroffenen Concept-Pfade in **drei textuell-deterministischen Stufen** (a → b → c). Ab der Workspace-Root:
|
||||
- **(a) Stufe a — grep/ripgrep über `wiki/`:** `rg -l '<term>' -g '!log.md' wiki/` (rgs native Glob-Exklusions-Syntax — `--exclude` ist kein rg-Flag; das GNU-grep-Äquivalent ist `grep -rl '<term>' --exclude=log.md wiki/`, §5.6-Scan-Scope-Konvention). `<term>` = jeder gezogene Term aus Pkt. 1 nach Normalisierung. Beide Formen exkludieren `log.md` **strukturell** (unabhängig von dessen Inhalt) — die Candidate-Liste bleibt auf Concept-Pfade definiert.
|
||||
- **(a) Stufe a — grep/ripgrep über `wiki/`:** `rg -l '<term>' -g '!log.md' wiki/` (rgs native Glob-Exklusions-Syntax — `--exclude` ist kein rg-Flag; das GNU-grep-Äquivalent ist `grep -rl '<term>' --exclude=log.md wiki/`, §5.6-Scan-Scope-Konvention). `<term>` = jeder gezogene Term aus Pkt. 1 nach Normalisierung. Beide Formen exkludieren `log.md` **strukturell** (unabhängig von dessen Inhalt) — die Candidate-Liste bleibt auf Concept-Pfade definiert. **Match-Scope (Pkt.-1b-Schließung, §5.14 Pkt. 5):** beide Formen erfüllen den Stufe-a-Match-Scope der geschlossenen Determinismus-Lücke — **ganze Wörter** über den **Body** (exklusive YAML-Frontmatter); Substring- und Frontmatter-Treffer (z. B. in `sources[].resource`/`generated.by`) liefern **keine** Kandidaten. Konsequent als **zweistufige, tool-portable Mechanik**: (1) **Sweep** mit der Formel je `<term>` (Substring-/dateiweite Suche — die Formel ist ein trichiger Erhebungsschritt, keine Scope-Filterung); (2) **deterministischer Scope-Filter** über die so gefundenen Pfade: der Producer prüft je Pfad, ob `<term>` als **ganzes Wort** im **Body** (Zeilen **nach der zweiten `---`-Zeile** — dem schließenden Frontmatter-Limit; die Frontmatter-Zeilen davor sind **nie Treffer-Ziel**) vorkommt — tool-portabel (z. B. rg `-w` über den Body bzw. GNU `grep -w`/`\b` nach `awk`-Frontmatter-Strip) — und verwirft Substring- und Frontmatter-only-Treffer deterministisch. `<term>` ist je Erhebung ein **einzelner** normalisierter Term (Pkt. 1); werden **mehrere Terme** je Einheit gezogen (Pkt. 1c), wird die Formel je Term ausgeführt und die Candidate-Liste ist die **Vereinigung** der Treffer, bereinigt über die besuchte Menge (Pkt. 3c) und in Zuwachs-Sicht-Ordnung (Pkt. 3b).
|
||||
- **(b) Stufe b — `index.md`-Traversal:** Für die in Stufe a getroffenen Bereiche (und die Bundleroot) folgt der Producer der gewurzelten Erreichbarkeit Root → Area → Concept (§5.8): trifft ein Term nur `wiki/index.md` oder eine Area-`index.md` (nicht einen Concept-Body), so sind alle **darunter gewurzelten Concept-Pfade** Treffer der Stufe b (TRAVERSAL_REACH_ONLY). Fehlende Bundleroot → Run-FAIL (V-1, §3 Pkt. 3, besteht fort).
|
||||
- **(c) Stufe c — Link-Following mit besuchter Menge:** Aus bereits als betroffen erhobenen Concepts folgt der Producer die Concept-Links (§5.6-Pin) auf weitere Concept-Pfade — file-relativ auflösen (§5.7 Pkt. 4), **jeder bereits besuchte Concept-Pfad wird nicht erneut besucht** (besuchte Menge): Zyklen (A → B → A) enden, die Candidate-Liste bleibt endlich (LINK_FOLLOWING_ZYKLUS).
|
||||
3. **Candidate-Liste (Ausgabe) + Determinismus-Vertrag:**
|
||||
@@ -298,7 +299,81 @@ Diese Sektion ist der **Instruktions-Ort der Synthese-Dimension** (AD-4, FR-7):
|
||||
5. **Reflektierter Wissensstand (FR-7 AC-4, NFR-7):** Der Body trägt **integrierte, je Aussage provenance-tags versehene Aussagen** — **keine per-Source-Zusammenfassungs-Struktur** (keine Blöcke „Quelle A: … / Quelle B: …"). **Reflektiertheits-Selbsttest (textuell deterministisch, grepbasiert):** es existiert **keine** Zeile, die einen Quell-Label abschnittsstrukturiert — das Muster ist ein Zeilenanfangs-Label „`Quelle <Label>:`"/„`Source <Label>:`" (einbuchstabig, mehrbuchstabig oder mit Ziffer/Unterstrich suffigiert), grepbasiert `grep -nE '^(Quelle|Source) [A-Z][A-Za-z0-9_ -]*:' <Synthese-Concept>` liefert leer; jede Aussage trägt ihren Provenienz-Tag (§5.5). Ein Befund (Aneinanderreihung) ist ein **Instruktions-Selbsttest-FAIL** (NFR-7, kein stiller Vorbeilass — NFR-4): der Producer stellt den Body vor Run-Abschluss so um, dass die Integration erfüllt ist.
|
||||
6. **§5.6-Pin unverändert:** Synthese fügt **keine neue Linkform** und **keine** neuen `index.md`-Links ohne echte, inhaltsbegründete Beziehung hinzu — §5.6-Pin (genau-eine-Linkform, file-relativ mit `.md`-Endung) und §5.9 Pkt. 3 (kein neuer Index-Link bei reinem Body-Update) gelten unverändert. **Form-Wahl-Klassifikationsprobe (Defer U2/U7, Story 3.3):** eine Synthese-Einheit, die zugleich neue + schärfende + ersetzende Anteile trägt (Form-Überlapp), wird per §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` **strikt pro Evidenz-Einheit** der **ersten zutreffenden Form** zugeordnet (deterministisch, AD-17h); die Textgenauigkeits-Rahmen der übrigen Formen gelten für die jeweiligen Teilbestandteile unverändert.
|
||||
7. **`log.md`-Eintragspflicht (Vertrag §5):** Jede Synthese wird als datumsgruppierter Eintrag in `wiki/log.md` dokumentiert. Ein Synthese-**Update** auf ein bestehendes Concept führt den Eintrag „**Story 3.1-Update**" (Disagreement-Fälle als Disagreement-Eintrag mit dem mutierten Concept-Pfad, AD-16b); eine Synthese-**Neu-Anlage** eines neuen Synthese-Concepts führt einen **Anlage-Eintrag** (disambiguierbar von Update-Einträgen). Der Eintrag verknüpft das erzeugte Concept mit der **vollständigen Multi-Source-`sources`-Liste** und hält den `<Baseline-Commit>` (R-1, §5.9 Pkt. 6) fest.
|
||||
8. **Erhaltungs-Invariante & Determinismus-Vertrag (AD-17h/A0-19):** Der **§5.9-Pkt.-5-Diff-Selbsttest gilt für Synthese-Runs unverändert**: die mutierten/neu angelegten Pfade sind eine Teilmenge von (Kandidatenliste ∪ Neu-Anlage-Zielpfade ∪ `log.md` ∪ nachgeführte `index.md`) — **keine neue Datei außer echten Ziel-Pfaden** (Duplikat-Kontrolle via `??`-Sicht, `git status --porcelain -- wiki/`). Gleicher Git-State + gleiche Eingabemenge → identischer Synthese-Vorgang (Ziel-Pfad via §3.2/§5.7, `sources`-Liste lexikografisch nach `resource` LC_ALL=C, Konsolidierung und Form-Zuordnung wie Pkt. 3/6); der `generated.at`-Wanduhr-Gap (gleiches Eingabeset, unabhängige Runs, verschiedene `at`) bleibt offene A0-20-Konvention mit Home **Story 3.8** (§5.9 Pkt. 2, `generated.at`-Konvention) — unverändert bindend. **Duplikat-Fall (Synchronisation zwischen Concepts und Evidenzbasis):** ein `raw/`-Quellpfad erscheint **nur einmal** in einer `sources`-Liste — ein bestehender Eintrag derselben `resource` wird **nicht** doppelt angelegt (die Neuanlage eines Synthese-Concepts setzt voraus, dass kein bestehendes Concept dieselbe Quelle bereits mit derselben Stellen-Kennung als Beleg nutzt; für Update-Fälle gilt das §5.9-Pkt.-2-Prinzip „bestehende Einträge bleiben unverändert" — der Zuwachs einer Quelle, die bereits existiert, wird niemals doppelt eingetragen). **Unzugeordnete/verwaiste Evidenz (Orphan-Kontrolle):** neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer bleibt **unzugeordnet** (weder still getilgt noch hintenherum als eigenes Concept angelegt) und wird in `log.md` als verwaist protokolliert — kein Banner/keine stille Vorbearbeitung; Story 3.8/Epic-4 können die Reconcile-Orphan-Politik präzisieren.
|
||||
8. **Erhaltungs-Invariante & Determinismus-Vertrag (AD-17h/A0-19):** Der **§5.9-Pkt.-5-Diff-Selbsttest gilt für Synthese-Runs unverändert**: die mutierten/neu angelegten Pfade sind eine Teilmenge von (Kandidatenliste ∪ Neu-Anlage-Zielpfade ∪ `log.md` ∪ nachgeführte `index.md`) — **keine neue Datei außer echten Ziel-Pfaden** (Duplikat-Kontrolle via `??`-Sicht, `git status --porcelain -- wiki/`). Gleicher Git-State + gleiche Eingabemenge → identischer Synthese-Vorgang (Ziel-Pfad via §3.2/§5.7, `sources`-Liste lexikografisch nach `resource` LC_ALL=C, Konsolidierung und Form-Zuordnung wie Pkt. 3/6); der `generated.at`-Wanduhr-Gap (gleiches Eingabeset, unabhängige Runs, verschiedene `at`) bleibt offene A0-20-Konvention mit Home **Story 3.8** (§5.9 Pkt. 2, `generated.at`-Konvention) — unverändert bindend. **Duplikat-Fall (Synchronisation zwischen Concepts und Evidenzbasis):** ein `raw/`-Quellpfad erscheint **nur einmal** in einer `sources`-Liste — ein bestehender Eintrag derselben `resource` wird **nicht** doppelt angelegt (die Neuanlage eines Synthese-Concepts setzt voraus, dass kein bestehendes Concept dieselbe Quelle bereits mit derselben Stellen-Kennung als Beleg nutzt; für Update-Fälle gilt das §5.9-Pkt.-2-Prinzip „bestehende Einträge bleiben unverändert" — der Zuwachs einer Quelle, die bereits existiert, wird niemals doppelt eingetragen). **Unzugeordnete/verwaiste Evidenz (Orphan-Kontrolle, pre-existing — dokumentiert den Vertrag, wie er vor dieser Story bestand):** neu committete `raw/`-Evidenz ohne Ziel-Pfad-Treffer bleibt **unzugeordnet** (weder still getilgt noch hintenherum als eigenes Concept angelegt) und wird in `log.md` als verwaist protokolliert — kein Banner/keine stille Vorbearbeitung; eine Präzisierung der Reconcile-Orphan-Politik leistete die Verankerung in §5.13-Phase 5 / **die deterministische Reconcile-Orphan-Regel unten (Story 3.8; §5.14 Pkt. 5)** — eine Staffelung an Epic-4 (Story 4.1) bleibt für die Verwaist-Klassifikation offen. **Deterministische Reconcile-Orphan-Regel (Story 3.8; §5.14-Präzisierung):** der Verwaist-Befund — neu committete `raw/`-Evidenz ohne jeglichen Ziel-Pfad-Treffer (kein Update-Kandidat nach §3.2, keine Synthese-Zuordnung nach Pkt. 1–7, kein §5.7-Neu-Anlage-Ziel-Pfad) — wird deterministisch aus dem committeten Git-State erhoben (Zuwachs gg. `<Baseline-Commit>`, §5.9 Pkt. 6 R-1) und als **datumsgruppierter `log.md`-Eintrag** verwaist protokolliert (Header `YYYY-MM-DD`, neueste zuerst — Vertrag-§5-Datumsgruppe; Quell-Pfad + `<Baseline-Commit>`); **kein Banner**, **keine stille Vorbearbeitung**, **keine** eigenständige Concept-Anlage aus verwaister Evidenz (AD-16-Default: Erhaltung — die Evidenz bleibt in `raw/` unangetastet, AD-3; keine Korrektur-/Erweiterungs-Klassifikation hier vorweggenommen — Epic-4-Interface, Story 4.1). Gleicher Git-State + gleiche Eingabemenge ⇒ identischer Verwaist-Befund und identischer `log.md`-Eintrag (AD-17h/A0-19, §5.14).
|
||||
|
||||
## 5.11 Leasing & Dirty-Tree-Schutz für konkurrierende Producer (Story 3.5)
|
||||
|
||||
Diese Sektion ist der **einzige Instruktions-Ort der Koordinations-Dimension für konkurrierende Producer** (D-3, Story 3.5; AD-17a..f, A0-12..A0-16, FR-2/FR-12): wie zwei Producer denselben Concept-Pfad **nicht stillschweigend überschreiben**, wie eine **Lease** auf `lease/<area>/<id>`-Branches mit Root-Scope und Merge-Base-Disziplin erworben wird, und wie fremde uncommittete Änderungen (**Dirty Tree**) geschützt statt als Nebenwirkung gelöscht werden. Sie ist eine weitere Spezifikations-Ebene der Mutationsphase §5 (nach §5.10, vor §6) und **schließt den §7-Vorbehalt** dieser Koordinations-Dimension (Story 3.5). Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone (D-3). Die Lease lebt in **Git/Datei-Ebene** (Branch-Name, Lockfile) — **nicht** in Concept-Frontmatter (Vertrag §3.1–§3.7 unverändert, keine neue §7-Klasse, **kein neuer Frontmatter-Key für Lease-Metadaten**; ein solcher wäre Ask-First). Leasing ist eine **Querschnitt-Dimension, keine neue Mutations-Form**: die §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` bleibt die **einzige** Form-Wahl; die §5.10-Synthese-Mechanik bleibt unberührt. Eine Lease-geschützte Mutation ist ein Compilation-Vorgang wie die Anlage/das Update: sie unterliegt denselben Phasen (§0: Interpretieren → Reconcile → Synthetisieren → Mutieren → Validieren), derselben Commit-Boundary = Mutations-Boundary (AD-17f, §0/§5.3) und derselben Validator-Erfolgsbedingung (§6).
|
||||
|
||||
1. **Lease-Akquise (AD-17a, A0-12) — deterministisch (AD-17h):** Ein Producer, der einen Bereich (Area oder Root-Scope gemäß Pkt. 2) bearbeiten will, akquiriert die Lease **bevor** er mutiert, gegen einen **eindeutigen Commit-Object-Wert** (Merge-Base-Disziplin):
|
||||
- **Arbeits-Branch-Form:** Der Producer arbeitet auf einem Branch der Form **`lease/<area>/<id>`** (z. B. `lease/alpha/run-a`; für Root-Scope-Bereiche `<area>` = `wiki`, z. B. `lease/wiki/run-a`). `<area>` ist der Bereichs-Pfad (deterministisch nach §5.7), `<id>` ein vom Producer vergebener, kollisionsfreier Run-Identifier. Der Branch wird **von der Merge-Base aus** erstellt (`git checkout -b lease/<area>/<id>`, Abzweigung von der letzten Mutations-Boundary, §5.9 Pkt. 6 R-1); der Producer arbeitet **ausschließlich** auf diesem Branch und committet dort (Commit-Boundary = Mutations-Boundary, Pkt. 5).
|
||||
- **Lockfile:** Eine Lease wird durch ein **Lockfile** realisiert, das **semantisch identisch in jedem Adapter** ist (A0-12 — die Realisierung ist **nicht** pro Adapter frei wählbar). Deterministisches Format (AD-17h): das Lockfile liegt unter `lease/<area>/<id>.lock` (außerhalb `wiki/`/`raw/`, im Workspace), enthält den **Feld-Satz** `area`, `id`, `producer`, `baseline_commit` (voller SHA) und — wenn bereits vergeben — `holder_id`, und ist ein **committeter** git-Tracking- oder ein deterministisch benannter ungetrackter Marker (die Semantik „eine Lease je `lease/<area>/<id>`-Branch" ist maßgeblich; die exakte Datei-Ablage ist in jedem Adapter identisch zu halten). **Lease-Hold:** Existiert das Lockfile schon (die Lease ist vergeben), **überschreibt der Producer sie nicht** — er hält die Lease (Lease-Hold) und es erfolgt **keine Mutation** am betroffenen Pfad durch den zweiten Producer; die Koordination geht in den AD-16-Pfad (Pkt. 4) über.
|
||||
- **Merge-Base-Disziplin (eindeutiger Commit-Object-Wert):** Die Lease wird gegen den **Merge-Base-Commit** akquiriert — den eindeutigen Commit-Object-Wert, von dem beide Producer (der haltende und der neue) ausgehen (deterministisch über `git merge-base` bzw. den notierten `<Baseline-Commit>` aus §5.9 Pkt. 6). Gegen denselben Git-State akquirieren zwei unabhängige Producer **deterministisch dieselbe** Koordinationsentscheidung (gleiche Merge-Base → gleiche Lease-Lage, AD-17h/A0-19). Der akquirierte `<Baseline-Commit>` wird im `log.md`-Eintrag notiert (Pkt. 6, D-2).
|
||||
- **Lease-Freigabe (Release, deterministisch):** Nach erfolgreichem Run, sobald die Mutation als Ganzes committet ist (Pkt. 5, Commit-Boundary), gibt der Producer die Lease **deterministisch** frei: das Lockfile `lease/<area>/<id>.lock` wird entfernt bzw. als freigegeben markiert und der Abschluss als `log.md`-Eintrag dokumentiert (Pkt. 6). Eine Lease, deren Inhaber den Run **ohne** Freigabe beendet (abgebrochen/verwaist), **verbleibt bis zum Staleness-/Recovery-Mechanismus der Story 3.6** — §5.11 prüft stets nur den **committeten** Zustand (AD-17h/A0-19) und wertet bei einer existierenden, nicht freigegebenen Lease als **Lease-Hold** (Pkt. 1). Staleness-TTL, verwaiste Leases und die `raw/`-Recovery sind Story-3.6-Thema.
|
||||
2. **Root-Scope-Lease (AD-17b, A0-13):** Die Lease umfasst **`wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien** — nicht nur den mutierten Concept-Pfad. Ein Producer, der einen Concept-Pfad mutiert, hält damit zugleich die Koordinations-Rechte an der Bundle-Integrität (Bundleroot `index.md`, `log.md`, Root-Concepts), weil jede Mutation die Pfad-Menge von `log.md`/`index.md` (Eintragspflicht bzw. Index-Regel) berühren kann. **Kein Bereich jenseits `wiki/`** ist Lease-Gegenstand (AD-17b-Root-Scope; `raw/` bleibt immutable, AD-3; jenseits-`wiki/`-Lease wäre Ask-First).
|
||||
3. **Dirty-Tree-Schutz (AD-17e/f, A0-16):** **Vor jeder Mutation** prüft der Producer die Working Copy **auf den zu mutierenden Bereich** (Pre-Mutation-Prüfung; deterministisch: `git status --porcelain -- <Mutationsbereich>` gegen HEAD — der mutierte Bereich ist die Pfad-Menge des Runs, Kandidatenliste ∪ Neu-Anlage-Zielpfade ∪ `log.md` ∪ `index.md`, §5.9 Pkt. 5):
|
||||
- **Fremde uncommittete Änderungen werden geschützt, nie gelöscht (AD-17e):** Liegen im mutierten Bereich **fremde uncommittete Änderungen** vor (Dirty Tree — Änderungen, die **nicht** vom laufenden Producer stammen und **nicht** committet sind), **löscht der Producer sie nicht** und übergeht sie nicht still. Er schützt sie in eine **Scratch-Zone/Stash** (deterministisch: `git stash push -- <Pfade>` mit Stash-/Verzeichnis-Konvention oder Kopie in eine benannte Scratch-Zone außerhalb `wiki/`, z. B. `scratch/<run-id>/<pfad>.stash`) und **dokumentiert den Vorgang in `log.md`** (Pkt. 6). **Screen-Artefakte beim Schutz** (Stash-/Kopier-Artefakte, die im Bundle-Baum auftauchen würden) werden **textuell benannt** (NFR-4) und liegen außerhalb der erlaubten Diff-Menge (§5.9 Pkt. 5) — sie werden nie als Concept verwechselt. Nach erfolgreichem Run sind die geschützten Änderungen für den ursprünglichen Producer zurückzuspielen (Restore; der Weg ist im `log.md`-Eintrag dokumentiert).
|
||||
- **UNCOMMITTED_INPUT-Abbruch (AD-17a, I/O-Matrix-Szenario `UNCOMMITTED_INPUT` der Story-3.5-Matrix):** Weicht die **Working-Copy von `raw/` oder `wiki/`** vom committeten HEAD in einem Umfang ab, der als Input des Runs gelten würde (uncommittete `raw/`-Zuwächse oder uncommittete `wiki/`-Abweichungen außerhalb der eigenen, bereits geleasten Mutation), bricht der Run mit dem textuell benannten Abbruch „**published/committed Input erforderlich**" ab — **vor** Interpretation und vor jeder Mutation (keine Mutation gegen Zwischenstände). Dies ist **dieselbe Pre-Run-Prüfung** wie das §5.9-P2-Element (1) (`INPUT_UNCOMMITTED`, Story-3.1-Matrix): dieselbe Bedingung, derselbe Abbruch-Wortlaut — **kein zweiter, separater Abbruch-Pfad**. Keine Mutation, Bundle konsistent.
|
||||
- **Mutationen operieren nur auf Directory-/Commit-Ebene — Commit-Boundary = Mutations-Boundary (Pkt. 5).**
|
||||
4. **Kein textueller Auto-Merge — compiler-vermittelter Merge (AD-17c, A0-14):** Zwei Branches mit **ungleichem Inhalt am selben Concept-Pfad** werden **nie textuell automatisch gemerged** (kein stiller textueller Auto-Merge, AD-17c — verboten, Never). Der Merge ist **compiler-vermittelt** und durchläuft die **AD-16-Klassifikation** mit explizitem `log.md`-Eintrag (Pkt. 6; Interface zu Epic 4): die beiden Inhaltsvarianten desselben Pfads werden als **Konflikt** behandelt — der haltende Producer klassifiziert sie gemäß AD-16 (Default: **Erhaltung** — beide Behauptungen bleiben, Disagreement-Eintrag in `log.md`; keine stille Konsolidierung, keine stille Löschung). **Lease-Konflikt (I/O-Matrix `LEASE_KONFLIKT`):** Ein zweiter Producer mit Lockfile-Konflikt am selben Pfad erzeugt **keinen stillen textuellen Auto-Merge**; die Auflösung geht in den compiler-vermittelten AD-16-Pfad (Pkt. 4) mit `log.md`-Eintrag. **Unentscheidbar → menschliche Eskalation (AD-17g):** Ist die AD-16-Klassifikation unentscheidbar, eskaliert der Producer menschlich (textuell benannt, NFR-4) — er nimmt **keine** Auto-Entscheidung vor.
|
||||
5. **Commit-Boundary = Mutations-Boundary (§0/§5.3-Verweis unverändert):** Auch im Leasing-Pfad gilt: **Zwischenstände werden nie als fertige Mutation veröffentlicht** (AD-17f). Der Producer committet die Mutationen als Ganzes auf dem `lease/<area>/<id>`-Branch, und zwar erst, nachdem der Diff-Selbsttest (§5.9 Pkt. 5) ohne Ghost-Diff abgeschlossen ist. Ein Ghost-Diff (auch ein durch den Dirty-Tree-Schutz erzeugter Screen-Artefakt im Bundle-Baum) ist ein textuell benannter Instruktions-Verstoß (NFR-4) und wird **vor** der Run-Gültigkeit zurückgerollt. Die Pkt.-5-Erhaltungs-Invariante gilt für Leasing-fähige Runs **unverändert** (Pfad-Menge ⊆ Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ Index; AD-5/FT-6).
|
||||
6. **`log.md`-Eintragspflicht (Vertrag §5):** Jede Lease-geschützte Koordinationsentscheidung wird als datumsgruppierter Eintrag in `wiki/log.md` dokumentiert (Header = ISO-Datum `YYYY-MM-DD`, **neueste zuerst** — Vertrag-§5-Datumsgruppe, wie §5.9 Pkt. 4): **(a) Lease-Akquise** (Branch `lease/<area>/<id>`, `<Baseline-Commit>`, Lockfile-Referenz — deterministisch auflösbar, D-2), **(b) Dirty-Tree-Schutz** (geschützte fremde uncommittete Änderung, Scratching-Ziel, Restore-Weg — „nie gelöscht", AD-17e), **(c) Merge-Klassifikation** (AD-16-Klassifikation bei ungleichem Pfad-Inhalt, Disagreement-/Konflikt-Vermerk) und **(d) Eskalation** (menschliche Eskalation bei Unentscheidbarkeit, AD-17g).
|
||||
7. **Determinismus-Vertrag (AD-17h/A0-19):** Lease-Akquise, Lockfile-Inhalte und Merge-Klassifikation folgen **deterministisch aus dem committeten Git-State**: gleicher Git-State + gleiche Eingabemenge → **identische Koordinationsentscheidung** (gleiche Merge-Base → gleiche Lockfile-Lage; gleiche Pfad-Inhalte → gleiche AD-16-Klassifikation). Der `generated.at`-Wanduhr-Gap (gleiches Eingabeset, unabhängige Runs, verschiedene `at`) bleibt offene A0-20-Konvention mit Home **Story 3.8** (§5.9 Pkt. 2, `generated.at`-Konvention) — unverändert bindend. **Lease-Staleness/Recovery** (TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery; AD-17d, A0-15) bleibt **Story 3.6** vorbehalten: diese Sektion verweist darauf und mutiert deren Mechanik nicht. (Testbares Seam-Kriterium der Koordinations-Dimension, das 3.5 von 3.6 trennt: **3.5** ist eine auf den **committeten Git-State** bezogene Determinismus-Frage — allein aus Merge-Base + Lockfile + Pfad-Inhalten entscheidbar (AD-17h/A0-19); **3.6** ist eine **Zeit- bzw. Umgebungs-Zustands-Frage**, die den committeten Zustand verlässt — TTL-Ablauf, verwaiste/hängende Leases, Registrierung, `raw/`-Recovery.)
|
||||
|
||||
## 5.12 Lease-Staleness & Recovery-Basis (Story 3.6)
|
||||
|
||||
Der §5.11-Lease-Hold (`lease/<area>/<id>`-Branches, Lockfile, Merge-Base-Disziplin) wertet stets nur den **committeten Git-State** (AD-17h/A0-19). Diese Sektion ist der **einzige Instruktions-Ort der Staleness-/Recovery-Dimension** (D-3, Story 3.6; AD-17d, A0-15; §5.11-Seam-Kriterium S-1 und Pkt.-7-Text bleiben unverändert): sie definiert, wie eine nach einem **abgebrochenen Run ohne Freigabe** hinterlassene Lease **nicht dauerhaft blockiert** (TTL plus Lease-Registrierung im Clone-Root-State), wie sie **übernommen oder als stale markiert** wird, und dass `raw/` die **Zugriffs-/Consistency-Basis** bleibt (AD-3). Sie ist die Ausformulierung der **Zeit-/Umgebungs-Zustands-Frage** des §5.11-Pkt.-7-Seam-Kriteriums („3.5 = committeter-Git-State-Determinismus; 3.6 = Zeit-/Umgebungs-Zustands-Frage — TTL-Ablauf, verwaiste/hängende Leases, Registrierung, `raw/`-Recovery") und greift §5.11 Pkt. 1 wortgleich auf („verbleibt bis zum Staleness-/Recovery-Mechanismus der Story 3.6"). Der §7-Vorbehalt der koordinations-Dimension ist damit **in dieser Sektion verankert (Story 3.6)**. Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone (D-3). Verwaiste/hängende Leases werden **nie still gelöscht** (AD-17e) und `raw/` bleibt bei jedem Vorgang unverändert (AD-3).
|
||||
|
||||
1. **Lease-Registrierung & generationenbasiertes TTL (AD-17d, A0-15) — deterministisch (AD-17h):** Die Registrierung der Leases erfolgt dauerhaft im **Clone-Root-State** — nicht in `wiki/`/`raw/`/Concept-Frontmatter (Vertrag §3.1–§3.7 unverändert) und **ohne Wanduhr-Timestamp im Lockfile- oder Registrierungs-Format** (A0-20-Konvention, §5.11 D1). Ein Run, der eine Lease akquiriert (Pkt. 1), **registriert** sie zur Erzeugungs-Generation `gen` — einem **monotonen Run-Generator (Highlight-`generation`-Zähler je Clone-Root)**, der **deterministisch aus dem committeten Git-State ableitbar** ist: entweder als **Baumableitung aus dem `lease/`-Baum** (höchster, committet sichtbarer Stände-Generator der akquirierten/release-freien Leases) oder als gepinnter **Mono-Commit-`lease-granite-root`-Marker** (ein einziger committeter Markerwert im Clone-Root, der den aktuellen Generationsstand trägt). Die **Quellen-Präzedenz ist deterministisch gepinnt**: liegt **beides** vor (Baumableitung und Marker), **gewinnt der committete `lease-granite-root`-Marker** (expliziter Pinn garantiert Eindeutigkeit); liegt **nur die Baumableitung** vor, dient sie als Generator; liegt **keins** vor (keine committete Lease im Clone-Root-Baum), gilt der **definierte Startwert `gen = 0`** (zählt keine Lease als älter — die erste Akquise startet als Erzeugungs-Generation 1, Pkt. 1). Das **TTL-Ablauf-Kriterium ist generationen-basiert**: eine Lease, deren Erzeugungs-Generation **älter** ist (niedrigere Gen-Zahl als der aktuell höchste sichtbare Reg-Generator des ablaufenden Runs — Gen kleiner = älter), gilt als **stale** (TTL-Ablauf-Äquivalent) — sie **blockiert keinen nachfolgenden Run** (I/O-Matrix `STALE_ABLAUF`). Die A0-20-zugelassene `at`-Notation (dokumentierender Zeitstempel, §5.9 Pkt. 2) bleibt **dokumentierend** — sie ist **nie** eine streng durchsetzende TTL-Steuergröße (ein wanduhr-durchsetzendes Zeit-TTL wäre Ask-First).
|
||||
|
||||
2. **holder_id-Ableitung (deterministischer Default; Defer „holder_id-Quelle" Story 3.5 aufgegriffen):** Das Lockfile-Feld `holder_id` (§5.11 Pkt. 1, „eindeutige Producer-/Run-Kennung") wird **deterministisch** als `holder_id := <producer>-<id>` abgeleitet — `<producer>` aus dem Lockfile-Feld `producer`, `<id>` der Run-Identifier aus der Branch-Form `lease/<area>/<id>` (§5.11 Pkt. 1). Die Ableitung ist **reproduzierbar aus dem committeten Git-State** (AD-17h/A0-19): gleicher Producer + gleicher Branch-Suffix → identische `holder_id`, ohne Wanduhr. Eindeutigkeit gilt je **aktiver** Lease (ein vorhandenes Lockfile blockiert ohnehin jede zweite Akquise, §5.11 Pkt. 1 Lease-Hold); ein Producer, der eine längere, kollisionsfreie Kennung wünscht, wählt `<id>` entsprechend — die Ableitbarkeits-/Lesbarkeits-Regel bleibt unverändert.
|
||||
|
||||
3. **Verwaist-Klassifikation, Übernahme & Stale-Markierung (AD-17e, AD-17g):** Findet ein neuer Run eine **verwaiste/hängende Lease** (Registrierung ohne Freigabe/Release oder mit älterer Erzeugungs-Generation, Pkt. 1), wird sie **klassifiziert** statt blockiert:
|
||||
- **VERWAIST_UEBERNEHMEN:** Der neue Run kann die Lease **übernehmen** — gegen die **erneute Merge-Base-Prüfung** (§5.11 Pkt. 1 + die Pkt.-4-Diskrepanz-Regel dieser Sektion, I/O-Matrix `VERWAIST_UEBERNEHMEN`). Die Übernahme wird als `log.md`-Eintrag protokolliert (Pkt. 6). Besteht ein **Konflikt** (die verwaiste Lease hält einen Bereich/`log.md`-Eintrag, der vom Übernehmer anderweitig mutiert würde), geht die Koordination in den **AD-16-Pfad** (Erhaltung; §5.11 Pkt. 4) bzw. bei Unentscheidbarkeit in die **menschliche Eskalation** (AD-17g).
|
||||
- **VERWAIST_STALE_MARKIEREN:** Ist die Übernahme nicht sinnvoll, markiert der Run die Lease als **stale** — über einen **Registry-Marker** (Registrierungs-Eintrag der aktuellen Generation, Pkt. 1/5) — und hebt damit die Blockade auf; der Vorgang wird als `log.md`-Eintrag protokolliert (Pkt. 6, I/O-Matrix `VERWAIST_STALE_MARKIEREN`).
|
||||
- **Nie gelöscht (AD-17e):** Die verwaiste Lease selbst (Lockfile, Registrierung, Marker) wird **nie still gelöscht** — sie wird übernommen oder als stale markiert; fremde uncommittete Änderungen werden **nie** als Seitenwirkung entfernt (§5.11 Pkt. 3).
|
||||
|
||||
4. **baseline_commit-Merge-Base-Diskrepanz-Regel (Vereinheitlichung; Defer „baseline_commit-Diskrepanz" Story 3.5 aufgegriffen):** §5.11 Pkt. 1 nennt zwei Quellen für den akquirierten Baseline-Commit („deterministisch über `git merge-base` bzw. den notierten `<Baseline-Commit>` aus §5.9 Pkt. 6"). Die **Diskrepanz-Regel** bündelt sie deterministisch (AD-17h): Widersprechen sich der `git merge-base`-Laufzeitbefund und der **notierte** `<Baseline-Commit>` (letzte notierte Mutations-Boundary, §5.9 Pkt. 6 D-2) derselben Lease, **gewinnt der git-abgeleitete Merge-Base-Wert** (Commit-Boundary-Prinzip); der notierte SHA bleibt **Sekundär-Fingerprint**. **Fallback:** Ohne vorherige Mutations-Boundary gelten alle `raw/`-Dateien als Zuwachs (§5.9 Pkt. 6 R-1) und die Merge-Base ist der älteste committete Fundamentpunkt (deterministisch auflösbar, AD-14). Dieselbe Regel gilt bei einer **Lease-Übernahme** (Pkt. 3): die Übernahme validiert die angestrebte neue Merge-Base gegen diese Auflösung, bevor Mutationsrechte übergehen.
|
||||
|
||||
5. **`raw/`-Recovery-Basis & native `git stash`-Variante (AD-3, AD-17d/A0-15):** `raw/` ist die **Zugriffs- und Consistency-Recovery-Basis** — die unveränderte, committete Evidenzbasis, auf die ein Run nach einem abgebrochenen Lauf seine Zugriffs-/Konsistenzannahmen bezieht (I/O-Matrix `RECOVERY_RAW_BASIS`). `raw/`-Inhalte werden **bei keinem Vorgang verändert** (AD-3) — auch nicht bei Staleness-/Übernahme-/Stale-Markierungs-Schritten; ein möglicher Real-Baum-Nachweis gegen die `raw/`-Inhaltsebene ist dabei auf **Sandbox-Evidenzwege beschränkt** (kein `git diff`-/SHA-256-Beweis gegen `raw/`-Inhaltsebene auf dem realen Ist-Baum, EC-1-Grenze). Für den **Dirty-Tree-Schutz** (§5.11 Pkt. 3) sind beide textuell zulässigen Sicherungswege nutzbar: die **Kopie in eine benannte Scratch-Zone** (bestehender Pfad) und die **native `git stash`-Variante** (`git stash push -- <Pfade>`, Stash-/Verzeichnis-Konvention) — beide deterministisch im Ergebnis (fremde uncommittete Änderung bleibt byte-identisch geschützt, nie gelöscht, Restore dokumentiert; Defer „native `git stash`-Variante" Story 3.5 aufgegriffen, als Sandbox-Doppel abgebildet).
|
||||
|
||||
6. **Registrierungs-Invariante & kumulativer Registry-Aufbau über Runs (Vertrag §5):** Die Registrierung hält je Clone-Root die **Invariante: Gen > erzeugend oder gleiche Gen** — der Registry-Stand regrediert nie auf eine **ältere** Generation als die erzeugende, und er hält stets den **sichtbar höchsten Reg-Generator** (I/O-Matrix `VERWAIST_STALE_MARKIEREN`-Error-Handling, Sandbox STALE-5). Der Registry-Aufbau ist **kumulativ über Runs** (mehrere aufeinanderfolgende Producer schreiben dieselbe Registrierung fort, analog zum `log.md`-Akkumulator; Defer „Sandbox-log-Akkumulator" Story 3.5 aufgegriffen): es existiert **kein eigener `# Log`-Stand** der Registry — `wiki/log.md` ist der **alleinige Aufzeichnungs-Ort** (Vertrag §5), die Registry-Marker selbst sind Git-/Datei-Ebene ohne Eintrags-Body.
|
||||
|
||||
7. **`log.md`-Eintragspflicht & Determinismus-Vertrag (Vertrag §5; AD-17h/A0-19):** Jede Staleness-/Recovery-Koordinationsentscheidung wird als datumsgruppierter `wiki/log.md`-Eintrag dokumentiert (Header = ISO-Datum `YYYY-MM-DD`, **neueste zuerst** — Vertrag-§5-Datumsgruppe, wie §5.9 Pkt. 4/§5.11 Pkt. 6): **(a) Registrierung/TTL** (Erzeugungs-Generation, Registry-Marker-Referenz, `<Baseline-Commit>` — deterministisch auflösbar, D-2), **(b) Lease-Übernahme** (verwaiste Lease, angestrebte Merge-Base, neuer holder_id), **(c) Stale-Markierung** (verwaiste Lease → Marker der aktuellen Generation, Blockade aufgehoben) und **(d) Recovery-Basis-Nutzung** (bei Rückgriff auf `raw/` bzw. Stash-Restore, Pfad-Referenz). **Determinismus-Vertrag (AD-17h/A0-19):** Registrierungs-Aufbau, TTL-Ablauf-Kriterium (Generationen), Verwaist-Klassifikation und `log.md`-Texte folgen **deterministisch aus dem committeten Git-State** (gleicher Git-State + gleiche Eingabemenge → identische Staleness-/Recovery-Entscheidung); **kein Wanduhr-Timestamp steuert** einen dieser Vorgänge (A0-20-Konvention unverändert).
|
||||
|
||||
## 5.13 Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)
|
||||
|
||||
Diese Sektion ist der **einzige Instruktions-Ort der Reason/Mutate-Phasen-Disziplin** (D-3, Story 3.7; AD-6, A0-7): sie bindet die **logische Phasen-Trennung** von AD-6 (Analyse → Änderungsplanung → Mutation → Validierung) als **durchsetzbare Ausführungs-Disziplin** an die bestehende, **unverändert** verbindliche §0-Ablaufstruktur. Der §7-Vorbehalt dieser Trennung ist damit **in dieser Sektion verankert (Story 3.7)**. Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone (D-3) — und sie baut **keine eigene Workflow-Engine** (AD-6, A0-7-AC-4): die Trennung ist eine **textuelle Ausführungs-Disziplin in einer Session** (Analyse → Änderungsplanung → Mutation → Validierung laufen nacheinander in dem einen vorhandenen agentischen Run; kein neuer Prozess/Server/MCP). Die Endzustands-Konsistenz wird als **beobachtbare, über Git prüfbare Eigenschaft** fixiert (A0-7-AC-2): nach einem abgebrochenen/fehlgeschlagenen Run ist der beobachtbare Bundle-Endzustand der Baseline **oder** der einer abgeschlossenen, valide committeten Mutation — **deterministisch und ohne Wanduhr** (AD-17h/A0-19, A0-20: weder Planung noch Rollback hängen von Kalenderzeit ab). Der §5.12-Seam-Satz bleibt unverändert (Lease-Staleness/Recovery: Story 3.6); diese Sektion mutiert deren Mechanik nicht.
|
||||
|
||||
| AD-6-Phase (logisch) | §0-Ablaufstruktur (deterministische Takt-Folge) |
|
||||
|---|---|
|
||||
| **Analyse** | (0) Input prüfen, (1) Interpretieren, (2) Reconcile (inkl. §3.2-Relevanzbestimmung) |
|
||||
| **Änderungsplanung** | §5.9 Pkt. 6-P2-Block (Pre-Run-Reconcile-Check-Block, nach Reconcile/vor Mutieren) |
|
||||
| **Mutation** | (3) Synthetisieren, (4) Mutieren (§§4–5, §5.9/§5.10/§5.11/§5.12-Mechanik) |
|
||||
| **Validierung** | (5) Validieren (§6; Rollback §5.3 Pkt. 3 / §6 Pkt. 3, Ghost-Diff-Rollback §5.9 Pkt. 5) |
|
||||
|
||||
Die Tabelle ist eine **Zuordnungsklassifikation ohne neue Norm**: §0 bleibt die **deterministische Ausführungs-Folge** (unverändert, keine Um-Nummerierung, keine neue Phase); die **vier** AD-6-Phasen decken sich **logisch** mit ihr (Reconcile ist in die Analyse-Zelle der Zuordnungstabelle gefasst) — ein Umbau des §0-Listentextes oder eine zusätzliche Phase wäre Ask-First.
|
||||
|
||||
1. **Änderungsplanung (AD-6, A0-7; Analyse-Ergebnis → konsistenter Plan):** Der **P2-Block (§5.9 Pkt. 6)** ist die **Änderungsplanung der AD-6-Kette** — er wird **Wortlaut-unverändert** referenziert (Rückverweis, keine Doppel-Instruktion): Aus der Analyse (Input prüfen/Interpretieren/Reconcile, §0 (0)–(2)) konsolidiert der Producer **vor der Mutationsphase** einen konsistenten Plan — (1) Input-Zustand (AD-17a, committet), (2) Ziel-Pfade, (3) Quellen-Existenz (EC-1), (4) Betroffenheits-Liste (§3 Pkt. 2, textuell-deterministisch nach §3.2), (5) `wiki/index.md`-V-1, (6) Struktur-Erhaltungs-Check (Vertrag §3.3/§3.4-Subset, §5.9 Pkt. 6). Ein **Plan-Defizit** (z. B. fehlgeschlagene Vorprüfung, uncommitteter Input `INPUT_UNCOMMITTED`) wird **textuell benannt** (NFR-4) und **verhindert die Mutation** — die Kette bricht **vor** der Mutationsphase ab (I/O-Matrix `PLAN_BEABSICHTIGT`: „fehlgeschlagene Vorprüfung ⇒ keine Mutation"). Der konsistente Plan wird **textuell festgehalten** (Teil der Ausführungsdisziplin — der P2-Block-Befund; **kein** Artefakt-File, keine Erhaltungs-Invarianten-Ausweitung) und ist **deterministisch aus dem committeten Git-State** ableitbar (AD-17h/A0-19): gleicher Git-State + gleiche Eingabemenge → identische Planung.
|
||||
2. **Plan-Freeze (Veränderungs-Sperre nach Phasenabschluss; AD-6-Kopplung §5.9 Pkt. 5):** Der Abschluss der Änderungsplanung friert den Plan ein: In der Mutationsphase **wird kein Plan-Gegenstand außerhalb der erlaubten Pfad-Menge** — **Kandidatenliste ∪ Neu-Anlage-Zielpfade ∪ `log.md` ∪ nachgeführte `index.md`** (§5.9 Pkt. 5) — mutiert (I/O-Matrix `PLAN_FREEZE`). Eine Verletzung der Sperre ist ein **Ghost-Diff (§5.9 Pkt. 5)**: Der Producer rollt den betreffenden Pfad aus dem Baseline-Zustand zurück und benennt die Verletzung **textuell** (NFR-4), bevor der Run als gültig vermerkt wird. Plan-Freeze und Diff-Probe sind dieselbe §5.9-Pkt.-5-Mechanik — diese Sektion institutionalisiert sie als Phasen-Disziplin, ohne deren Mechanik zu ändern.
|
||||
3. **Mutationsphase — Abbruch mit Zustands-Restaurations-Invariante (AD-6, A0-7-AC-2):** Bricht der Run während der Mutationsphase ab (Fehler, I/O-Matrix `MUTATION_ABBRUCH`), greift die bestehende Rollback-Maschinerie **Wortlaut-unverändert**: **§5.3 Pkt. 3 / §6 Pkt. 3** (Teilzustand des Bundles zurückrollen) und der **Ghost-Diff-Rollback (§5.9 Pkt. 5)**. Die **Zustands-Restaurations-Invariante** macht die erzählte Konsistenz-Eigenschaft zu einer **beobachtbaren, über Git prüfbaren**: nach Abbruch/FAIL ist der **Post-Rollback-Diff gegen die Baseline leer** — `git diff <Baseline-Commit> -- wiki/` ist leer und der Bundle-Zustand **== Baseline** (Restaurations-Fall); Erfolgsfall: das Bundle ist der **valide committete Zustand des Runs** (Commit-Boundary = Mutations-Boundary, AD-17f, §0/§5.3). Beide Pfade sind deterministisch und **ohne Wanduhr** (A0-20). `raw/` bleibt bei jedem Vorgang unverändert (AD-3).
|
||||
4. **Kein Teilerfolg wird als fertige Mutation veröffentlicht (AD-17f; I/O-Matrix `MUTATION_TEILFOLGE`):** Sind mehrere Mutationen geplant und wird nur ein Teil ausgeführt, stellt der Rollback nach Pkt. 3 die Baseline wieder her — **Zwischenstände werden nie veröffentlicht** (Commit-Boundary = Mutations-Boundary, unverändert). Die **textuelle Failure-Benennung** (NFR-4) und die Phasen-Disziplin selbst bleiben unverändert.
|
||||
5. **Validierungsphase — FAIL (AD-6; §6 Pkt. 3):** Melden die §6-Validierungen einen **VALIDATION_FAIL**, gilt der Run als gescheitert; es werden **keine weiteren Mutationen** durchgeführt, `raw/` bleibt unangetastet (AD-3), die Fehlerursache wird textuell benannt (NFR-4) und der Teilzustand gemäß **§5.3 Pkt. 3 / §6 Pkt. 3** zurückgerollt — Endzustand konsistent (Zustands-Restaurations-Invariante, Pkt. 3).
|
||||
6. **Validierungsphase — SUCCESS mit Konsistenz-Commit (AD-6, A0-7-AC-3):** Sind **alle** geplanten Mutations-Validierungen erfolgreich durchlaufen (**VALIDATION_SUCCESS**, alle `wiki/`-Dateien SUCCESS nach §6), werden die Mutationen **als Ganzes committet** — erst nachdem der Diff-Selbsttest (§5.9 Pkt. 5) ohne Ghost-Diff abgeschlossen ist (AD-17f); die Konsistenz des Endzustands ist damit der **valide committete Zustand des Runs**. Der **§6-Pkt.-4-Ausführungs-Nachweis** wird geführt; **kein Wanduhr-Zeitstempel** steuert den Commit (A0-20).
|
||||
7. **Keine eigene Workflow-Engine (D-3, AD-11, AD-6; I/O-Matrix `KEINE_EIGENE_ENGINE`):** Die Phasen-Trennung ist **logisch in einer Session** — Analyse, Änderungsplanung, Mutation und Validierung laufen in dem einen vorhandenen agentischen Run nacheinander ab; es wird **nichts Steuerndes gebaut** (kein neuer Prozess, kein Server, kein MCP, kein Standalone-Compiler/eigene LLM-Runtime, D-3/AD-11). Die Erhaltungs-Invariante (§5.9 Pkt. 5) und der Diff-Selbsttest gelten **unverändert**; der Determinismus-Vertrag (AD-17h/A0-19) gilt für Plan-Inhalt, Phasen-Reihenfolge, Rollback-Trigger und Post-Zustand — **mit dem dokumentierten `generated.at`-Wanduhr-Gap** (§5.10 Pkt. 8): zwei unabhängige Runs desselben Git-States können im Post-Zustand in `generated.at` abweichen (A0-20-Konvention); die übrigen Post-Zustands-Teile sind byte-identisch. Die **Behandlung des Gaps im Bundle-State-Vergleich** (Ausnahme-Menge, Abweichungs-Klassifikation) definiert **§5.14** (Determinismus-Vertrag & Agent-Instruktions-Validator, Story 3.8) — der Home-Verweis der A0-20-Konvention ist damit auf die §5.14-Definition verlagert.
|
||||
|
||||
## 5.14 Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)
|
||||
|
||||
Diese Sektion ist der **einzige Instruktions-Ort der geschlossenen Determinismus-Bestätigungs-Mechanik** (D-3, Story 3.8; AD-17h/FT-10/A0-19; §3.2/§5.9/§5.10/§5.11/§5.12/§5.13-Rückverweise verankern den Vertrag an vielen Stellen — hier wird seine **Bestätigung** geschlossen instruiert): Über denselben committeten Git-State + dasselbe Eingabeset erzeugen **zwei unabhängige Runs denselben Bundle-State**; eine Abweichung ist ein **Fehler der AD-16-Klassifikation**, kein Rauschen (textuell benannt, NFR-4). Das Enforcement lebt im MVP als **Agent-Instruktions-Validator (D-3, Q-6)** — ein Producer führt die Instruktion **zweimal über demselben committeten Git-State in einer Session** aus und vergleicht die Bundle-States; **kein** dedizierter Validator-Prozess, **keine** extra Runtime, **keine** eigene Workflow-Engine (AD-6, AD-11; D-3 — die Phasen-Trennung des §5.13 bleibt logisch in einer Session). Sie fügt **kein** Prädikat, **keine** neue §7-Invaliditätsklasse, **keinen** `schema/wiki-compiler.md`-/`schema/validator.md`-/`adapters/`-/`raw/`-Change hinzu (AD-3) und **keinen** Standalone/Schema-/Format-/Frontmatter-Key (D-3). Die **§0-Phasen-Ablaufstruktur (0)–(5)**, die **§5.13-Phasen-Disziplin** und **§6/§6.5** bleiben **textuell unverändert** (Fugen-Identität; diese Sektion referenziert sie Wortlaut-unverändert). Der `generated.at`-**Wanduhr-Gap selbst bleibt verhaltens-seitig unverändert** (A0-20-Konvention, §5.9 Pkt. 2, §5.10 Pkt. 8) — §5.14 definiert **nur seine Behandlung im Bundle-State-Vergleich** (Ausnahme-Menge); ein Wechsel des `generated.at`-Verhaltens (z. B. Ableitung aus dem Git-State statt Wanduhr) wäre **Ask-First**.
|
||||
|
||||
1. **Bundle-State-Definition (deterministische Projektion des committeten Git-States):** Der **Bundle-State** eines Runs ist die vollständige, **deterministisch aus dem committeten Git-State ableitbare** Zustandsmenge des Runs — (a) der **committete Baum** (Datei-Gesamtheit unter `wiki/`/`raw/` gg. die Baseline, §5.9 Pkt. 6 R-1: mutierte/neu angelegte `wiki/`-Pfade, `log.md`-Einträge, nachgeführte `index.md`-Links, `raw/`-Zuwächse als Input), (b) die **Plan-/Kandidaten-/Reihenfolge-Outputs** (§3.2-Candidate-Liste in Zuwachs-Sicht-Ordnung, §5.13-P2-Block-Befund, §5.9-Vorprüfungen) und (c) die **Ausführungs-Entscheidungen** (§5.9-Form-Wahl, §5.10-Konsolidierung/Form-Zuordnung, §5.10-Pkt.-8-Reconcile-Orphan-Befund). **`generated.at` (und ggf. `verified[].at`) ist eine benannte Ausnahme** der Bundle-State-Projektion: es ist der **einzige** Projektions-Bestandteil, der zwischen zwei unabhängigen Runs desselben Git-States abweichen darf (A0-20-Konvention; §5.14-Pkt.-3-Ausnahme-Menge). Gleicher Git-State + gleiche Eingabemenge ⇒ **identischer Bundle-State bis auf die benannte `at`-Ausnahme** (FT-10, AD-17h, AC-1).
|
||||
2. **Zwei-Run-Bestätigungs-Mechanik (agent-Instruktions-basiert, D-3/Q-6; re-executierbar):** Der Producer **führt die Instruktion zweimal über demselben committeten Git-State aus** (in einer Session; die zweite Ausführung startet aus dem **unveränderten Baseline-Commit-Baum** — Isolation pro Lauf, §5.13-Pkt.-3-Zustands-Restaurations-Invariante) und **vergleicht die Bundle-States**:
|
||||
- **Vergleichs-Operandum = committeter Baum:** Vergleichbar ist der aus dem committeten Git-State ableitbare Bundle-State (Pkt. 1); der Vergleich nutzt re-executierbare, deterministische Formeln über denselben Baum — diff- und hashbasiert (z. B. `git diff --name-only <Baseline-Commit> -- wiki/`, pro-Pfad-SHA-256 der mutierten `wiki/`-Inhalte, Plan-/Kandidatenlisten-Textvergleich). Den **Zwei-Run-Abgleich** stellen die Formeln über den **Vergleich der beiden Run-Ausgänge gegeneinander** sicher — nicht nur je Lauf gegen die Baseline (z. B. `git diff <Run-A-Commit> <Run-B-Commit> -- wiki/` ohne die maskierte `at`-Zeile bzw. direkter Bundle-State-Hash-Textvergleich der zwei Läufe, Pkt. 1); ein Quervergleich fehlt, wenn jeder Lauf nur seinem eigenen Baseline-Diff gegenüber geprüft wird und keine eigene Abweichung zwischen den beiden Runs erkannt wird. **Keine Wanduhr/`now`-Zeit** steuert den Vergleich (A0-20; die Ausnahme-Menge definiert die einzig zulässige Differenz, Pkt. 3).
|
||||
- **Identische Outputs:** zwei unabhängige Runs desselben committeten Git-States + desselben Eingabesets liefern **identische Plan-/Kandidaten-/Reihenfolge-Outputs** (§3.2-Candidate-Liste, §5.13-P2-Block, `sources`-Lexikografie, Konsolidierung/Form-Zuordnung) und **byte-identische mutierte Bundle-Bestandteile** — bis auf die `at`-Ausnahme. Die **Sandbox-Demonstration** (Szenario DET-2, `_bmad-output/implementation-artifacts/sandbox-3-8/run-sandbox.sh`) belegt die Identität **nicht-vakuum** (echte Content-Hashes, nicht nur Vorhandensein; AD-17h/A0-19, AC-3).
|
||||
- **Mechanische Bestätigung (Agent-Instruktions-Validator):** Die Bestätigung ist der **textuell festgehaltene, re-executierbare Vergleich der zwei Ausführungen** (log.md-Nachweis je Run mit `<Baseline-Commit>`, D-2; der Befund „identisch bis auf `at`" wird textuell benannt). **Kein neuer Prozess/Server/MCP, kein Standalone** (D-3/AD-11): der Validator ist die **Instruktion selbst**, von einem Agenten einmal ausgeführt und durch eine zweite Ausführung bestätigt.
|
||||
3. **Ausnahme-Menge des Bundle-State-Vergleichs (vollständig, deterministisch):** Der Bundle-State-Vergleich zweier unabhängiger Runs lässt **genau eine** benannte Differenz zu: den **`generated.at`-Wanduhr-Gap** (A0-20-Konvention, §5.9 Pkt. 2, §5.10 Pkt. 8 — ein Wanduhr-Zeitstempel im `generated.at`-Feld bzw. `verified[].at`, der zwischen zwei Runs desselben Git-States abweichen darf). Der Gap ist **dokumentiert, kein stiller Ausschluss**: die Ausnahme bezieht sich **ausschließlich** auf den `at`-Feld-Wert; **alle übrigen Bundle-State-Bestandteile** (Pkt. 1 (a)/(b)/(c)) sind zwischen zwei Runs desselben Git-States **byte-identisch**. Jede **andere** Differenz liegt **außerhalb** der Ausnahme-Menge.
|
||||
4. **Abweichungs-Klassifikation (AD-16-Klassifikationsdefekt, kein Rauschen):** Eine bei der Zwei-Run-Bestätigung festgestellte Differenz außerhalb der benannten Ausnahme-Menge (Pkt. 3) **ist ein Fehler im AD-16-Klassifikations-Mechanismus** (FT-10/AD-17h-AC): der Run wird **textuell benannt** (NFR-4) und **korrigiert bzw. rollt zurück** (Zustands-Restaurations-Invariante, §5.13 Pkt. 3) — die Abweichung wird **nicht** als akzeptables Rauschen oder Umgebungs-Streuung toleriert. Erzeugt ein deterministischer Bestandteil (Pkt. 1) in zwei Runs unterschiedliche Werte, ist die Ursache in der Ausführungs-Instruktion zu suchen und dort zu beheben, **bevor** der Vertrag als bestätigt gilt. Die textuelle Benennung (NFR-4) ist verpflichtend; der Befund wird (ggf. als korrigierter zweiter Lauf) erneut bestätigt.
|
||||
5. **Normalisierungs-/Match-/Orphan-Schließung (§3.2/§5.10-Verankerung):** Die bekannten deterministischen Lücken der Relevanz-/Synthese-Erhebung sind mit dieser Sektion **geschlossen** (als deterministische Regel-Ergänzungen, ohne Umbruch des bestehenden Wortlauts): die **Em-Dash-Kollaps-Klasse**, die **Kollaps-Reichweite** und der **Match-Scope der Stufe a** gelten wie in **§3.2-Pkt.-1b** (geschlossene Determinismus-Lücken) definiert; die **Orphan-Politik** (§5.10 Pkt. 8) gilt als deterministische **Reconcile-Orphan-Regel**. Damit sind die §3.2-/§5.10-Erhebungen über den committeten Zustand **vollständig pinbar** — ein Bestandteil, der zwischen zwei Runs differiert (Ziel-Pfad via §3.2/§5.7, Candidate-Liste, Form-Zuordnung, Verwaist-Befund), ist ein AD-16-Klassifikationsdefekt (Pkt. 4), keine offene Frage.
|
||||
|
||||
## 6. Validieren (mechanische Bestätigung)
|
||||
|
||||
@@ -347,15 +422,16 @@ Interpretations-Hinweis: Die „✗"-Zeilen zeigen die deterministische Fehlerur
|
||||
|
||||
## 7. Selbstbegrenzung (Scope der Instruktion)
|
||||
|
||||
Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in Areas gemäß §5.7, das inkrementelle Update bestehender Concepts gemäß §3 + §5.9 sowie die Synthese mehrerer `raw/`-Quellen zu einer gemeinsamen Wissensrepräsentation gemäß §5.10** begrenzt. Folgendes verbleibt in anderen Stories und wird hier **nicht** vorweggenommen:
|
||||
Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in Areas gemäß §5.7, das inkrementelle Update bestehender Concepts gemäß §3 + §5.9, die Synthese mehrerer `raw/`-Quellen zu einer gemeinsamen Wissensrepräsentation gemäß §5.10 sowie die Leasing-/Dirty-Tree-Koordination für konkurrierende Producer gemäß §5.11** begrenzt. Folgendes verbleibt in anderen Stories und wird hier **nicht** vorweggenommen:
|
||||
|
||||
- **Claim-granulare Provenienz** je belegter Aussage (Inline-`raw/`-Verweise, Kontext-Marker) — in **§5.5** dieser Instruktion verankert (Story 2.2; AD-4a, A0-3). Keine neue §7-Klasse, kein Standalone, keine Vertragsänderung.
|
||||
- **Deterministische Area-Zuordnung & Concept-Hierarchie** (Anlage von `wiki/<area>/index.md` + `wiki/<area>/<concept>.md`) — in **§5.7** dieser Instruktion verankert (Story 2.4; AD-7c, A0-10, A0-8, AD-13). Keine neue §7-Klasse, kein Validator-Change.
|
||||
- **Progressive Discovery über `index.md`** (Navigation, Area-Indizes) — in **§5.8** dieser Instruktion verankert (Story 2.5; AD-9, FR-11, AD-13, NFR-3). **Suche** bleibt konsumenten-/extern-seitig (Consumer-grep über `wiki/`, §5.8 Pkt. 4 — kein Bundle-/Instruktions-Thema mehr). Keine neue §7-Klasse, kein Schema-/Validator-Change.
|
||||
- **Eine genau-eine-Linkform** (file-relativ mit `.md`-Endung, in Areas `../`-fähig) — in **§5.6** dieser Instruktion gepinnt (Story 2.3; AD-7b, A0-9, FR-10; Auflösungsmodell §5.7 Pkt. 4) — der Punkt-11-Check des Validators akzeptiert bis auf Weiteres beide Schreibweisen (strukturell unverändert, Story-2.2-Präzedenz).
|
||||
- **Synthese über mehrere Sources** (mehrere `raw/`-Quellen → eine gemeinsame Wissensrepräsentation mit gemischter, claim-granularer Provenienz) — **in §5.10** dieser Instruktion verankert (Story 3.4; AD-4, FR-7). Die Verankerung des inkrementellen Datenflusses (Erweitern/Präzisieren/Korrigieren einzelner bestehender Concepts) bleibt **§3 + §5.9** überlassen und ist dort bereits verankert (Story 3.1). Beide sind damit aus diesem Vorbehalt entlassen.
|
||||
- **Leasing / Dirty-Tree-Schutz** für konkurrierende Producer → **Story 3.5/3.6** (AD-17a..f, A0-12..A0-16); die bestehende Commit-Boundary = Mutations-Boundary-Regel (§0/§5.3, AD-17f) bleibt in v1 bestehender Schutz.
|
||||
- **Relevanzbestimmung** (feinkörniger Relevanz-Findungsmechanismus der Kandidatenerhebung) — in **§3.2** dieser Instruktion verankert (Story 3.2; Term-Ziehverfahren + Kanonisierungs-Resolver `schema/canonical-terms.md`, drei Erhebungs-Stufen grep/ripgrep + `index.md`-Traversal + Link-Following mit besuchter Menge, `log.md`-Exklusion, Candidate-Liste als relative OKF-Pfade ohne `.md`, Determinismus-Vertrag AD-17h/A0-19). Keine neue §7-Klasse, kein Schema-/Validator-Change; die Em-Dash-`—`-Varianten-Lücke ist als Determinismus-Frage an Story 3.8 übergeben.
|
||||
- **Leasing / Dirty-Tree-Schutz** für konkurrierende Producer — **in §5.11 dieser Instruktion verankert** (Story 3.5; AD-17a..f, A0-12..A0-16; die Enum enthält AD-17d/A0-15 nur als **Norm-Rückverweis** — die §5.11-Auflösung selbst schließt Staleness aus und delegiert an Story 3.6, Pkt. 7-Seam-Kriterium): Lease-Akquise auf `lease/<area>/<id>`-Branches mit Lockfile und Merge-Base-Disziplin, Root-Scope-Lease inkl. `log.md`/`index.md`, Dirty-Tree-Schutz mit Stash/Scratch-Zone und `log.md`-Dokumentation, compiler-vermittelter Merge als AD-16-Pfad, kein textueller Auto-Merge, Commit-Boundary = Mutations-Boundary, Lease-Freigabe (Release, deterministisch); die bestehende Commit-Boundary = Mutations-Boundary-Regel (§0/§5.3, AD-17f) bleibt unverändert bestehender Schutz. **Lease-Staleness/Recovery** (TTL, Lease-Registrierung, verwaiste Leases, `raw/`-Recovery; AD-17d, A0-15) — **in §5.12 dieser Instruktion verankert (Story 3.6)**.
|
||||
- **Relevanzbestimmung** (feinkörniger Relevanz-Findungsmechanismus der Kandidatenerhebung) — in **§3.2** dieser Instruktion verankert (Story 3.2; Term-Ziehverfahren + Kanonisierungs-Resolver `schema/canonical-terms.md`, drei Erhebungs-Stufen grep/ripgrep + `index.md`-Traversal + Link-Following mit besuchter Menge, `log.md`-Exklusion, Candidate-Liste als relative OKF-Pfade ohne `.md`, Determinismus-Vertrag AD-17h/A0-19). Keine neue §7-Klasse, kein Schema-/Validator-Change; die Em-Dash-`—`-Varianten-Lücke ist als Determinismus-Frage an Story 3.8 übergeben. **Story-3.8-Auflösung:** die Determinismus-Frage ist in **§5.14** dieser Instruktion verankert (Story 3.8, Determinismus-Vertrag & Agent-Instruktions-Validator): die Em-Dash-Kollaps-Klasse, die Kollaps-Reichweite und der Match-Scope der Stufe a sind in **§3.2-Pkt.-1b** als geschlossene, deterministische Regel-Ergänzungen verankert; die Orphan-Politik ist in **§5.10 Pkt. 8** als deterministische Reconcile-Orphan-Regel präzisiert; die `generated.at`-Wanduhr-Gap-Ausnahme ist in §5.14-Pkt.-3 in die **Ausnahme-Menge** des Bundle-State-Vergleichs gefasst (A0-20-Konvention, Ask-First-geschützt). Keine offene Determinismus-Frage verbleibt (bestehende Story-Bullets unverändert).
|
||||
- **Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand** (logische Phasen-Disziplin: Analyse → Änderungsplanung → Mutation → Validierung, AD-6/A0-7, AC-1/AC-2/AC-3/AC-4) — **in §5.13 dieser Instruktion verankert (Story 3.7; D-3):** Änderungsplanung als institutionalisierter §5.9-Pkt.-6-P2-Block (textuell festgehalten, Plan-Defizit = textuell benannte Abbruch-Kette vor der Mutation), Plan-Freeze = Veränderungs-Sperre an die §5.9-Pkt.-5-Ghost-Diff-Kopplung (erlaubte Pfad-Menge: Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ `index.md`), Zustands-Restaurations-Invariante (Post-Rollback-Diff gg. Baseline leer / Bundle == valide committet, §5.3-Pkt.-3-/§6-Pkt.-3-Rollback unverändert), kein Teilerfolg als fertige Mutation (AD-17f), keine eigene Workflow-Engine (logische Trennung in einer Session, kein Prozess/Server/MCP, D-3/AD-11). Keine neue §7-Klasse, kein Schema-/Validator-Change, keine Vertragsänderung.
|
||||
- **Standalone-Compiler / eigene LLM-Runtime / MCP** → verboten in v1 (D-3, D-4, AD-11).
|
||||
- **OKF-Dialekt / Schema-Erweiterung** → niemals (AD-1a; Vertrag §7 „abschließende Liste").
|
||||
|
||||
@@ -365,9 +441,9 @@ Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in A
|
||||
|
||||
- `schema/wiki-compiler.md` — autorisierter Vertrag (Story 1.3): §2 Bundleroot, §3.1–§3.7 Feldsubset & Formate, §5 `log.md`-Typ, §6 Index-Regel/Prädikate, §7 abschließende 14-Punkte-Liste, §8 Normreferenzen.
|
||||
- `schema/validator.md` — Prüfgrundlage (Story 1.4, Revision 9): §3 14 Punkte, §4 Normalform (Reihenfolge §4.1, ISO-8601 §4.3), §5 Verdikt, §6 Fachprüfungen (EC-1 Existenz, EC-3 Kalender, EC-11 non-md; Punkt-11-Area-Lesart formalisiert).
|
||||
- Architektur-Spine (raw/`architecture-spine`): AD-2/AD-3 (raw immutable), AD-4a (claim-granulare Provenienz, §5.5), AD-5 (inkrementelle Kompilation), AD-6 (Reason/Mutate-Trennung), AD-7a (Identität = OKF-Pfad ohne `.md`, §5.7), AD-7b (genau eine Linkform gepinnt, §5.6), AD-7c (deterministische Bereichszuordnung, §5.7), AD-7d (Renaming/Redirect-Pflicht — nicht in den ACs, Epic 3), AD-8 (Standard-Markdown-Links = Navigations-/Beziehungsschicht, §5.6), AD-9 (Progressive Discovery, §5.7/§5.8), AD-10 (agent-unabhängige Regeln), AD-11 (keine eigene Runtime), AD-13 (Retrieval gehört zu Consumers / Suche = Consumer-grep / keine Embedding-Bereichszuordnung, §5.7/§5.8; Relevanzbestimmung textuell-deterministisch, §3.2), AD-14 (Git liefert Historie, nicht Domain-State), AD-15 (Trust-Metadaten v1), AD-16 (Konflikte werden explizit bewahrt), AD-17a (nur veröffentlichte/committete Inhalte als Input), AD-17f (Commit-Boundary = Mutations-Boundary), AD-17h (Determinismus), D-3 (kein Standalone).
|
||||
- Architektur-Spine (raw/`architecture-spine`): AD-2/AD-3 (raw immutable), AD-4a (claim-granulare Provenienz, §5.5), AD-5 (inkrementelle Kompilation), AD-6 (Reason/Mutate-Trennung, §5.13), AD-7a (Identität = OKF-Pfad ohne `.md`, §5.7), AD-7b (genau eine Linkform gepinnt, §5.6), AD-7c (deterministische Bereichszuordnung, §5.7), AD-7d (Renaming/Redirect-Pflicht — nicht in den ACs, Epic 3), AD-8 (Standard-Markdown-Links = Navigations-/Beziehungsschicht, §5.6), AD-9 (Progressive Discovery, §5.7/§5.8), AD-10 (agent-unabhängige Regeln), AD-11 (keine eigene Runtime), AD-13 (Retrieval gehört zu Consumers / Suche = Consumer-grep / keine Embedding-Bereichszuordnung, §5.7/§5.8; Relevanzbestimmung textuell-deterministisch, §3.2), AD-14 (Git liefert Historie, nicht Domain-State), AD-15 (Trust-Metadaten v1), AD-16 (Konflikte werden explizit bewahrt), AD-17a (nur veröffentlichte/committete Inhalte als Input), AD-17b (Root-Scope-Lease, §5.11), AD-17c (kein textueller Auto-Merge, compiler-vermittelter Merge, §5.11), AD-17d (Lease-Staleness, §5.12), AD-17e/f (Dirty-Tree-Schutz; Commit-Boundary = Mutations-Boundary, §5.11/§0/§5.3), AD-17g (Unentscheidbarkeit → menschliche Eskalation, §5.11), AD-17h (Determinismus, §5.14 — Zwei-Run-Bestätigung mit benannter `generated.at`-Wanduhr-Gap-Ausnahme, A0-20/§5.9-Pkt.-2/§5.10-Pkt.-8), D-3 (kein Standalone).
|
||||
- PRD (raw/prd): FR-2 (Sources vs. Curated), FR-5 (Concept-Erzeugung), FR-9 (OKF-Konformität), FR-10 (Concepts miteinander verlinken, §5.6), FR-11 (Progressive Discovery siehe PRD-§4.3-Zeile unten — Discovery-Pfad/gewurzelte Erreichbarkeit, §5.8), FR-16 (Consumer-Unabhängigkeit), NFR-3 (Agent Readability — Standard-Dateioperationen/grep über `wiki/`, §5.8 Pkt. 4), A-4 (nur lokale Sources).
|
||||
- Epics (raw/epics): Story-2.1-Ziel und -Abgrenzung zu Story 2.2–2.5; A0-3 (Kontext-Marker-Wortlaut, §5.5), A0-8 (Concept-Identität/Normalisierung, §5.7), A0-9 (eine erlaubte Linkform, §5.6), A0-10 (deterministische Bereichszuordnung, §5.7), A0-13 (Lease-Root-Scope); FR-6/FR-12/FR-14, A0-6/A0-7/A0-11/A0-18 (Belege der nachkonformierten Concept-Bodies); **A0-6 (inkrementeller Datenfluss Interpret → Reconcile → Synthesize → Update, §0/§3/§5.9), FR-6 (Aktualisierung statt neuer Dateien, §3/§5.9), FR-12 (unverändertes Wissen bleibt erhalten, §5.9)**, **A0-18 (Deterministische Relevanzbestimmung — grep/ripgrep, `index.md`-Traversal, Link-Following, keine Embedding-/Vector-Infrastruktur; §3.2, AD-13, PRD OQ-3)**, **A0-19 (Determinismus-Vertrag: gleicher Git-State + gleiche Eingabemenge → gleicher Bundle-State / gleiche Candidate-Liste in gleicher Reihenfolge; §3.2, AD-17h)**.
|
||||
- Epics (raw/epics): Story-2.1-Ziel und -Abgrenzung zu Story 2.2–2.5; A0-3 (Kontext-Marker-Wortlaut, §5.5), A0-8 (Concept-Identität/Normalisierung, §5.7), A0-9 (eine erlaubte Linkform, §5.6), A0-10 (deterministische Bereichszuordnung, §5.7), A0-13 (Lease-Root-Scope, §5.11); FR-6/FR-12/FR-14, A0-6/A0-7/A0-11/A0-18 (Belege der nachkonformierten Concept-Bodies); **A0-6 (inkrementeller Datenfluss Interpret → Reconcile → Synthesize → Update, §0/§3/§5.9), FR-6 (Aktualisierung statt neuer Dateien, §3/§5.9), FR-12 (unverändertes Wissen bleibt erhalten, §5.9)**, **A0-12 (Leasing-Modell — `lease/<area>/<id>`-Branches, Merge-Base-Disziplin, Lockfile semantisch identisch in jedem Adapter, §5.11), A0-13 (Root-Scope-Lease inkl. `log.md`/`index.md`/Root-Dateien, §5.11), A0-14 (kein textueller Auto-Merge; compiler-vermittelter Merge über AD-16, §5.11), A0-15 (Lease-Staleness, §5.12), A0-16 (Dirty-Tree-Schutz: Pre-Mutation-Prüfung, Stash/Scratch-Zone, `log.md`-Dokumentation, §5.11)**, **A0-18 (Deterministische Relevanzbestimmung — grep/ripgrep, `index.md`-Traversal, Link-Following, keine Embedding-/Vector-Infrastruktur; §3.2, AD-13, PRD OQ-3)**, **A0-19 (Determinismus-Vertrag: gleicher Git-State + gleiche Eingabemenge → gleicher Bundle-State / gleiche Candidate-Liste in gleicher Reihenfolge; §3.2/§5.11, AD-17h)**.
|
||||
- PRD §4.3 (FR-11 — progressive Discovery, §5.7 Pkt. 5/§5.8) und §8.2/§8.3 (Canonical State; Separation of Concerns), **PRD OQ-3 („Compilation Scope" — wie findet der Compiler relevante vorhandene Concepts; textuell-deterministische Relevanzbestimmung, §3.2; AD-13/A0-18; Muster `raw/architecture-spine/…md` §8.3)**.
|
||||
|
||||
**Revisionslog:**
|
||||
@@ -394,3 +470,7 @@ Diese Instruktion ist auf die **Erzeugung neuer Concepts auf Root-Ebene und in A
|
||||
- **Revision 2.7 (2026-08-19, Story 3.2, Step-04-Review Loop 3, 4 Layer; Nutzer-Entscheidungen D-1/D-2/D-3/D-4/D-5 = 1/1/1/1/1, jeweils empfohlene Option; + 2 Patches; D-3-Instruktions-Patch, kein neuer Inhalt):** (1) **§3.2-Pkt.-3b-Ordnung auf reine Lexikografie gehoben (D-4):** Stufe-a-Treffer waren „in der Reihenfolge des ziehenden Terms, dann lexikografisch als deterministischer Tie-Break" — die Term-Ordnung selbst war für Mehrfach-Terme (Pkt. 1c) nirgends festgelegt, zwei rechtmäßige Term-Ordnungen erzeugten zwei unterschiedliche Listen (AD-17h-Lücke). Jetzt: **innerhalb jeder Stufe rein lexikografisch aufsteigend (LC_ALL=C)**; die Term-Verarbeitungsreihenfolge dient nur der Interpretation/Erhebung, **nicht** der Listen-Ordnung — dieselbe Treffermenge → identische Liste. (2) **`schema/canonical-terms.md`-Lookup-Semantik vervollständigt (D-5):** Lookup-Verfahren deterministisch fixiert (lowercasing → `[-–_ ]`→`-`-Kollaps → **Lookup der normalisierten Form**; Spalten tragen ausschließlich normalisierte Formen), **Eindeutigkeits-Invariante** (jede normalisierte Form in genau einem Eintrag — Canon **oder** Variante, nie beides/zweimal) + **Konflikt-Verfahren** (keine stille Anhängung; `deferred-work.md`-Handoff / Ask-First, analog zur Umbenennungs-Regel). (3) **Statuskette `in-progress → done` dokumentiert (D-1):** der Review-Loop-Abschluss-Flip `in-progress → done` (Sprint-Sync-Konvention, Story-3.1-Präzedenz) war in keinem `wiki/log.md`-Eintrag als eigener Schritt belegt — nachgeführt als neuer oberster `wiki/log.md`-Bullet (append-only; die gefrorene Always-Klausel `→ in-progress` beschreibt den Implementierungsstand, der Review-Abschluss `done` ist der übliche Folgezustand). (4) **Mini-Sandbox um T5/T6/T7 erweitert + T1/T4-Asssertionen + T2-Kommentar-Korrektur (D-2 + Patch P-1):** T5 `LINK_FOLLOWING_ZYKLUS` (A→B→A-Links; besuchte Menge → endliche, doppelungsfreie Liste), T6 `TRAVERSAL_REACH_ONLY` (Term nur in Area-`index.md` → gewurzelte Concept-Pfade als Kandidaten; `index.md` selbst ist kein Concept-Kandidat), T7 `TERM_ABLEITUNG_SYNONYM` (Registry-Test-Doppel: Schreibvarianten → canonische Form via Lookup auf normalisierter Form; Negativ-Fall: nicht auflösbar → wie notiert, kein stiller Ausschluss); T1/T4 tragen jetzt harte Pass/Fail-Asssertionen (`exit 1` bei Abweichung) — die exakte Candidate-Liste wird erzwungen, nicht nur ausgegeben; der T2-Kommentar zu `index` korrigiert (Stufe-a-Treffer auf `wiki/index.md` löst Stufe b aus; `index.md` ist kein Concept-Kandidat). (5) **Selbsttest-Beleg (b) gegen den realen Sandbox-Ist-Baum re-executiert (D-3):** der Rev-2.5-Log-Beleg (b) beschrieb einen nicht-committierten Baum (Term `quanten-protocol`, `sub/beta.md`, SHA-256 `159092bb…`) — Stale-Evidenz-Falle (B1-Präzedenz). Der Nachweis wird jetzt gegen die echte `run-sandbox.sh`-Ausgabe (Term `deterministische-relevanz-bestimmung`, root-level `alpha`/`beta`/`gamma`) neu belegt; der Rev-2.5-Eintrag bleibt historisch unverändert, die Korrektur steht als neuer `wiki/log.md`-Bullet (append-only). (6) **Typos im normativen Text korrigiert (Patch P-2):** `Determinsmus` → `Determinismus` (§8-Referenzen + Log-/Defer-Belege), `§3.2-beankert` → `§3.2-angeankert`, `Membrum` → `Mitglied` (Spec-Design-Notes), `Konventionelle Determinismus-Lücke` → `Bekannte Determinismus-Lücke` (Defer-Beleg; `compiler.md:53` sagt „Bekannte"), `Resovierung` → `Auflösung` (Spec-Change-Log/-Verification). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; Commit-Boundary-Regel unverändert. (Revisionslog-Reihenfolge-Anomalie 2.6/2.5 bleibt als dokumentierter Defer bestehen — nicht in dieser Runde umgeordnet.)
|
||||
- **Revision 2.8 (2026-08-19, Story 3.3):** §5.9-Update-Formen **operationell ausformuliert** (dieselbe Evidenz + formale Struktur → identisches Update-Ergebnis, AD-17h/A0-19): (1) **Pkt. 2 — Präzisierungsebene je Update-Form** (als Sub-Bullet an die Commit-Boundary-Regel angefügt; der frozen Story-3.1-Regeltext bleibt textuell **unverändert** als normative Basis, die operationellen Regeln sind die Ausführungs-Ebene darunter — keine Re-Negotiation): je Form **(a) Abgrenzungskriterium** (aus der committeten Evidenz: neue belegte Aussage → Erweitern; Schärfung einer bestehenden Formulierung/Abgrenzung ohne Ersatz → Präzisieren; Ersetzung einer fehlerhaften/überholten → Korrigieren; keines davon → No-Op, engere Auslegung), **(b) Struktur-Erhaltungsregel** (das Geschützte — Frontmatter-Subset nur als `sources`-Zuwachs um echten neuen Beleg + `generated.at`-Bump; bestehende belegte Aussagen nicht umgeschrieben ohne dass Präzisieren/Korrigieren greift; §5.5-Inline-Verweise gültig soweit Beleg Body-Bestand; §5.6-Linkform unverändert, keine neuen Links außer bei echten Beziehungen) und **(c) Textgenauigkeits-Rahmen für Präzisieren** (Schärfen an der Aussage, kein Satz-Umbau, kein Neuschreiben ohne Ersetzungsbeleg). (2) **Pkt. 5 — Erhaltungs-Invariante um den Struktur-Erhaltungs-Bezug ergänzt:** die Diff-Probe verifiziert zwei Ebenen — die Pfad-Mengen-Ebene (bestehender Teilmenge-Vergleich) und die **Struktur-Ebene je berührtem Pfad** (pro betroffenem Concept-Pfad über die Pkt.-2-Regeln geprüft: Frontmatter-Subset, keine Umschreibung belegter Aussagen außerhalb der Formen, Inline-Verweise, Linkform — Verstöße textuell benannt (NFR-4), vor dem Commit zu beheben, sonst Ghost-Diff mit Rollback). (3) **Pkt. 6 — P2-Check-Block um den Struktur-Erhaltungs-Check erweitert** (Element (6), zusätzliches textuelles Element: keine unbefugten Keys — Vertrag §3.3/§3.4-Subset, keine stille Löschung — AD-16/„Korrigieren"-Form, Links unverändert/keine neuen ohne echte Beziehung — §5.6-Pin; Verstöße textuell benannt (NFR-4) und vor dem Commit behoben). (4) **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; Commit-Boundary-Regel unverändert; kein Leasing-Scope (AC-4 → Story 3.5/3.6, AD-17.3-Hinweis unverändert in §7). **`sprint-status.yaml`:** Key `3-3-bestehende-concepts-erweitern-präzisieren-korrigieren` bleibt **`in-progress`** (Review-Abschluss `done` erfolgt gemäß Workflow-Konvention durch den Review-Schritt). Sandbox-Nachweis und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.3, Revision 2.8).
|
||||
- **Revision 2.9 (2026-08-19, Story 3.4):** Neue Sektion **§5.10 „Synthese aus mehreren Sources (Story 3.4)"** eingefügt (nach §5.9, vor §6) — die **verbindliche Verankerung der Synthese-Dimension** (AD-4, FR-7): (1) **Synthese-Stimulus** (≥ 2 belegende `raw/`-Quellen desselben Themas per §2-Interpretation → eine Synthese-Einheit; einzelne Quelle bleibt auf §5.9-/§3.2-Pfad), (2) **Ein-Ziel-Repräsentation (§5.7-Routing; FR-7 AC-1)** — ein Synthese-Concept über einen Ziel-Pfad, `sources`-Liste ≥ 2 Einträge, **deterministisch lexikografisch nach `resource` (LC_ALL=C, AD-17h)**, getrennte Zusammenfassungs-Concepts je Quelle verboten; (3) **gemischte claim-granulare Provenienz (AD-4a/4b, A0-3)** — je Aussage Inline-`raw/`-Verweis §5.5, **Multi-Beleg-Konsolidierung** (§5.5-Semikolon-Form, voller Pfad je Beleg; keine Beleg-Tilgung AD-4), **AD-16-Default** (widersprüchliche Aussagen bleiben, Disagreement in `log.md`; Sandbox-N2); (4) **AD-4c-Übernahme-Marker** („übernommen aus `<Concept>` auf Basis von `<source>`, nicht eigenständig belegt" — nie alleinige Provenienz; Sandbox-N3); (5) **Reflektiertheits-Selbsttest** (FR-7 AC-4/NFR-7) — keine per-Source-Zusammenfassungs-Struktur („Quelle A: …"), grepbasiert `grep -nE 'Quelle [A-Z]:|Source [A-Z]:'`, Selbsttest-FAIL → textuell benannt (NFR-4) und vor Run-Abschluss behoben; (6) **§5.6-Pin unverändert** + **Form-Wahl-Klassifikationsprobe (Story-3.3-Defer U2/U7)** — Überlapp-Einheiten an die §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` (erste zutreffende Form), Textgenauigkeits-Rahmen der übrigen je Teilbestand; (7) **`log.md`-Eintragspflicht** („Story 3.1-Update" bei Update, Anlage-Eintrag bei neuem Synthese-Concept; Multi-Source-Liste + `<Baseline-Commit>`); (8) **Erhaltungs-Invariante (§5.9 Pkt. 5 gilt) + Determinismus-Vertrag** (Ziel-Pfad via §3.2/§5.7, `sources`-Lexikografie, Konsolidierung/Form-Zuordnung; `generated.at`-Wanduhr-Gap bleibt A0-20-Konvention, Home Story 3.8). **§7:** Synthese-Vorbehalt **aufgelöst** (in §5.10 verankert; verbleibende 3.x-Themen: Leasing/Dirty-Tree → Story 3.5/3.6). **§8:** Normreferenzen bleiben unverändert (AD-4/AD-4a/FR-7/A0-3 sind bereits über §5.5/Roh-Normreferenzen abgedeckt; AD-4c-Kontext-Marker weiterhin §5.5). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **keine fünfte Update-Form** (Abgrenzungs-Reihenfolge §5.9 unverändert, Synthese = Querschnitt); kein Leasing-Scope; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-4-wissen-aus-mehreren-sources-synthetisieren` → **`in-progress`**. Sandbox-Nachweis (S1–S6 + N1–N3 + Form-Wahl-Probe = 10 Szenarien, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.4, Revision 2.9). **Review-Loop-1-Patches (bmad-code-review, 3 Layer; dieser Eintrag nachgeführt):** Reflektiertheits-Selbsttest-Muster auf Zeilenanfangs-Label erweitert (`^(Quelle|Source) [A-Z][A-Za-z0-9_ -]*:` — mehrbuchstabige/nummerierte Quell-Labels werden ebenso erkannt); **Body-Reihenfolge deterministisch** (Aussagen-Positionierung lexikografisch über die Beleg-Anker, AD-17h) + **Konsolidierungs-Kriterium** (Befund-Äquivalenz über dieselbe erkannte Wissenseinheit) explizit; Stil-/Genus-Korrekturen (`sole`→`einzige`, `der Update`→`das Update`, `sources`-Pluspunkt→`sources`-Zuwachs). Keine Änderung an Normreferenzen, §7, Abschlussklausel.
|
||||
- **Revision 3.0 (2026-08-19, Story 3.5):** Neue Sektion **§5.11 „Leasing & Dirty-Tree-Schutz für konkurrierende Producer (Story 3.5)"** eingefügt (nach §5.10, vor §6) — die **verbindliche Verankerung der Koordinations-Dimension für konkurrierende Producer** (AD-17a..f, A0-12..A0-16, FR-2/FR-12; §7-Vorbehalt `:357` aufgelöst): (1) **Lease-Akquise (AD-17a, A0-12)** — Arbeits-Branch-Form **`lease/<area>/<id>`** von der Merge-Base aus, **Lockfile** (`lease/<area>/<id>.lock`, semantisch identisch in jedem Adapter, A0-12; deterministisches Format: `area`, `id`, `producer`, `baseline_commit` voller SHA, `holder_id`), Lease-Hold bei existierendem Lockfile (kein Überschreiben, keine Mutation), **Merge-Base-Disziplin** (Lease gg. eindeutigen Commit-Object-Wert über `git merge-base`/`<Baseline-Commit>`, AD-17h); (2) **Root-Scope-Lease (AD-17b, A0-13)** — umfasst `wiki/` inkl. `log.md`, `index.md` und aller Root-Dateien; kein Bereich jenseits `wiki/`; (3) **Dirty-Tree-Schutz (AD-17e/f, A0-16)** — Pre-Mutation-Prüfung (`git status --porcelain -- <Mutationsbereich>`), fremde uncommittete Änderungen **geschützt statt gelöscht** (Stash/Scratch-Zone außerhalb `wiki/`, dokumentiert in `log.md`), Screen-Artefakte textuell benannt (NFR-4), **UNCOMMITTED_INPUT-Abbruch** „published/committed Input erforderlich" (AD-17a; I/O-Matrix-`UNCOMMITTED_INPUT` — dieselbe Pre-Run-Prüfung wie §5.9-P2-Element-(1)-`INPUT_UNCOMMITTED`, kein zweiter Abbruch-Pfad), Mutationen nur auf Directory-/Commit-Ebene; (4) **kein textueller Auto-Merge (AD-17c, A0-14)** — compiler-vermittelter Merge über die **AD-16-Klassifikation** (Default: Erhaltung) mit `log.md`-Eintrag; Lease-Konflikt → AD-16-Pfad; Unentscheidbarkeit → menschliche Eskalation (AD-17g); (5) **Commit-Boundary = Mutations-Boundary** unverändert (§0/§5.3, AD-17f) — Diff-Selbsttest (§5.9 Pkt. 5) auch für Leasing-Runs, Ghost-Diff-Rollback; (6) **`log.md`-Eintragspflicht** (Lease-Akquise, Dirty-Tree-Schutz-Dokumentation, Merge-Klassifikation, Eskalation, **Lease-Freigabe**; `<Baseline-Commit>` notiert, D-2; Datumsgruppe **neueste zuerst**); (7) **Determinismus-Vertrag (AD-17h/A0-19)** — Lease-Akquise, Lockfile-Inhalte, Merge-Klassifikation deterministisch aus dem committeten Git-State; **Lease-Staleness/Recovery bleibt Story 3.6** (AD-17d, A0-15) — diese Sektion mutiert deren Mechanik nicht; testbares Seam-Kriterium (3.5 = committed-state-deterministisch, 3.6 = Zeit-/Umgebungs-Zustands-Frage, die den committeten Zustand verlässt). **§7:** Leasing-/Dirty-Tree-Vorbehalt **aufgelöst** („in §5.11 verankert (Story 3.5)"); Scope-Einleitung um §5.11 geöffnet; verbleibendes 3.x-Thema: Lease-Staleness/Recovery → Story 3.6. **§8:** Normreferenzen um AD-17b/AD-17c/AD-17d/AD-17e-f/AD-17g (Spine) und A0-12/A0-13/A0-14/A0-15/A0-16 (Epics) ergänzt; AD-17d/A0-15 in der §5.11-Enum als Norm-Rückverweis (ohne §5.11-Auflösungs-Bezug) gekennzeichnet. **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Frontmatter-Key für Lease-Metadaten** (Lease lebt in Git/Datei-Ebene, Vertrag §3.1–§3.7 unverändert); **keine fünfte Update-Form** (Abgrenzungs-Reihenfolge §5.9 unverändert, Leasing = Querschnitt); kein Staleness-Scope (→ Story 3.6); Commit-Boundary-Regel unverändert. **Lease-Freigabe** (Release) ist in §5.11 Pkt. 1/6 verankert (deterministisch, auf das committete Ergebnis bezogen); Staleness-/Recovery-Aspekte der Freigabe (verwaiste Leases) bleiben Story 3.6. **`sprint-status.yaml`:** Key `3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz` → **`in-progress`**. Sandbox-Nachweis (L1–L6 + Negativ-Kontrollen, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.5, Revision 3.0).
|
||||
- **Revision 3.1 (2026-08-19, Story 3.6):** Neue Sektion **§5.12 „Lease-Staleness & Recovery-Basis (Story 3.6)"** eingefügt (nach §5.11, vor §6) — die **verbindliche Verankerung der Staleness-/Recovery-Dimension** (AD-17d, A0-15; §5.11-Seam-Kriterium S-1 und Pkt.-7-Text sowie §5.11 Pkt. 1 „verbleibt bis Story 3.6" bleiben textuell unverändert): (1) **Lease-Registrierung & generationenbasiertes TTL** — Registrierung im **Clone-Root-State**, monotoner Run-Generator (`generation`-Zähler je Clone-Root, deterministisch aus dem committeten Git-State ableitbar: `lease/`-Baumableitung oder Mono-Commit-`lease-granite-root`-Marker), **TTL-Ablauf-Kriterium generationen-basiert** (ältere/niedrigere Erzeugungs-Generation = stale, blockiert keinen nachfolgenden Run), **kein Wanduhr-Timestamp** im Lockfile-/Registrierungs-Format (A0-20; streng durchsetzendes Zeit-TTL = Ask-First); (2) **holder_id-Ableitung** (deterministischer Default `holder_id := <producer>-<id>`, Defer aufgegriffen); (3) **Verwaist-Klassifikation** (Übernehmen gg. erneute Merge-Base-Prüfung oder Stale-Markieren via Registry-Marker, je `log.md`-Eintrag; verwaiste Leases **nie still gelöscht**, AD-17e; Konflikt → AD-16-Pfad/Eskalation AD-17g); (4) **baseline_commit-Merge-Base-Diskrepanz-Regel** (git-merge-base gewinnt, notierter SHA = Sekundär-Fingerprint, Fallback ohne Boundary; Defer aufgegriffen); (5) **`raw/`-Recovery-Basis & native `git stash`-Variante** (`raw/` immutable AD-3 als Zugriffs-/Consistency-Basis, Real-Baum-Beweis auf Sandbox-Evidenzwege beschränkt, EC-1-Grenze; `git stash push -- <Pfade>` als zweitezulässige Schutzvariante, Defer aufgegriffen); (6) **Registrierungs-Invariante & kumulativer Registry-Aufbau über Runs** (Gen > erzeugend oder gleiche Gen, hält den sichtbar höchsten Reg-Generator; kein eigener `# Log`-Stand — `wiki/log.md` alleiniger Aufzeichnungs-Ort, Vertrag §5; Defer aufgegriffen); (7) **`log.md`-Eintragspflicht & Determinismus-Vertrag** (Registrierung/TTL, Lease-Übernahme, Stale-Markierung, Recovery-Basis-Nutzung als Datumsgruppen-Einträge; Registrierung/TTL/Verwaist-Klassifikation/log.md-Texte deterministisch aus dem committeten Git-State). **§7:** Staleness/Recovery-Vorbehalt **aufgelöst** („in §5.12 verankert (Story 3.6)"). **§8:** Normreferenzen AD-17d/A0-15 von reiner Story-Zuordnung auf **§5.12-Anker** angehoben (`AD-17d (Lease-Staleness, §5.12)`, `A0-15 (Lease-Staleness, §5.12)`). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Frontmatter-Key für Lease-Metadaten** (Lease lebt in Git/Datei-Ebene, Vertrag §3.1–§3.7 unverändert); kein Prädikat-/Format-Key; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-6-lease-staleness-recovery-basis-absichern` → **`in-progress`**. Sandbox-Nachweis (STALE-1..STALE-6 + Erhaltungs-Invariante, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.6, Revision 3.1).
|
||||
- **Revision 3.2 (2026-08-20, Story 3.7):** Neue Sektion **§5.13 „Reason/Mutate-Phasen-Trennung & Konsistenz-Endzustand (Story 3.7)"** eingefügt (nach §5.12, vor §6) — die **verbindliche Verankerung der logischen Phasen-Disziplin** (AD-6, A0-7; §7-Vorbehalt der Reason/Mutate-Trennung aufgelöst): (1) **Änderungsplanung** — der §5.9-Pkt.-6-P2-Block wird Wortlaut-unverändert als Änderungsplanungs-Phase der AD-6-Kette institutionalisiert (textuell festgehaltener konsistenter Plan: Input-Zustand, Ziel-Pfade, Quellen-Existenz, Betroffenheits-Liste, Struktur-Erhaltung; Plan-Defizit textuell benannt NFR-4 und verhindert die Mutation — I/O-Matrix `PLAN_BEABSICHTIGT`); (2) **Plan-Freeze** (Veränderungs-Sperre nach Phasenabschluss, Kopplung an die §5.9-Pkt.-5-Ghost-Diff-Probe; erlaubte Pfad-Menge = Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ `index.md` — `PLAN_FREEZE`); (3) **Zustands-Restaurations-Invariante** (Post-Rollback-Diff gg. Baseline leer / Bundle == valide committet; §5.3-Pkt.-3-/§6-Pkt.-3-Rollback und Ghost-Diff-Rollback Wortlaut-unverändert, Endzustands-Konsistenz als git-prüfbare Eigenschaft — `MUTATION_ABBRUCH`, A0-7-AC-2); (4) **kein Teilerfolg als fertige Mutation** (`MUTATION_TEILFOLGE`, AD-17f unverändert); (5) **Validierungsphase `VALIDATION_FAIL`** (§6-Pkt.-3-Rollback, keine weiteren Mutationen, Endzustand konsistent); (6) **Validierungsphase `VALIDATION_SUCCESS`** (Mutationen als Ganzes committet erst nach Diff-Selbsttest ohne Ghost-Diff, §6-Pkt.-4-Nachweis, kein Wanduhr-Trigger A0-20 — A0-7-AC-3); (7) **keine eigene Workflow-Engine** (`KEINE_EIGENE_ENGINE`: logische Trennung in einer Session, kein Prozess/Server/MCP, D-3/AD-11 — A0-7-AC-4). Die **AD-6-Phasen-Zuordnungstabelle (§0 ↔ vier AD-6-Phasen)** ist eine Klassifikation ohne neue Norm: §0 bleibt die deterministische Takt-Folge (Wortlaut unverändert, keine Um-Nummerierung, keine neue Phase); §5.9-Pkt.-6-P2-Block, §5.3-Pkt.-3/§6-Pkt.-3-Rollback und §5.9-Pkt.-5-Diff-Probe bleiben **textuell unverändert** (Rückverweise, keine Doppel-Instruktion); §5.12-Seam-Satz unverändert (Lease-Staleness/Recovery bleibt Story 3.6). **§7:** Reason/Mutate-Vorbehalt **aufgelöst** („in §5.13 verankert (Story 3.7)"-Bullet, AD-6/A0-7, AC-1/2/3/4; bestehende Story-Bullets unverändert). **§8:** Normreferenz AD-6 von reiner Story-Zuordnung auf **§5.13-Anker** angehoben (`AD-6 (Reason/Mutate-Trennung, §5.13)`); A0-7-Reihenfolge-Referenzen in der Epic-Zeile um §5.13-Bezug nicht neu aufgelistet (A0-7 bleibt in der bestehenden Epic-Enum, die §5.13-Ankerung erfolgt über die Spine-AD-6-Zeile und den neuen §7-Bullet). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; keine neue Workflow-Engine (AD-6); §0-Phasen-Listentext unverändert; Commit-Boundary-Regel unverändert. **`sprint-status.yaml`:** Key `3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell` → **`review`** (Implementierungs-Flip `in-progress` + Review-Start-Flip `review` im selben Commit; der finale `done`-Flip erfolgt im Step-05-Status-Sync). Sandbox-Nachweis (CONSIST-1..CONSIST-7 + Erhaltungs-/Restaurations-Invariante, Exit 0) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.7, Revision 3.2).
|
||||
- **Revision 3.3 (2026-08-20, Story 3.8):** Neue Sektion **§5.14 „Determinismus-Vertrag & Agent-Instruktions-Validator (Story 3.8)"** eingefügt (nach §5.13, vor §6) — die **geschlossene, aus dem committeten Git-State ableitbare Bestätigungs-Mechanik des Determinismus-Vertrags** (AD-17h/FT-10/A0-19; §3.2/§5.9/§5.10/§5.11/§5.12/§5.13-Rückverweise verankern den Vertrag, §5.14 die Bestätigung): (1) **Bundle-State-Definition** (deterministische Projektion des committeten Git-States: committeter Baum unter `wiki/`/`raw/` gg. Baseline + Plan-/Kandidaten-/Reihenfolge-Outputs + Ausführungs-Entscheidungen; `generated.at`/`verified[].at` als benannte Ausnahme, A0-20 — einzig zulässige Differenz zwischen zwei Runs), (2) **Zwei-Run-Bestätigungs-Mechanik** (agent-Instruktions-basiert, D-3/Q-6: der Producer führt die Instruktion **zweimal über demselben committeten Git-State in einer Session** aus und vergleicht die Bundle-States — Vergleichs-Operandum = committeter Baum, re-executierbare diff-/hashbasierte Formeln, keine Wanduhr-Steuerung; identische Plan-/Kandidaten-/Reihenfolge-Outputs, byte-identische mutierte Bestandteile; keine eigene Runtime/kein neues Werkzeug, AD-6/AD-11), (3) **Ausnahme-Menge** (allein der `generated.at`-Wanduhr-Gap; dokumentiert, kein stiller Ausschluss; alle übrigen Bestandteile byte-identisch), (4) **Abweichungs-Klassifikation** (jede Differenz außerhalb der Ausnahme = AD-16-Klassifikationsfehler, kein Rauschen; textuell benannt NFR-4, Run korrigiert/rollt zurück, Zustands-Restaurations-Invariante §5.13 Pkt. 3), (5) **Normalisierungs-/Match-/Orphan-Schließung** (§3.2-Pkt.-1b-Em-Dash-Kollaps-Klasse `[-–— _]`, Kollaps-Reichweite, Match-Scope Stufe a; §5.10-Pkt.-8-Reconcile-Orphan-Regel; Erhebungen vollständig pinbar). **§3.2:** die bekannten Determinismus-Lücken als append-only-Regel-Ergänzungen geschlossen (Em-Dash in der Kollaps-Klasse, Kollaps-Reichweite, Match-Scope der Stufe a) — bestehender §3.2-Wortlaut unverändert. **§5.10 Pkt. 8:** Orphan-Politik zur deterministischen **Reconcile-Orphan-Regel** präzisiert (unzugeordnet, datumsgruppierter `log.md`-Verwaist-Eintrag mit `<Baseline-Commit>`, kein Banner/keine stille Vorbearbeitung/keine eigenständige Anlage; AD-16-Default). **§5.13 Pkt. 7:** Home-Verweis des `generated.at`-Gaps auf die §5.14-Definition verlagert (Wortlaut des §5.13-Satzbaus semantisch unverändert; die Behandlung des Gaps ist jetzt in §5.14-Pkt.-3 definiert). **§7:** Determinismus-Vorbehalt der Relevanzbestimmung **aufgelöst** (Em-Dash-Lücke → „in §5.14 verankert (Story 3.8)"-Ergänzung; §3.2-/§5.10-/`at`-Gap-Schließung benannt; bestehende Story-Bullets unverändert — kein neuer Bullet ersetzt einen bestehenden). **§8:** Normreferenz **AD-17h** von reiner Story-Zuordnung auf **§5.14-Anker** angehoben (`AD-17h (Determinismus, §5.14 — Zwei-Run-Bestätigung mit benannter `generated.at`-Wanduhr-Gap-Ausnahme, A0-20/§5.9-Pkt.-2/§5.10-Pkt.-8)`). **Abschlussklausel:** Keine Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`adapters/`/`raw/` (AD-3); **keine neue §7-Invaliditätsklasse**; **kein Standalone** (D-3); keine Vertragsänderung; **kein neuer Prädikat-/Format-/Frontmatter-Key**; das `generated.at`-Verhalten selbst **unverändert** (nur seine Behandlung im Bundle-State-Vergleich; Verhaltenswechsel = Ask-First); keine Workflow-Engine/kein neuer Prozess/Server/MCP (AD-6, AD-11); §0-Phasen-Listentext, §5.13-Phasen-Disziplin und §6/§6.5 textuell **unverändert** (Fugen-Identität); Commit-Boundary-Regel unverändert; `schema/canonical-terms.md` unverändert (Em-Dash-/Kollaps-Schließung lebt als Regel in §3.2, nicht als Registry-Edits). **`sprint-status.yaml`:** Key `3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato` **`backlog` → `in-progress`** (Implementierungs-Flip; finaler `review`/`done`-Flip im Step-04/05). Sandbox-Nachweis (**DET-1..DET-8 + Erhaltungs-Invariante, Exit 0**; Zwei-Run-Identität nicht-vakuum) und per-Datei-Validator-Verdikt siehe `wiki/log.md`-Eintrag (Story 3.8, Revision 3.3).
|
||||
|
||||
+10
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user