Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
861e65f628 | ||
|
|
4a53771a9f | ||
|
|
895b006f7f |
@@ -413,3 +413,60 @@ 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.
|
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".
|
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
|
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)
|
||||||
|
|||||||
@@ -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)"
|
||||||
+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)
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
# - Dev moves story to 'review', then runs code-review (fresh context, different LLM recommended)
|
# - 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
|
# - Retrospective appends its action items to action_items; the status view surfaces open ones
|
||||||
generated: 08-14-2026 00:00
|
generated: 08-14-2026 00:00
|
||||||
last_updated: 08-19-2026 16:40
|
last_updated: 08-19-2026 22:10
|
||||||
project: wow20
|
project: wow20
|
||||||
project_key: NOKEY
|
project_key: NOKEY
|
||||||
tracking_system: file-system
|
tracking_system: file-system
|
||||||
@@ -55,8 +55,8 @@ development_status:
|
|||||||
3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip: done
|
3-2-relevanzbestimmung-textual-deterministisch-umsetzen-grep-rip: done
|
||||||
3-3-bestehende-concepts-erweitern-präzisieren-korrigieren: done
|
3-3-bestehende-concepts-erweitern-präzisieren-korrigieren: done
|
||||||
3-4-wissen-aus-mehreren-sources-synthetisieren: done
|
3-4-wissen-aus-mehreren-sources-synthetisieren: done
|
||||||
3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz: backlog
|
3-5-leasing-dirty-tree-schutz-für-konkurrierende-producer-umsetz: done
|
||||||
3-6-lease-staleness-recovery-basis-absichern: backlog
|
3-6-lease-staleness-recovery-basis-absichern: done
|
||||||
3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: backlog
|
3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: backlog
|
||||||
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: backlog
|
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: backlog
|
||||||
epic-3-retrospective: optional
|
epic-3-retrospective: optional
|
||||||
|
|||||||
+46
-4
@@ -300,6 +300,46 @@ Diese Sektion ist der **Instruktions-Ort der Synthese-Dimension** (AD-4, FR-7):
|
|||||||
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.
|
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):** 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.
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
## 6. Validieren (mechanische Bestätigung)
|
## 6. Validieren (mechanische Bestätigung)
|
||||||
|
|
||||||
1. Nach Abschluss aller Mutationen wird das gesamte Bundle gemäß `schema/validator.md` geprüft (§3 14 Punkte je Datei + §6-Fachprüfungen; Verdikt-Grammatik §5).
|
1. Nach Abschluss aller Mutationen wird das gesamte Bundle gemäß `schema/validator.md` geprüft (§3 14 Punkte je Datei + §6-Fachprüfungen; Verdikt-Grammatik §5).
|
||||||
@@ -347,14 +387,14 @@ Interpretations-Hinweis: Die „✗"-Zeilen zeigen die deterministische Fehlerur
|
|||||||
|
|
||||||
## 7. Selbstbegrenzung (Scope der Instruktion)
|
## 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.
|
- **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.
|
- **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.
|
- **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).
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
||||||
- **Standalone-Compiler / eigene LLM-Runtime / MCP** → verboten in v1 (D-3, D-4, AD-11).
|
- **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").
|
- **OKF-Dialekt / Schema-Erweiterung** → niemals (AD-1a; Vertrag §7 „abschließende Liste").
|
||||||
@@ -365,9 +405,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/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).
|
- `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), 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), 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).
|
- 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)**.
|
- 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:**
|
**Revisionslog:**
|
||||||
@@ -394,3 +434,5 @@ 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.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.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 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).
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user