Cartridge Lifecycle
How the nOSh runtime detects, mounts, registers, hot-swaps, and unmounts a cartridge. Single source for the complete lifecycle — referenced by orchestration.md (mission flow), deck-state.md (which fields get touched on each event), and cipher-voice.md (grammar merge on insertion).
adr/ADR-0019— the SD-card-via-USB-MSC physical model that drives the lifecycle events.adr/ADR-0017— Pico 2 coprocessor; per ADR-0019, no longer touches the cart bus (event source isudevon the Pi).adr/ADR-0006—.kn86container format the runtime parses on insertion.software/api-reference/grammars/coprocessor-protocol.md— UART frames the runtime emits to the Pico on session boundaries.
Lifecycle states
Section titled “Lifecycle states”A cartridge moves through five states:
ABSENT → MOUNTED → REGISTERED → ACTIVE → UNMOUNTING → ABSENT| State | Trigger | Runtime action |
|---|---|---|
| ABSENT | No cart present in the slot | Mission board generates contracts from cartridge_history alone (no cart-specific templates available). |
| MOUNTED | udev add event from the SD-via-USB-MSC bridge | Filesystem mounted; .kn86 header read; runtime decides if header is valid and capability bits are recognized. |
| REGISTERED | Header valid + capability bits parsed | Templates parsed; cipher-grammar merged into the active grammar tables; phase handlers entered into dispatch table; cartridge_history bit set in deck state. |
| ACTIVE | First mission contract using this cart’s capability begins | Cart program code (Fe Lisp, ADR-0001) handles cell events; cart contributes events to the Cipher event ring; cart save file is opened for read/write. |
| UNMOUNTING | udev remove event OR Hot Swap initiated | Phase chain serialized to deck-state SRAM (then flash); cart save file flushed and closed; grammar overlay rolled back; phase handlers unregistered; coprocessor session closed (if any). |
Transitions are runtime-driven; cartridges never observe state changes directly — they see only their own handler invocations.
Insertion (ABSENT → MOUNTED → REGISTERED)
Section titled “Insertion (ABSENT → MOUNTED → REGISTERED)”- Operator inserts the SD-card cartridge sled into the slot. The push-push socket engages the SD card’s contacts. (Per ADR-0019: the card-detect switch fires the lifecycle event.)
- The internal USB hub’s SD bridge IC enumerates the card as a USB mass-storage device.
- The Pi’s kernel emits a
udevadd event. The nOSh runtime subscribes to this event. - The runtime mounts the filesystem read-only at a known mount point (e.g.,
/mnt/cart). - The runtime opens the
.kn86container per ADR-0006. Header validation:- Magic bytes
- Format version
capability_bits(which capabilities this cart provides)- Manifest checksums
- If the header is invalid, the runtime surfaces an in-fiction error on the bare-deck terminal (
> CARTRIDGE FORMAT NOT RECOGNIZED) and remains in MOUNTED state until the cart is removed. - If the header is valid, registration proceeds:
- Mission template structures are parsed from the cart’s template region.
- The cart’s
cipher-grammarblock is merged into the active CIPHER-LINE grammar tables. Seecipher-voice.mdfor merge rules. - Phase handlers are registered with the runtime’s dispatch table.
cartridge_historybit is set in Universal Deck State if not already set.- Mission board regenerates with the expanded template pool.
The runtime never auto-launches a contract on insertion — the operator chooses what to do next from the mission board.
Activation (REGISTERED → ACTIVE)
Section titled “Activation (REGISTERED → ACTIVE)”A cart becomes ACTIVE the first time a mission contract requiring its capability begins:
- Operator selects a contract from the mission board.
- Runtime resolves the first phase’s required capability against the loaded carts.
- If the required cart is REGISTERED, runtime calls its phase handler with the deserialized phase chain context.
- Cart’s program code begins executing in the cell runtime (Fe VM). Cart’s per-save file at
/save/<cart_id>.savon the cart’s SD is opened for read/write. - Cart contributes events to the Cipher event ring via
cipher-push-event(ADR-0015).
Multiple carts can be REGISTERED simultaneously (rare in practice — the slot holds one cart at a time, but a recently-removed cart’s templates can stay parsed transiently). Only one cart is ACTIVE at any moment.
Hot Swap (ACTIVE → UNMOUNTING → MOUNTED → REGISTERED → ACTIVE)
Section titled “Hot Swap (ACTIVE → UNMOUNTING → MOUNTED → REGISTERED → ACTIVE)”Multi-phase missions that span capabilities require physical cartridge swaps. The runtime formalizes this as Hot Swap — a tactical pause where the operator exchanges modules to pick up new capabilities. Hot Swap mechanics are detailed in software/cartridges/modules/ice-breaker.md where they serve as a core decision framework.
Phase chain protocol
Section titled “Phase chain protocol”Phase 1: ICE BREAKER (NETWORK INTRUSION) ↓ phase completes → runtime serializes intermediate state to phase_chain ↓ runtime displays: > PHASE 2 REQUIRES: FORENSIC ACCOUNTING ↓ > INSERT: BLACK LEDGER ↓ operator physically swaps cartridgePhase 2: BLACK LEDGER (FORENSIC ACCOUNTING) ↓ runtime reads phase_chain, passes context to cartridge handler ↓ phase completes → runtime updates phase_chain ↓ runtime displays: > CONTRACT COMPLETE ↓ > PAYOUT: 4,800 ¤ ↓ > REPUTATION: +12Debrief: Cipher voice summaryWhat happens during a swap
Section titled “What happens during a swap”- The active phase completes. The cart’s phase handler returns control to the runtime.
- The runtime writes the phase chain to deck state (SRAM). Includes: phase index, intermediate results (data extracted, evidence gathered, contacts mapped), accumulated threat modifiers, and partial payout accrued.
- The runtime displays the swap prompt with the next required capability.
- The operator physically removes the current cartridge.
udevremove event fires; runtime executes the UNMOUNTING sequence (see below) and returns to ABSENT. - The operator inserts the next cartridge. Runtime executes the insertion sequence; new cart reaches REGISTERED.
- Runtime verifies the new cart’s
capability_bitsinclude the required capability. If not, surfaces> CARTRIDGE DOES NOT PROVIDE: <CAPABILITY> / RE-INSERT: <expected_module>. - Runtime calls the new cart’s phase handler with the deserialized phase chain context.
- Phase begins. Operator works in the new capability domain with full context from the previous phase.
Swap timeout
Section titled “Swap timeout”If the operator does not insert a valid cartridge within 5 minutes, the runtime offers two options:
- Suspend — phase chain preserved in flash; suspended missions can be resumed on next boot.
- Abandon — reputation penalty, partial payout for completed phases.
Diegetic swap surface
Section titled “Diegetic swap surface”The physical act of swapping a cartridge is a diegetic event, not a limitation to apologize for. The swap prompt is in-fiction:
> NETWORK TRACE RECOVERED> FINANCIAL RECORDS IDENTIFIED IN EXTRACTED DATA> ANALYSIS REQUIRES FORENSIC ACCOUNTING CAPABILITY> INSERT MODULE: BLACK LEDGER> PHASE CHAIN ACTIVE — DO NOT POWER DOWNThe display flickers briefly when the cartridge is removed (2–3 frames of noise, matching the original device’s behavior). The boot sequence for the new cartridge is abbreviated — no full splash screen, just: > MODULE LOADED: BLACK LEDGER / PHASE 2 OF 3 / RESUMING...
Removal (ACTIVE → UNMOUNTING → ABSENT)
Section titled “Removal (ACTIVE → UNMOUNTING → ABSENT)”Whether triggered by Hot Swap or by the operator simply pulling the cart, removal follows the same sequence:
udevremove event fires.- Runtime serializes any in-flight phase chain state to the deck state (SRAM, then flash).
- Cart’s per-save file (
/save/<cart_id>.savon the cart’s SD) is flushed and closed. Save format continues to be ADR-0006 tagged static data; the storage backend is filesystem read/write per ADR-0019, replacing ADR-0013’s MBC5-SRAM model. - The cart’s grammar overlay is rolled back from the CIPHER-LINE grammar tables.
- Phase handlers are unregistered from the dispatch table.
- Coprocessor session is closed (if the cart held one — see
coprocessor-protocol.mdsession-control frames). - Filesystem is unmounted.
- Runtime returns to ABSENT.
cartridge_history is never cleared — once a cart has been registered on this deck, the bit stays set for the deck’s lifetime.
Per-cart save data
Section titled “Per-cart save data”Per ADR-0019, save state lives as a file on the cart’s own SD card filesystem:
- Path:
/save/<cart_id>.savon the cart’s mounted volume - Format: ADR-0006 tagged static data (unchanged at the FFI surface from ADR-0013)
- Lifecycle: opened for read/write at ACTIVE; flushed and closed at UNMOUNTING
- Cross-cart fields (handle, credits, reputation) live on the device’s microSD per Universal Deck State, not on the cart
The cart_save / cart_load NoshAPI primitives are unchanged at the FFI surface; only the underlying storage moves from MBC5 SRAM bank-switching to filesystem read/write.
Edge cases
Section titled “Edge cases”- Cart removed mid-phase without a Hot Swap prompt. Runtime treats this as an unsafe removal; phase chain still serializes, but Cipher voice surfaces an
:anomalousevent (“the deck remembers the removal”). On re-insert, runtime offers Resume from saved phase chain. - Cart with corrupted save file. Runtime falls back to a fresh save; original is renamed to
<cart_id>.sav.corruptand a Cipher event fires. - Cart whose
.kn86header references a missing capability. Header rejected at registration; cart stays in MOUNTED state. Bare deck terminal shows the error. - Multiple inserts/removes in rapid succession.
udevevent coalescing in the runtime: each MOUNTED → UNMOUNTING transition must complete before the next event is processed. - Power-off during ACTIVE. Low-voltage interrupt drives the same UNMOUNTING sequence with a tightened budget. Phase chain preservation takes priority over save flushing.