# Lifecycle Markers, Program Init, and Frame Root Semantics Decision Status: Accepted Date: 2026-03-22 Related Agenda: `docs/compiler/pbs/agendas/19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda.md` ## Context After closing the globals surface and module-boundary model, PBS v1 needed a precise lifecycle policy for: - module bootstrap, - project bootstrap, - frame-root identification, - host-call admissibility during init, - and failure behavior at boot. The key requirement was to keep initialization expressive enough for real setup work, while preserving deterministic semantics and preventing the declaration initializer of `global` from becoming a general procedural execution surface. Important fixed inputs already existed: - `declare global` lowers into module-owned runtime storage, - global initializers remain declarative and restricted, - top-level `fn` calls are not allowed inside `declare global` initializers, - `FRAME_SYNC` remains the only normative safepoint, - and PBS v1 remains deterministic and single-threaded. ## Decision PBS adopts the following lifecycle-marker and bootstrap policy in v1: 1. PBS introduces the reserved userland attributes: - `[Init]` - `[Frame]` 2. `[Frame]` is mandatory for an executable project. 3. `[Frame]` is unique per executable project. 4. `[Init]` is optional. 5. `[Init]` may appear in ordinary userland source both for module-local bootstrap and for project bootstrap. 6. Both `[Init]` and `[Frame]` require the same signature shape: - `fn name() -> void` 7. Recommended canonical source forms are: ```pbs [Init] fn init() -> void { ... } ``` ```pbs [Frame] fn frame() -> void { ... } ``` ## Module Init and Project Init Policy PBS adopts the following bootstrap layering: 1. Every module has exactly one synthetic module init. 2. `declare global` always lowers into that synthetic module init. 3. Each file may declare at most one `[Init]`. 4. A file may declare `[Init]` even when it declares no globals. 5. For one file: - that file's globals are materialized first, - then that file's `[Init]`, if present, executes. 6. File-level init fragments are then composed into the one synthetic module init. 7. The `[Init]` colocated in the same file as `[Frame]` is the project init. 8. `[Init]` in other files remains module init. 9. The distinction between module init and project init is one of ordering and semantic role, not one of different source syntax. 10. There is therefore at most one project init per executable project. 11. Project init uses the same signature shape: - `fn init() -> void` ## Bootstrap Order PBS adopts the following logical bootstrap order: 1. materialize globals for each file according to dependency order, 2. execute that file's `[Init]`, if present, 3. compose those steps into one synthetic module init, 4. after required module work is complete, execute project init, if present, 5. then enter the first `frame()`. Within one module: 1. file ordering must follow the order derived from the globals `DependencyGraphAnaliser`, 2. and when two files have no dependency edge between them, the compiler must still use a deterministic stable order. ## Host Interaction During Init PBS adopts the following init-time host-call policy: 1. Loops are permitted in module init and project init. 2. Host calls are forbidden by default during init. 3. The only host calls admissible during init are host methods marked with `[InitAllowed]`. 4. `[InitAllowed]` is a reserved SDK attribute, not a userland attribute. 5. `[InitAllowed]` has no arguments in v1. 6. `[InitAllowed]` is valid only on SDK host methods. 7. `[InitAllowed]` is invalid on userland `fn`, `global`, `service`, or any non-host-method surface. 8. `[InitAllowed]` does not distinguish module init from project init. 9. The difference between those phases is ordering and semantic role, not a separate host-permission class. 10. Admissibility of `[InitAllowed]` must be checked at compile time. Illustrative shape: ```pbs declare host Runtime { [Host(module = "runtime", name = "random_u32", version = 1)] [Capability(name = "runtime")] [InitAllowed] fn random_u32() -> int; } ``` ## Failure Policy PBS adopts a fail-fast boot policy: 1. Failure during module init aborts boot. 2. Failure during project init aborts boot. 3. No automatic retry occurs on the next frame. 4. Implementations must surface a coherent failure message identifying the failing boot phase. ## Rationale This decision keeps the declaration initializer of `global` simple, while still allowing practical bootstrap work in later lifecycle stages. That balance matters because: - arrays and derived data often require loops during setup, - module bootstrap must remain expressive enough for real projects, - host interaction during boot must still be tightly controlled, - and the runtime model must remain deterministic and explainable. Colocation of project init with frame was chosen deliberately as a source-organization rule. It avoids introducing a second project-level marker while keeping project bootstrap discoverable in source. ## Mandatory Diagnostics PBS must provide, at minimum, the following diagnostics for this policy: 1. `project init must be colocated with frame` 2. `multiple project init functions detected` 3. `multiple frame functions detected` 4. `missing frame function for executable project` 5. `init function must have signature fn name() -> void` 6. `frame function must have signature fn name() -> void` 7. `host call not allowed during init` 8. `InitAllowed is valid only on SDK host methods` 9. `boot failed during module init` 10. `boot failed during project init` 11. `multiple module init functions detected` 12. `Init attribute target invalid` ## Invariants 1. `[Frame]` remains unique per executable project. 2. Project init is identified by colocation with `[Frame]`. 3. Each file contributes at most one init fragment. 4. Each module contributes exactly one synthetic module init. 5. Host-call admissibility during init is explicit and compile-time validated. 6. Boot ordering remains deterministic. ## Explicit Non-Decisions 1. This decision does not define the published synthetic wrapper entrypoint. 2. This decision does not define `FRAME_RET` ownership. 3. This decision does not define the final IR encoding of bootstrap. 4. This decision does not define cartridge manifest publication details. ## Spec Impact This decision should feed at least: 1. `docs/compiler/pbs/specs/3. Core Syntax Specification.md` 2. `docs/compiler/pbs/specs/4. Static Semantics Specification.md` 3. `docs/compiler/pbs/specs/9. Dynamic Semantics Specification.md` 4. `docs/compiler/pbs/specs/12. Diagnostics Specification.md` It also constrains future topic `19` work in: 1. `docs/compiler/pbs/agendas/19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda.md` 2. `docs/compiler/pbs/agendas/19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda.md` 3. `docs/compiler/pbs/agendas/19.5. Diagnostics, Manifest Propagation, and Conformance Coverage Agenda.md` ## Validation Notes At minimum, validation for this decision should include: 1. accepted fixtures for `[Init]` and `[Frame]` with correct signatures; 2. accepted fixtures for file-level `[Init]` without globals; 3. accepted fixtures for loops inside module init and project init; 4. rejection fixtures for invalid marker targets and invalid signatures; 5. rejection fixtures for illegal host calls during init; 6. accepted fixtures for SDK host calls marked `[InitAllowed]`; 7. failure fixtures that distinguish module-init boot failure from project-init boot failure; 8. ordering fixtures proving deterministic file ordering within one module.