frame-core 0.3.0

Component model, lifecycle, process isolation — hosts components as supervised BEAM process trees
Documentation
//! The composed component runtime — the ONLY composition surface embedding
//! applications touch (scaffold-unification design, §5.2, finding B6).
//!
//! # Why this wrapper exists (the composition story)
//!
//! Before this module, every embedding host — frame-host's `runtime.rs` and
//! every `frame new` scaffold in the wild — hand-wrote the same five beamr
//! compositions, each of which hides a trap:
//!
//! 1. **Scheduler composition.** A bare `Scheduler::with_services` silently
//!    installs an EMPTY built-in-function registry, so every `erlang:*`
//!    import in hot-loaded bytecode resolves Deferred and guard-BIF dispatch
//!    kills the process at its first arithmetic instruction with
//!    `InvalidOperand("guard bif native import")` — the composition defect
//!    attributed in `frame-host/attribution/gcbif-wedge/` (Artemis's
//!    2026-07-18 probe report, Round 2). [`ComponentRuntime::compose`]
//!    absorbs the fix, [`crate::composition::compose_scheduler`]: gate-1 BIF
//!    population BEFORE construction, so load-time import resolution binds
//!    `erlang:*` to Native entries.
//! 2. **Support-module hot load.** A component's FFI bytecode is hot-loaded
//!    directly on the scheduler, outside the registry's lifecycle — and the
//!    registry's deferred-BIF wall did not cover it.
//!    [`ComponentRuntime::load_support_module`] applies the same wall (the
//!    crate-internal `composition::deferred_erlang_imports` scan both paths
//!    share) and returns an opaque [`SupportModuleHandle`] instead of a
//!    beamr atom.
//! 3. **Support-module unload.** The correct teardown is a dance: purge any
//!    retained old generation, delete the current one, then VERIFY the module
//!    is actually gone. [`ComponentRuntime::unload_support_module`] performs
//!    and verifies it; the single-use handle makes a double unload
//!    unrepresentable.
//! 4. **Mailbox acknowledgement values.** BEAM mailbox integers must fit the
//!    small-integer term payload, and the low integers are usually claimed by
//!    a child's message protocol (liveness, stop). Deriving an
//!    acknowledgement from identifier bytes therefore means folding into
//!    `reserved_below ..= Term::SMALL_INT_MAX` — previously hand-rolled
//!    against `Term::SMALL_INT_MAX` in generated code, now
//!    [`mailbox_integer`].
//! 5. **Teardown discipline.** `Scheduler::shutdown` is not cleanup: ordered
//!    stop must have drained every process tree first.
//!    [`ComponentRuntime::shutdown`] refuses while
//!    [`ComponentRuntime::live_process_count`] is nonzero.
//!
//! Zero beamr types appear in this module's public signatures: bytecode is
//! `&[u8]`, modules are opaque handles, mailbox messages are the `i64` the
//! registry already speaks. The runtime's beamr dependency (and its feature
//! selection) is therefore declared in exactly one place — frame-core's own
//! manifest — instead of being fossilized into every generated application
//! (finding B6). [`crate::composition`] remains public as the wrapper's
//! ground truth for embedders that need raw scheduler access; applications
//! and frame-host itself compose only through this module.

mod component_runtime;
mod error;
mod mailbox;

pub use component_runtime::{
    ComponentRuntime, RuntimePolicy, SupportModuleHandle, SupportModuleRef, SupportSwapper,
};
pub use error::RuntimeError;
pub use mailbox::mailbox_integer;