Round-8 review (Claude, register key `lift-guard-regex-both-directions`).
Every class reproduced here before fixing, and re-tested after.
THREE FALSE NEGATIVES — the payload reads a name and the guard stayed silent,
which is the direction that costs something, because silence is what the tool
exists to prevent:
echo $((X + 1)) arithmetic expansion reads X without a `$` before the
(( Y > 0 )) name, and the read pattern cannot match it: the
character after `$` is `(`. Both contexts are now
tokenised. `if [ $((inm + ins)) -eq 0 ]` is already live
at check.sh:464 — not lifted today, so latent, not absent.
n=Q; ${!n} indirect expansion defeats text analysis outright. The
guard now REFUSES the lift rather than passing it. Its
contract is "does not miss a dependency"; where it cannot
honour that it must say so, not shrug.
SIX FALSE POSITIVES — the driver defines the name and the guard cried wolf.
This direction matters too: a guard that raises false alarms gets edited away,
and then it guards nothing.
case x in a) FOO=1 ;; `)` added to the assignment delimiters
if …; else FOO=1; fi `else` added
! FOO=1 `!` added
mapfile -t FOO binds a name with no `=` at all
readarray -t FOO likewise
printf -v FOO "x" likewise
The banner also over-claimed. It read as a completeness statement about
LIFTING; it is a completeness statement about VARIABLES. A lifted payload also
inherits functions, shell options, traps and a working directory, and this tool
models none of them — loud failures under `set -e`, but the header now says so
rather than implying otherwise.
Verified: all nine classes behave correctly, a genuine missing variable is
still caught by name, and the seven lifting self-tests pass in dalek plus the
four fast ones in each ported fork.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round-9 swept all ten instruments per fork instead of only the two buttons,
and found selftest-axgate.sh red in all four. Root cause, identical to the
KERNLOG defect fixed the day before: a self-test lifts one phase out of
check.sh and supplies its variables from a HAND-WRITTEN preamble, and the
phase later grew a dependency nobody told the preamble about.
Phase 2b stopped globbing Proofs/*.olean and started reading $PROOFS by
membership. Bash does not error on an unset array under `set -u` — it
expands to nothing — so the gate silently received
expected := [".olean"]
one entry with an empty name, and rejected the baseline via its own
fail-closed absence check. Exit code right, reason wrong.
· lift-guard.sh (new, pinned): derives every variable the lifted payload
reads, subtracts what the driver defines, and fails AT LIFT TIME naming
the remainder. Wired into all five lifting self-tests; selftest-shapes.sh
drops its inline copy. One implementation, not five, since drifting out
of sync is the entire failure mode.
· selftest-axgate.sh: lifts PROOFS verbatim, and its case 2 now poisons a
MANIFESTED leaf module rather than adding a stray file. The stray-file
attack was a no-op against membership semantics — that is the dead-file
gate's job, proven by selftest-harness case 8 — and it is also the
weaker attack, since adding files to Proofs/ must clear a gate that
editing an already-manifested module does not.
· Phase 2c's accounting identity moves behind its own marker with its own
ACCTFAIL verdict, so the phase is liftable by construction rather than
by a self-test knowing where to stop. Truncating the lift range alone
would have dropped Phase 2c's own verdict and left the test unable to fail.
· The other four lifting self-tests assert on the PAYLOAD rather than the
assembled driver, so a marker in the preamble cannot satisfy a check
meant to prove the lift landed.
Certified: 8/8 self-tests green in all four forks (~212 assertions, none
failing); check.sh bytes unchanged and still matching the pin written before
the 3h08m round-9 sweep, whose 36 GREEN button rows therefore stand.
--audit-only re-run against the new HARNESS.sha256 in every fork.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>