cartridge substrate item 5: L1 wired over L3 — abi table, host trait, rustlite extern/pub/if/intrinsics, cartridge lifecycle - #16
Merged
Conversation
… rustlite extern/pub/if/intrinsics, cartridge lifecycle
a cartridge can now receive a message and answer it. ONE ABI table
(src/cartridges/abi.rs) is read by the compiler, the runtime, and the
lifecycle alike — HostFn ×5 (log/now_ms/store_get/store_set/emit),
GuestFn ×3 (cart_init/cart_handle/cart_alloc), pack/unpack for the
`ptr<<32|len` result, and the `Host` trait, which takes BYTES only.
- rustlite: `extern "C" { fn …; }` imports (only "C"; string tokens exist
for this one purpose and are refused as expressions with the workaround
named), `pub fn` = export, `if / else / else if` (else-if desugars to a
nested If), eight inline intrinsics (load_u8 store_u8 load_i32
store_i32 memory_size pack unpack_ptr unpack_len — rustlite has no
pointers, casts, or shifts by design). `Program {externs, fns}`.
every-path-returns is now a checker rule that names the fix.
- wasm.rs: import/memory/export sections in spec order; a fixed 16-page
memory exported as "memory" whenever the module imports, exports, or
touches memory; function index space = imports then defined (the
runtime applies the identical rule). check_program = the module-level
contract: unique names, intrinsics reserved, externs vs the ABI table,
lifecycle exports vs theirs, lifecycle entry points must be pub.
- runtime.rs: decodes the new sections (custom skipped; every OTHER
unknown section now REFUSED — silently dropping a data segment is the
D4 failure in binary form); if/else through the same unresolved-site
patching as blocks and loops; memory ops with u64 effective-address
math; Trap::{MemoryOutOfBounds, CallDepthExceeded, Unreachable};
MAX_CALL_DEPTH 1024; host dispatch that bounds-checks and COPIES every
(ptr, len) before the host sees it; guest_alloc = the host→guest hop
for store_get, bounded at MAX_HOST_REENTRY 4.
- lifecycle.rs: Cartridge::load refuses at the door (manifest, unknown
import, import shape, missing or mis-shaped lifecycle export, no
exported memory — each naming the fix); init (nonzero = Refused(code),
verbatim); handle (packed answer bounds-checked, bytes copied out).
store_set refusal = status 1 to the guest; emit failure = a trap.
three real bugs the new evals caught before the first commit:
1. void calls were unwritable as statements — `log(…);` was refused as
"used as a value". check_call now serves both positions.
2. item 4's decoder underflowed `r.pos - body_end` on a body that ended
SHORT of its declared size. the pure-module fuzz never produced that
shape; the cartridge-shaped fuzz did on its first run.
3. every-path-returns bodies failed wasmparser (empty stack at the
function's end after an if/else whose arms both return). a trailing
`unreachable` closes them; the runtime names it as a trap, and a
compiled test proves it never executes.
Cargo.lock: stale since PR #14 added wasmparser (the browser agent cannot
run cargo); refreshed.
tests: tests/cartridge_lifecycle.rs (new, 14) — an echo cartridge in
rustlite through the REAL pipeline against a recording fake host, every
host fn observed through guest memory both ways, every refusal named,
the corruption fuzz across the host boundary; runtime_evals +12,
rustlite_emit +7, rustlite_front +3. taskboard/status/plan updated
(item 4 marked done — PR #15's board edit was one item behind — item 5
done, item 6 next; string literals / data segments flagged as the
language gap to close before item 8).
verification: the shared gate run natively — every tests/*.rs suite
green (217 tests), clippy --deny warnings clean, cargo check --target
wasm32-unknown-unknown --lib --bins clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`a_failed_delete_keeps_its_index_entry` searches src/agent/tools.rs for
a literal `{\n` shape. a windows checkout with core.autocrlf=true serves
the same bytes as `{\r\n`, so the guard went red on a tree ci passes — a
false red that blocks running the shared gate locally at all. the
source() helper now normalizes line endings before any guard looks.
verification: boot_reconcile 7/7 on a CRLF checkout; a no-op on LF.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Owner
Author
|
live boot verification (the assertion
|
compusophy
added a commit
that referenced
this pull request
Aug 25, 2026
… secret (#17) smoke-preview has been red on every pr since it landed (#12–#16). the cause was never the app: preview deployments sit behind vercel deployment protection, the preview url 302s to vercel.com/sso-api, and ci/e2e.mjs's interstitial markers did not match the "Login – Vercel" page it landed on — so it reported "#feed never appeared — the app html did not mount", the exact D4 misdiagnosis its own header says it exists to prevent. and main carries no branch protection, so the red check never blocked a merge; the board's "merge_pr refuses until booted-green" was never true in practice. - ci/e2e.mjs: the protection check now fires on a redirect off the preview host onto vercel.com AND on the login page's text, exits 3 with the fix named (bypass secret or relaxed preview protection), and says explicitly whether a secret was supplied. with VERCEL_AUTOMATION_BYPASS_SECRET set, every request from the browser context carries x-vercel-protection-bypass (+ set-bypass-cookie), so index.html, worker.js, and the wasm all pass the wall. - .github/workflows/e2e.yml: passes the secret through when it exists. - memory: the finding, the owner action (generate the bypass secret in vercel, add it as the repo secret), and the lesson — a gate nobody has seen pass is not a gate; witness one green and one red. verification: node --check on the script; tests/ci_gate.rs guards still green. the pr for this branch is ITSELF the red-side witness: its smoke-preview must now fail with "EVIDENT-CAUSE FAILURE … redirected to https://vercel.com/sso-api" instead of the false "did not mount". the green side needs the owner's secret. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CARTRIDGE_PLAN §11 item 5: the L1 ABI wired over the L3 runtime. A cartridge written in rustlite can now be loaded, initialized, and handed messages, talking to the host through the ABI.
what landed
src/cartridges/abi.rs(new) — the ONE ABI table:HostFn×5,GuestFn×3,pack/unpack, theHosttrait (bytes only, never pointers). Compiler, runtime, and lifecycle all read it, so a signature cannot drift between guest and host.extern "C" { … }imports,pub fnexports,if / else / else if, eight inline intrinsics (load_u8 store_u8 load_i32 store_i32 memory_size pack unpack_ptr unpack_len),Program {externs, fns}, an every-path-returns checker rule.check_programfor the module-level contract (unique names, reserved intrinsics, externs vs the ABI, lifecycle exports vs the ABI).if/elsevia the existing branch-patching discipline, memory ops,Trap::{MemoryOutOfBounds, CallDepthExceeded, Unreachable},MAX_CALL_DEPTH, host dispatch with bounds-checked copies, bounded host→guest re-entry forstore_get.src/cartridges/lifecycle.rs(new) —Cartridge::load→init→handle, every refusal at the door named.three real bugs the new evals caught
log(…);refused by the checker).r.pos - body_endon a short body — found by the cartridge-shaped fuzz on its first run.unreachablethat the runtime names as a trap.Also:
Cargo.lockrefreshed (stale since #14 added wasmparser), andboot_reconcile's source-shape guard made CRLF-tolerant (false red on Windows checkouts).verification
tests/cartridge_lifecycle.rs(new, 14): echo cartridge through the real pipeline against a recording fake host; every host fn, every refusal, corruption fuzz across the host boundary.runtime_evals+12,rustlite_emit+7 (cartridge validates under wasmparser),rustlite_front+3.--deny warningsclean,cargo check --target wasm32-unknown-unknown --lib --binsclean.Memory updated on this branch before opening (taskboard: item 4 done, item 5 done, item 6 next; string literals / data segments flagged before item 8).
🤖 Generated with Claude Code