#!/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 && r0) 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: / id: / producer: / baseline_commit: / holder_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 := - — 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)"