Beef residual coverage at the softcode JIT / Lua JIT boundary: production
nest shapes, host setq/attr survival, multi-entry lua(), mux.eval effects,
and float/nil/bool marshal consumers. Expand tests/luajit NESTED under
brackets-on and add STATE e4 so mux.eval cannot re-run silently.
Softcode CALL_FUNC is already total (guest error strings, never
ECALL_DECLINE). The residual poison was mid-run DBT failure (code buffer
full, harvest miss) returning false → full AST re-run after effects.
Track eval_ctx::host_ecalls on softcode host ECALLs; after entry, commit
#-1 JIT POST-ENTRY FAIL instead of re-running. Document the audit in
plan-softcode-post-entry-contract.md and cross-link the Lua plan.
#1764 shape 2: default STRING-claim calls no longer marshal to TY_STRING
text immediately. ECALL_LUA_CALL_VAL leaves the first pcall result on the
Lua stack and returns a TY_LUA_HANDLE. Softcode boundary marshals via
ECALL_LUA_MARSHAL (fun_lua rules). TEST/NOT use ECALL_LUA_TOBOOL so
tostring(0)/"" / string.find stay truthy under Lua semantics.
CALL_INT / CALL_VOID unchanged. Equality of a value handle to a string
constant still declines (needs VM compare).
AGREE_DECLINE 16→12: the four #1764 if/not pins now execute.
The EQK nil fix is right, and its lesson generalizes further than it was
applied. Lua's == is false across TYPES, and HIR erases every
distinction that decides it: false and 0 are the same ICONST, nil and ""
the same empty SCONST, and the numeric path coerces "5" to 5. Measured
on the PR as submitted -- all executing, all silently wrong:
local a=0 if a == false jit "y" interp "n"
local a=1 if a == true jit "y" interp "n"
local a=false if a == 0 jit "y" interp "n"
local a="5" if a == 5 jit "y" interp "n"
local a=5 if a == "5" jit "y" interp "n"
Generalizes the author's nil branch into a type-class test
(lua_type_class_of_value / _of_const, from the truth tag plus the HIR
type): mismatched classes are constant false whatever the
representations say; nil == nil stays true.
EQI needed the same treatment and is now its own case rather than
sharing CMP_RI: `a == 0` lowers to EQI and `a == ""` to EQK, so fixing
either alone leaves the other wrong -- the opcode-twin trap this PR's
own doc note names, one level deeper. Order comparisons keep declining
instead: Lua RAISES on mismatched types there, so answering false would
be its own wrong answer.
Five AGREE pins across both opcode forms; all EXECUTE and answer like
the interpreter. Plan gains the rule as item 5.
Also records the softcode no-post-entry-decline assumption that #1767's
exactly-once argument rests on -- derived today from the #1002 pre-entry
watermarks plus Phase 4 leaving one defensive ECALL_DECLINE that nothing
emits into, and therefore worth writing where it can be checked.
make test exit 0; smoke 1561/0; luajit PASSED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#1766's consumer audit covered CMP_RR (return t[2]=="") but not EQK
(if t[2]=="" then). nil is the empty SCONST in HIR; EQK's STRCMP path
made nil == "" true compiled while the interpreter answers false.
Resolve EQK against the pool constant type and the lhs NIL tag before
STRCMP: nil==nil is true, nil vs anything else (including "") is false.
Tag pool TNIL/TFALSE/TTRUE on LOADK/LOADKX and on EQK's constant side.
Also refuse GTI/GEI on nil (order of nil).
Three EXEC-friendly AGREE pins for the if-EQK shapes. Document the
"new value class ⇒ consumer audit" lesson (including opcode twins) in
the post-entry plan.
Phase 4 deleted silent re-run, so #1750's effect-free corridor is obsolete:
bridge notify/pemit/set/eval run on both routes under the same permissions.
Remove EFFECT_REFUSED and compile-time effectful ineligibility.
STATE e2 delivers PING once on both legs. POST_ENTRY_LOUD 1→0;
AGREE_DECLINE 10→8 (mux.eval executes).
Docs: rewrite plan-lua-post-entry-contract as campaign-complete + residual
design goals; product plan points residual quality at §3.
File plan-lua-post-entry-contract.md as the multi-agent source of truth
for moving Lua from silent decline-then-rerun to softcode's eligibility
decides / run commits model. Point plan-lua-jit-product.md at it so
default-on is not read as architecture done.