* feat: add host-callback channels to library mode
- the profile's `callbacks` array declares named channels and
`abi.callback_register_symbol` names the one registration entry point:
`int32_t <sym>(const char *name, void (*fn)(void), void *ctx)` — 0 on
success, -1 for an unknown or NULL name, latest registration wins, a
NULL fn clears, registrations persist across init/reset. The stored
pointer's typed shape is the channel's with the opaque context first,
the panic sink's layout
- channel signatures ride the existing marshalling classes: params are
f64/bool/string/bytes plus the u8/u32/i32 plumbing classes (outbound
plumbing keeps JS's ToUint32/ToInt32, the executable FFI rule);
returns are scalar only (f64/bool/u8/u32/i32/void — a buffer return
needs an ownership contract the mode does not define). string/bytes
parameters arrive as (ptr, len) pairs borrowed for the call's duration
- compiled code reaches a channel as a signature-only ambient function
declaration whose direct calls deliver synchronously on the calling
thread; the recognition rides the FFI import machinery under a library
flavor: SC4024 refuses a call the channels cannot serve (undeclared
name or off-class signature), unused channels are legal capacity, and
callback-free profiles keep the ambient ReferenceError semantics
- calling an unregistered channel is the SC4025 runtime trap: a detected
trap through the library funnel, so the structured sink message names
the channel in its text and the entry the host called in its symbol
field, and profile teachings/remediations overlay it like the rest of
the runtime family
- registration slots are per copy of the runtime state, the sink's story:
per-archive under abi.localize_runtime (the register symbol joins the
localization keep-list), per-thread instance under
abi.instance_per_thread. The host callback must not reenter any library
entry or unwind across library frames
- both emissions produce identical behavior by construction: the slot
store/fetch lives in scr_library.c (scr_library_cb_set/_require/_ctx),
the generated TU emits the registration dispatch and typed indirect
calls, and the trap text is assembled once at export resolution
- callback-free profiles emit byte-identical program TUs and serialized
IR, and executable builds are byte-identical end to end
- conformance: the CB suite (tests/harness/library-callbacks.test.ts)
covers the acceptance stream, unregistered and pre-registration traps,
symbol exactness, teaching overlays, SC4024 refusals, capacity and
callback-free postures, the localized+thread-instanced composition
probe, and ASan reruns; profile-shape refusals join
library-profile.test.ts
* fix: preserve callback binding identity
* fix: harden library callback contracts
* fix: recognize project callback declarations
* fix: enforce host callback declarations
- The library trap funnel (scr_library.c) now assembles every detected trap into the ratified 0x01 text 0x1F code 0x1F symbol [0x1F remediation] form: the baseline human line rides unchanged as field 0, and 0x01-led messages (facade-authored throws, the compile-time SC4012 contract trap) still pass verbatim.
- Trap kinds classify over the runtime's own message conventions into a new SC4013-SC4019 runtime code family (escaped exception, range, type, syntax, OOM, internal invariant, residual), documented beside SC4012 in the diagnostics registry; no arithmetic kind exists because no runtime site can fire one.
- Every generated entry prologue (init, reset, collect, each export, both emissions) records its external symbol in a SCR_LIB-only static slot the funnel reads, so the structured message names the trapping entry as the host linked it; profile teachings/remediations for the family land as a program-TU overlay table both backends emit identically.
- K5/K7/init-trap fixtures now pin the structured layout with the spec's parse rule (default text, code, entry symbol, fields=3 on a no-teachings profile), and new K12 tests pin the SC4014 teaching/remediation overlay plus the undeclared-code default; the verbatim and text-leads-printable pins stay.
- The default executable lane is byte-identical (signature-stripped cmp over a stash/rebuild pair); all library suites and the diagnostics suite are green on both emissions.
- The return shapeRef now seeds container 'helpers' with the helper name as the member, so an inline-record helper return tables helpers_<name> instead of helpers_<name>_return; parameter naming (helpers_<name>_<param>) is unchanged
- The contract fixture gains 'extent', a helper returning an inline record, pinning the synthesized name helpers_extent in the type table and the value-kind helper return with the arena bit
- The anti-alphabetical suite asserts the new struct entry and helper row on both emissions; V13 byte-determinism stays green
- the contract fixture declares every order anti-alphabetically (type names, fields, enum members, union arms, msg arms, helpers) and the harness asserts the exact orders on both emissions
- its probe reads the identity getters before init and after a poisoning trap and the harness compares both reads to the sidecar's build_id (V12 plus the ratified exemption, end to end)
- V13 rides two independent identical invocations compared hash-equal; V11 rides nm both directions over the prefix
- the contract-attest fixture pins tuple-returning init/update, subscriptions presence, the default <out>.contract.json path, the empty absent forms, and Date.now demoting deterministic to false
- the validator suite passes a full-vocabulary conformance document and catches one targeted mutation per rule, plus SC4009 refusal coverage through compileLibrary