frame_core/runtime/mod.rs
1//! The composed component runtime — the ONLY composition surface embedding
2//! applications touch (scaffold-unification design, §5.2, finding B6).
3//!
4//! # Why this wrapper exists (the composition story)
5//!
6//! Before this module, every embedding host — frame-host's `runtime.rs` and
7//! every `frame new` scaffold in the wild — hand-wrote the same five beamr
8//! compositions, each of which hides a trap:
9//!
10//! 1. **Scheduler composition.** A bare `Scheduler::with_services` silently
11//! installs an EMPTY built-in-function registry, so every `erlang:*`
12//! import in hot-loaded bytecode resolves Deferred and guard-BIF dispatch
13//! kills the process at its first arithmetic instruction with
14//! `InvalidOperand("guard bif native import")` — the composition defect
15//! attributed in `frame-host/attribution/gcbif-wedge/` (Artemis's
16//! 2026-07-18 probe report, Round 2). [`ComponentRuntime::compose`]
17//! absorbs the fix, [`crate::composition::compose_scheduler`]: gate-1 BIF
18//! population BEFORE construction, so load-time import resolution binds
19//! `erlang:*` to Native entries.
20//! 2. **Support-module hot load.** A component's FFI bytecode is hot-loaded
21//! directly on the scheduler, outside the registry's lifecycle — and the
22//! registry's deferred-BIF wall did not cover it.
23//! [`ComponentRuntime::load_support_module`] applies the same wall (the
24//! crate-internal `composition::deferred_erlang_imports` scan both paths
25//! share) and returns an opaque [`SupportModuleHandle`] instead of a
26//! beamr atom.
27//! 3. **Support-module unload.** The correct teardown is a dance: purge any
28//! retained old generation, delete the current one, then VERIFY the module
29//! is actually gone. [`ComponentRuntime::unload_support_module`] performs
30//! and verifies it; the single-use handle makes a double unload
31//! unrepresentable.
32//! 4. **Mailbox acknowledgement values.** BEAM mailbox integers must fit the
33//! small-integer term payload, and the low integers are usually claimed by
34//! a child's message protocol (liveness, stop). Deriving an
35//! acknowledgement from identifier bytes therefore means folding into
36//! `reserved_below ..= Term::SMALL_INT_MAX` — previously hand-rolled
37//! against `Term::SMALL_INT_MAX` in generated code, now
38//! [`mailbox_integer`].
39//! 5. **Teardown discipline.** `Scheduler::shutdown` is not cleanup: ordered
40//! stop must have drained every process tree first.
41//! [`ComponentRuntime::shutdown`] refuses while
42//! [`ComponentRuntime::live_process_count`] is nonzero.
43//!
44//! Zero beamr types appear in this module's public signatures: bytecode is
45//! `&[u8]`, modules are opaque handles, mailbox messages are the `i64` the
46//! registry already speaks. The runtime's beamr dependency (and its feature
47//! selection) is therefore declared in exactly one place — frame-core's own
48//! manifest — instead of being fossilized into every generated application
49//! (finding B6). [`crate::composition`] remains public as the wrapper's
50//! ground truth for embedders that need raw scheduler access; applications
51//! and frame-host itself compose only through this module.
52
53mod component_runtime;
54mod error;
55mod mailbox;
56
57pub use component_runtime::{
58 ComponentRuntime, RuntimePolicy, SupportModuleHandle, SupportModuleRef, SupportSwapper,
59};
60pub use error::RuntimeError;
61pub use mailbox::mailbox_integer;