F3b: the gate with real-SIGKILL recovery arms, and the sabotage suite
gate-f3b.sh: both harness self-tests; the 12-arm F3b battery; the 22-arm F3a battery as the no-regression arm; SIGKILL-mid-rename at four staggered delays PLUS a busy-poll round that kills the instant the manifest atomically appears -- the poll round CAUGHT the window on its first run (manifest_at_kill=yes) and recovery completed a real-killed validated set to the fully-new state with the report, so completion is demonstrated against a genuine SIGKILL, not only against constructed crash bytes. Coherence contract per round: every file fully-old or fully-new, all files agreeing, never torn, manifest never surviving recovery. Arm 5 constructs the UNPROVABLE state (validated manifest, corrupted temp) and requires the refusal-to-a-human with nothing touched.
sabotage-f3b.sh: six legs, predictions first, plants hash-guarded and build-checked, restores by checkout verified by hash, full gate re-run green at the end. T1 tolerates the raw-vs-semantic accounting -- the graph closure must catch the residue; T2 fells BOTH layers -- the stale referrer publishes and b5's content asserts catch it (the pairing shows the layers are independent, not redundant); T3 makes delete ignore referrers; T4 silences recovery entirely; T5 makes recovery tolerate the unprovable state (gate arm 5's driver observes it proceeding over garbage); TA is the assertion-layer leg on the bystander hash.
Committed BEFORE the sabotage dry-run, per this session's own a-dry-run-of-a-restore-script-is-still-a-restore lesson.
test/f3b-corrupt-temp-driver.sgl | 50 ++++++++++++++++++++++++++++++++++++++
test/f3b-recover-driver.sgl | 24 +++++++++++++++++++
test/f3b-renamer.sgl | 25 +++++++++++++++++++
test/gate-f3b.sh | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/sabotage-f3b.sh | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 507 insertions(+)test/f3b-corrupt-temp-driver.sgladded
;;; f3b-corrupt-temp-driver.sgl -- construct the UNPROVABLE recovery state;;; (a validated manifest whose staged temp was corrupted after the crash);;; and prove the next transaction REFUSES TO A HUMAN and touches nothing.;;; Used by gate-f3b.sh arm 5; sabotage-f3b.sh's recovery-tolerates plant;;; makes this driver's refusal vanish, which is that leg's red.(import (sigil core) (sigil io) (sigil fs) (sigil string) (sigil path) (folio doc revision) (folio doc bounded) (folio doc service) (folio doc manifest) (folio doc coordinator) (folio doc transaction) (folio doc structural))(define root (or (getenv "F3B_ROOT") (begin (println "DRIVER ERROR no root") (exit 3))))(define lib-dir (or (getenv "F3B_LIB") "build/dev/lib"))(define src-dir (or (getenv "F3B_SRC") "src"))(define (rooted n) (path-join root n))(define target (rooted "ref-one.sgd"))(define pre-rev (revision-of-file target))(define good-post (string-replace (read-file-string target) "one site" "one FINISHED site"))(define temp ".folio-staging-ref-one.sgd.corrupt")(define backup ".folio-backup-ref-one.sgd.corrupt")(write-file-string (rooted temp) good-post)(write-file-bytes (rooted backup) (read-file-bytes target))(define post-rev (revision-of-file (rooted temp)))(write-transaction-manifest! root "corrupttest" "probe-op" (list (mentry "ref-one.sgd" pre-rev temp post-rev post-rev backup pre-rev)));; NOW corrupt the temp -- the state recovery cannot prove safe to finish.(write-file-string (rooted temp) "garbage that matches no recorded hash")(define coord (make-coordinator root (make-service (default-worker-argv lib-dir src-dir) default-limits)))(define raised (guard (e (#t (if (exception? e) (exception-message e) "raised"))) (folio-insert-node! coord (rooted "dest.sgd") "incoming" 'update (revision-of-file (rooted "dest.sgd")) timestamp: "2026-08-25" id: "u-corrupt") #f))(cond ((and (string? raised) (string-contains? raised "human") (equal? (revision-of-file target) pre-rev) (file-exists? (rooted ".folio-transaction-manifest"))) (println "CORRUPT_TEMP_REFUSED_TO_HUMAN")) ((string? raised) (println "RAISED_BUT_WRONG_SHAPE: ~a (target-moved=~a manifest-gone=~a)" raised (not (equal? (revision-of-file target) pre-rev)) (not (file-exists? (rooted ".folio-transaction-manifest"))))) (else (println "PROCEEDED_OVER_UNPROVABLE_STATE (target-rev=~a)" (revision-of-file target))))test/f3b-recover-driver.sgladded
;;; f3b-recover-driver.sgl -- run one transaction so the coordinator's;;; automatic recovery executes, and print every recovery action so the gate;;; can assert a self-heal was REPORTED. Used by gate-f3b.sh arm 4.(import (sigil core) (sigil io) (sigil fs) (sigil path) (folio doc revision) (folio doc bounded) (folio doc service) (folio doc coordinator) (folio doc transaction) (folio doc structural))(define root (or (getenv "F3B_ROOT") (begin (println "DRIVER ERROR no root") (exit 3))))(define lib-dir (or (getenv "F3B_LIB") "build/dev/lib"))(define src-dir (or (getenv "F3B_SRC") "src"))(define coord (make-coordinator root (make-service (default-worker-argv lib-dir src-dir) default-limits)))(define r (folio-insert-node! coord (path-join root "dest.sgd") "incoming" 'update (revision-of-file (path-join root "dest.sgd")) timestamp: "2026-08-25" id: "u-recdrv" body-text: "Recovery driver entry."))(cond ((txn-result? r) (for-each (lambda (a) (println "~a" a)) (txn-result-recovery-actions r)) (println "DRIVER_OK")) ((txn-refusal? r) (for-each (lambda (a) (println "~a" a)) (txn-refusal-recovery-actions r)) (println "DRIVER_REFUSED ~a: ~a" (txn-refusal-reason r) (txn-refusal-message r)) (exit 1)))test/f3b-renamer.sgladded
;;; f3b-renamer.sgl -- one real writer process performing the 3-file rename,;;; for gate-f3b.sh's kill arms. Prints RENAMER_READY once the coordinator;;; is built, waits for the start file, runs the rename, prints RENAMER_DONE;;; or the refusal. The gate SIGKILLs it at staggered delays and then proves;;; the all-or-nothing invariant.(import (sigil core) (sigil io) (sigil fs) (sigil string) (sigil time) (sigil path) (folio doc revision) (folio doc bounded) (folio doc service) (folio doc coordinator) (folio doc transaction) (folio doc structural))(define (need name) (or (getenv name) (begin (println "RENAMER ERROR ~a not set" name) (exit 3))))(define root (need "F3B_ROOT"))(define start-file (need "F3B_START_FILE"))(define lib-dir (or (getenv "F3B_LIB") "build/dev/lib"))(define src-dir (or (getenv "F3B_SRC") "src"))(define coord (make-coordinator root (make-service (default-worker-argv lib-dir src-dir) default-limits)))(println "RENAMER_READY")(let wait () (unless (file-exists? start-file) (sleep 0.005) (wait)))(let ((r (folio-rename-id! coord "t-target" "t-retargeted" (revision-of-file (path-join root "owner.sgd"))))) (cond ((sop-result? r) (println "RENAMER_DONE ~a" (sop-result-detail r))) ((txn-refusal? r) (println "RENAMER_REFUSED ~a" (txn-refusal-reason r)))))test/gate-f3b.shadded
#!/bin/sh# gate-f3b.sh -- the F3b gate: structural operations, crash recovery, and# cross-file transactions.## RUN: sh test/gate-f3b.sh (all arms)# SABOTAGE: sh test/sabotage-f3b.sh (proves these arms can go red)## ARMS# 1 harness self-tests: both probe batteries' assertion primitives CAN fail# 2 the F3b battery (test/f3b-probe.sgl, 12 arms: stand-down control first,# both recovery branches with the discriminator, the 3-file rename with# graph closure, referrer-guarded delete, cross-file move with document# re-addressing, scoped-out wording, invalid-doc read-only, body# replacement, the hash-pinned Markdown bystander)# 3 the F3a battery (22 arms) -- the scalar layer must not regress# 4 REAL SIGKILL mid-rename at staggered delays: after recovery, every# file is fully-old or fully-new by content -- NEVER mixed -- and when a# manifest existed at kill time the recovery is REPORTED# 5 a VALIDATED manifest whose staged temp was corrupted after the crash:# recovery REFUSES to a human and touches nothing## SETUP-FAILED is distinct from RED and GREEN throughout.set -urepo_dir=$(cd "$(dirname "$0")/.." && pwd)cd "$repo_dir" || exit 1work=$(mktemp -d /tmp/gate-f3b.XXXXXX) || { echo "GATE-F3B SETUP-FAILED: no temp dir"; exit 3; }trap 'chmod -R u+rwX "$work" 2>/dev/null; rm -rf "$work"' EXIT INT TERMsigil="${SIGIL:-sigil}"export SIGIL_REDIRECTS="${SIGIL_F3B_REDIRECTS:-$repo_dir/dev-redirects.sgl}"F3A_FIX="$repo_dir/test/fixtures/f3a"F3B_FIX="$repo_dir/test/fixtures/f3b"fails=0fail () { echo "GATE-F3B FAIL: $*" >&2; fails=$((fails + 1)); }setup_failed () { echo "GATE-F3B SETUP-FAILED: $*" >&2; exit 3; }prev_fails=0arm_summary () { if [ "$fails" -gt "$prev_fails" ]; then echo "GATE-F3B arm $1: FAILED ($((fails - prev_fails)) failure(s))" else echo "GATE-F3B arm $1: OK" fi prev_fails=$fails}only_arms="${*:-1 2 3 4 5}"want () { case " $only_arms " in *" $1 "*) return 0;; *) return 1;; esac; }fresh_f3b_root () { mkdir -p "$1" && cp "$F3B_FIX"/* "$1"/ || setup_failed "cannot populate f3b root"; }echo "GATE-F3B: building (dev config)...""$sigil" build >"$work/build.log" 2>&1 || setup_failed "sigil build failed"[ -d build/dev/lib ] || setup_failed "build/dev/lib absent after build"run_sgl () { "$sigil" -L build/dev/lib -L src "$@"; }# Sequential cache warm-up before any concurrent spawn (the F3a lesson).warm="$work/warm"; fresh_f3b_root "$warm"F3B_PROBE_SELFTEST=1 F3B_ROOT="$warm" run_sgl test/f3b-probe.sgl >/dev/null 2>&1 || true# ---------------------------------------------------------------------------if want 1; then r1="$work/st1"; fresh_f3b_root "$r1" F3B_PROBE_SELFTEST=1 F3B_ROOT="$r1" run_sgl test/f3b-probe.sgl >"$work/st-b.log" 2>&1 \ && grep -q "F3B_ARM_selftest_RED" "$work/st-b.log" \ || fail "arm1: the f3b harness did not record a red for a false assertion" r1a="$work/st1a"; mkdir -p "$r1a"; cp "$F3A_FIX"/*.sgd "$r1a"/ F3A_PROBE_SELFTEST=1 F3A_ROOT="$r1a" run_sgl test/f3a-txn-probe.sgl >"$work/st-a.log" 2>&1 \ && grep -q "F3A_ARM_selftest_RED" "$work/st-a.log" \ || fail "arm1: the f3a harness did not record a red for a false assertion" arm_summary 1fi# ---------------------------------------------------------------------------if want 2; then r2="$work/b"; fresh_f3b_root "$r2" F3B_ROOT="$r2" run_sgl test/f3b-probe.sgl >"$work/b.log" 2>&1 || fail "arm2: battery exited non-zero" grep -q "F3B_PROBE_DONE arms=12 green=12 red=0" "$work/b.log" \ || { fail "arm2: expected 12/12; got $(grep F3B_PROBE_DONE "$work/b.log" || echo none)"; \ grep "_RED" "$work/b.log" >&2 || true; } arm_summary 2fi# ---------------------------------------------------------------------------if want 3; then r3="$work/a"; mkdir -p "$r3"; cp "$F3A_FIX"/*.sgd "$r3"/ F3A_ROOT="$r3" run_sgl test/f3a-txn-probe.sgl >"$work/a.log" 2>&1 || fail "arm3: f3a battery exited non-zero" grep -q "F3A_PROBE_DONE arms=22 green=22 red=0" "$work/a.log" \ || { fail "arm3: expected 22/22; got $(grep F3A_PROBE_DONE "$work/a.log" || echo none)"; \ grep "_RED" "$work/a.log" >&2 || true; } arm_summary 3fi# ---------------------------------------------------------------------------# Arm 4: SIGKILL mid-rename. Coherence contract per file after recovery:# fully-old (has t-target, no t-retargeted) or fully-new (the reverse), and# ALL FILES AGREE. A file with both, or files disagreeing, is a torn write.# ---------------------------------------------------------------------------if want 4; then for delay in 0.05 0.20 0.60 1.20; do rk="$work/kill-$delay"; fresh_f3b_root "$rk" startk="$work/ks-$delay"; rm -f "$startk" F3B_ROOT="$rk" F3B_START_FILE="$startk" \ "$sigil" -L build/dev/lib -L src test/f3b-renamer.sgl >"$work/rn-$delay.log" 2>&1 & rn=$! tries=0 while ! grep -q RENAMER_READY "$work/rn-$delay.log" 2>/dev/null; do tries=$((tries + 1)); [ "$tries" -gt 600 ] && break; sleep 0.1 done grep -q RENAMER_READY "$work/rn-$delay.log" || setup_failed "arm4: renamer never became ready" : > "$startk" sleep "$delay" kill -9 "$rn" 2>/dev/null wait "$rn" 2>/dev/null manifest_at_kill=no [ -f "$rk/.folio-transaction-manifest" ] && manifest_at_kill=yes # Recovery driver: any next transaction (its log carries the report). F3B_ROOT="$rk" run_sgl test/f3b-recover-driver.sgl >"$work/rec-$delay.log" 2>&1 \ || fail "arm4 delay=$delay: the post-kill transaction failed" old_files=0; new_files=0; torn=0 for f in owner.sgd ref-one.sgd ref-two.sgd; do has_old=no; has_new=no grep -q '"t-target"' "$rk/$f" && has_old=yes grep -q '"t-retargeted"' "$rk/$f" && has_new=yes if [ "$has_old" = yes ] && [ "$has_new" = yes ]; then torn=$((torn+1)); fi [ "$has_old" = yes ] && old_files=$((old_files+1)) [ "$has_new" = yes ] && new_files=$((new_files+1)) done [ "$torn" -eq 0 ] || fail "arm4 delay=$delay: a file carries BOTH ids -- torn" if [ "$old_files" -gt 0 ] && [ "$new_files" -gt 0 ]; then fail "arm4 delay=$delay: files DISAGREE (old=$old_files new=$new_files) -- partial rewrite survived recovery" fi if [ "$manifest_at_kill" = yes ]; then grep -q "recovery:" "$work/rec-$delay.log" \ || fail "arm4 delay=$delay: a manifest existed at kill time but no recovery was reported" [ "$new_files" -eq 3 ] \ || fail "arm4 delay=$delay: manifest existed (validated) but recovery did not complete to the new state" fi [ -f "$rk/.folio-transaction-manifest" ] && fail "arm4 delay=$delay: manifest survived recovery" echo "GATE-F3B arm4 delay=$delay: manifest_at_kill=$manifest_at_kill old=$old_files new=$new_files torn=$torn" done # Best-effort MANIFEST-WINDOW round: busy-poll for the manifest's atomic # appearance and kill the instant it exists. The window (renames + # cleanup) is milliseconds wide, so catching it is not guaranteed and NO # assertion here requires it -- the coherence contract holds either way, # and when the window IS caught this round exercises real-kill completion # (the deterministic completion coverage is the battery's b3 arm, which # constructs the identical bytes). The round REPORTS which case ran. rkw="$work/kill-window"; fresh_f3b_root "$rkw" startw="$work/ks-window"; rm -f "$startw" F3B_ROOT="$rkw" F3B_START_FILE="$startw" \ "$sigil" -L build/dev/lib -L src test/f3b-renamer.sgl >"$work/rn-window.log" 2>&1 & rnw=$! tries=0 while ! grep -q RENAMER_READY "$work/rn-window.log" 2>/dev/null; do tries=$((tries + 1)); [ "$tries" -gt 600 ] && break; sleep 0.1 done : > "$startw" while kill -0 "$rnw" 2>/dev/null && [ ! -f "$rkw/.folio-transaction-manifest" ]; do :; done kill -9 "$rnw" 2>/dev/null wait "$rnw" 2>/dev/null window_hit=no [ -f "$rkw/.folio-transaction-manifest" ] && window_hit=yes F3B_ROOT="$rkw" run_sgl test/f3b-recover-driver.sgl >"$work/rec-window.log" 2>&1 \ || fail "arm4 window: the post-kill transaction failed" wold=0; wnew=0; wtorn=0 for f in owner.sgd ref-one.sgd ref-two.sgd; do ho=no; hn=no grep -q '"t-target"' "$rkw/$f" && ho=yes grep -q '"t-retargeted"' "$rkw/$f" && hn=yes [ "$ho" = yes ] && [ "$hn" = yes ] && wtorn=$((wtorn+1)) [ "$ho" = yes ] && wold=$((wold+1)) [ "$hn" = yes ] && wnew=$((wnew+1)) done [ "$wtorn" -eq 0 ] || fail "arm4 window: torn file" if [ "$wold" -gt 0 ] && [ "$wnew" -gt 0 ]; then fail "arm4 window: files disagree (old=$wold new=$wnew) after recovery" fi if [ "$window_hit" = yes ]; then [ "$wnew" -eq 3 ] || fail "arm4 window: manifest present at kill but recovery did not complete" grep -q "recovery:" "$work/rec-window.log" || fail "arm4 window: unreported recovery" fi echo "GATE-F3B arm4 window: manifest_at_kill=$window_hit old=$wold new=$wnew torn=$wtorn" arm_summary 4fi# ---------------------------------------------------------------------------# Arm 5: a validated manifest whose temp was corrupted post-crash: recovery# REFUSES to a human, the target does not move, the manifest stays for the# human. (This is the unprovable state; guessing here is the defect.)# ---------------------------------------------------------------------------if want 5; then r5="$work/corrupt"; fresh_f3b_root "$r5" F3B_ROOT="$r5" run_sgl test/f3b-corrupt-temp-driver.sgl >"$work/corrupt.log" 2>&1 grep -q "CORRUPT_TEMP_REFUSED_TO_HUMAN" "$work/corrupt.log" \ || { fail "arm5: recovery did not refuse the unprovable state; log:"; tail -5 "$work/corrupt.log" >&2; } arm_summary 5fiecho "---"if [ "$fails" -eq 0 ]; then echo "F3B_GATE_GREEN arms_run=[$only_arms] f3b_arms=12 f3a_arms=22 kill_rounds=5 sha=$(git -C "$repo_dir" rev-parse --short HEAD 2>/dev/null || echo unknown)" exit 0else echo "F3B_GATE_RED failures=$fails arms_run=[$only_arms]" exit 1fitest/sabotage-f3b.shadded
#!/bin/sh# sabotage-f3b.sh -- proves gate-f3b.sh's arms can go RED. Product-layer# plants with the F3a discipline: prediction printed first (red AND green# sets), plant landed by hash + marker, planted tree must build, restores by# git checkout verified by hash, full gate re-run green at the end.## Legs:# T1 raw-vs-semantic ACCOUNTING tolerated -> the graph-closure check# catches the stale referrer: b5-rename red (closure signature)# T2 T1 + closure check disabled -> the stale referrer PUBLISHES:# b5-rename red on its own content asserts (BOTH layers had to fall)# T3 delete ignores referrers -> b7-delete red# T4 recovery goes silent (recover-root! returns '() doing nothing) -># b3-recovery-completes red (completion never happens, manifest stays)# T5 recovery TOLERATES an unprovable state (the first-pass proof clause# always passes) -> gate arm 5 red (the corrupt-temp driver observes# recovery proceeding over garbage instead of refusing to a human)# TA assertion layer: flip the bystander hash expectation -> b12 redset -urepo_dir=$(cd "$(dirname "$0")/.." && pwd)cd "$repo_dir" || exit 1work=$(mktemp -d /tmp/sabotage-f3b.XXXXXX) || { echo "SAB-F3B SETUP-FAILED: no temp dir"; exit 3; }trap 'chmod -R u+rwX "$work" 2>/dev/null; rm -rf "$work"' EXIT INT TERMsigil="${SIGIL:-sigil}"export SIGIL_REDIRECTS="${SIGIL_F3B_REDIRECTS:-$repo_dir/dev-redirects.sgl}"F3B_FIX="$repo_dir/test/fixtures/f3b"ST=src/folio/doc/structural.sglMAN=src/folio/doc/manifest.sglPROBE=test/f3b-probe.sgllegs=0; reds=0; setup_fails=0fail_leg () { echo "SAB-F3B LEG $1: DID NOT GO RED AS PREDICTED -- $2" >&2; }setup_failed_leg () { echo "SAB-F3B LEG $1: SETUP-FAILED -- $2" >&2; setup_fails=$((setup_fails + 1)); }hash_of () { sha256sum "$1" | cut -d' ' -f1; }h_st=$(hash_of "$ST"); h_man=$(hash_of "$MAN"); h_probe=$(hash_of "$PROBE")restore_all () { git checkout -- "$ST" "$MAN" "$PROBE" || return 1 [ "$(hash_of "$ST")" = "$h_st" ] || { echo "SAB-F3B RESTORE FAILED: $ST" >&2; return 1; } [ "$(hash_of "$MAN")" = "$h_man" ] || { echo "SAB-F3B RESTORE FAILED: $MAN" >&2; return 1; } [ "$(hash_of "$PROBE")" = "$h_probe" ] || { echo "SAB-F3B RESTORE FAILED: $PROBE" >&2; return 1; } return 0}git diff --quiet -- "$ST" "$MAN" "$PROBE" \ || { echo "SAB-F3B SETUP-FAILED: dirty tree under the plant files"; exit 3; }plant_landed () { leg=$1; file=$2; old=$3; marker=$4; want=$5 [ "$(hash_of "$file")" != "$old" ] || { setup_failed_leg "$leg" "no diff in $file"; return 1; } got=$(grep -c "$marker" "$file" || true) [ "$got" -eq "$want" ] || { setup_failed_leg "$leg" "marker '$marker' count $got, wanted $want"; return 1; } return 0}build_planted () { if ! "$sigil" build >"$work/build-$1.log" 2>&1; then setup_failed_leg "$1" "planted tree does not build"; return 1 fi return 0}run_battery () { rb="$work/root-$1"; mkdir -p "$rb"; cp "$F3B_FIX"/* "$rb"/ F3B_ROOT="$rb" "$sigil" -L build/dev/lib -L src "$PROBE" >"$work/bat-$1.log" 2>&1 return $?}expect () { leg=$1; arm=$2; want=$3 grep -q "F3B_ARM_${arm}_${want}" "$work/bat-$leg.log" && return 0 fail_leg "$leg" "predicted $arm=$want; got: $(grep "F3B_ARM_${arm}_" "$work/bat-$leg.log" | head -1 || echo 'no marker')" return 1}leg_result () { legs=$((legs + 1)) [ "$2" -eq 0 ] && { reds=$((reds + 1)); echo "SAB-F3B LEG $1: RED AS PREDICTED"; }}# ---------------------------------------------------------------------------echo "SAB-F3B LEG T1: raw-vs-semantic accounting tolerated"echo " PREDICT RED: b5-rename -- via the GRAPH CLOSURE check (the dropped"echo " accounting lets a semantically-invisible occurrence"echo " through, closure sees the residue and refuses)"echo " PREDICT GREEN: b1, b7 (delete's guard reads semantic+raw directly)"sed -i 's/(if (= accounted (length hits))/(if #t ;; SAB-T1/' src/folio/doc/structural.sglif plant_landed T1 "$ST" "$h_st" "SAB-T1" 1 && build_planted T1; then run_battery T1 || true ok=0 expect T1 b5-rename RED || ok=1 grep -q "closure" "$work/bat-T1.log" || { fail_leg T1 "red, but not with the closure signature"; ok=1; } expect T1 b1-stand-down GREEN || ok=1 expect T1 b7-delete GREEN || ok=1 leg_result T1 $okfirestore_all || exit 3# ---------------------------------------------------------------------------echo "SAB-F3B LEG T2: accounting tolerated AND closure disabled -- the stale"echo " referrer PUBLISHES; b5's content asserts catch it (both layers fell)"echo " PREDICT RED: b5-rename (residue assert); b6 may cascade -- only b5 is"echo " asserted"sed -i 's/(if (= accounted (length hits))/(if #t ;; SAB-T1/' src/folio/doc/structural.sglsed -i 's/(if (pair? leak)/(if #f ;; SAB-T2/' src/folio/doc/structural.sglif plant_landed T2 "$ST" "$h_st" "SAB-T2" 1 && build_planted T2; then run_battery T2 || true ok=0 expect T2 b5-rename RED || ok=1 leg_result T2 $okfirestore_all || exit 3# ---------------------------------------------------------------------------echo "SAB-F3B LEG T3: delete ignores referrers"echo " PREDICT RED: b7-delete (a referenced delete publishes; the arm"echo " expects the refusal)"echo " PREDICT GREEN: b1, b5"sed -i 's/((or (pair? semantic) (pair? raw-only))/(#f ;; SAB-T3/' src/folio/doc/structural.sglif plant_landed T3 "$ST" "$h_st" "SAB-T3" 1 && build_planted T3; then run_battery T3 || true ok=0 expect T3 b7-delete RED || ok=1 expect T3 b1-stand-down GREEN || ok=1 expect T3 b5-rename GREEN || ok=1 leg_result T3 $okfirestore_all || exit 3# ---------------------------------------------------------------------------echo "SAB-F3B LEG T4: recovery goes silent (does nothing, reports nothing)"echo " PREDICT RED: b3-recovery-completes (no completion, no report, the"echo " manifest survives into the arm's asserts)"echo " PREDICT GREEN: b1 (no crash state on its path)"sed -i 's/(if (file-exists? mpath)/(if #f ;; SAB-T4/' src/folio/doc/manifest.sglif plant_landed T4 "$MAN" "$h_man" "SAB-T4" 1 && build_planted T4; then run_battery T4 || true ok=0 expect T4 b3-recovery-completes RED || ok=1 expect T4 b1-stand-down GREEN || ok=1 leg_result T4 $okfirestore_all || exit 3# ---------------------------------------------------------------------------echo "SAB-F3B LEG T5: recovery TOLERATES the unprovable (the first-pass proof"echo " always passes) -- gate arm 5's driver must observe recovery proceeding"echo " over a corrupt temp instead of refusing to a human"sed -i 's/(else$/(#f ;; SAB-T5/' src/folio/doc/manifest.sglif ! grep -q "SAB-T5" src/folio/doc/manifest.sgl; then setup_failed_leg T5 "plant did not land (anchor moved)"else # The blunt anchor above may hit several (else lines; use git to re-verify # only ONE landed where intended, else refuse the leg. count=$(grep -c "SAB-T5" src/folio/doc/manifest.sgl) if [ "$count" -ne 1 ]; then setup_failed_leg T5 "anchor matched $count sites; the plant is not the plant" restore_all || exit 3 else if build_planted T5; then r5="$work/root-T5"; mkdir -p "$r5"; cp "$F3B_FIX"/* "$r5"/ F3B_ROOT="$r5" "$sigil" -L build/dev/lib -L src test/f3b-corrupt-temp-driver.sgl >"$work/t5.log" 2>&1 ok=0 if grep -q "CORRUPT_TEMP_REFUSED_TO_HUMAN" "$work/t5.log"; then fail_leg T5 "the driver still saw a refusal with the proof clause disabled"; ok=1 else grep -qE "PROCEEDED_OVER_UNPROVABLE_STATE|RAISED_BUT_WRONG_SHAPE" "$work/t5.log" \ || { fail_leg T5 "no recognisable outcome: $(tail -2 "$work/t5.log")"; ok=1; } fi leg_result T5 $ok fi fifirestore_all || exit 3# ---------------------------------------------------------------------------echo "SAB-F3B LEG TA: assertion layer -- flip the bystander hash expectation"echo " PREDICT RED: b12-bystander"sed -i 's/(equal? (revision-of-file (rooted "bystander.md")) bystander-hash)/(not (equal? (revision-of-file (rooted "bystander.md")) bystander-hash)) ;; SAB-TA/' test/f3b-probe.sglif plant_landed TA "$PROBE" "$h_probe" "SAB-TA" 1; then run_battery TA || true ok=0 expect TA b12-bystander RED || ok=1 leg_result TA $okfirestore_all || exit 3# ---------------------------------------------------------------------------echo "SAB-F3B: rebuilding restored tree and re-running the FULL gate""$sigil" build >"$work/build-final.log" 2>&1 || { echo "SAB-F3B SETUP-FAILED: restored tree does not build"; exit 3; }if sh test/gate-f3b.sh >"$work/gate-final.log" 2>&1; then echo "SAB-F3B: restored gate GREEN"else echo "SAB-F3B FAILED: gate RED on the RESTORED tree"; tail -5 "$work/gate-final.log"; exit 1fiecho "---"echo "F3B_SABOTAGE legs=$legs red=$reds setup_failed=$setup_fails"[ "$legs" -eq 6 ] && [ "$reds" -eq 6 ] && [ "$setup_fails" -eq 0 ] && exit 0exit 1