Skip to content

Mission Board Generation — Design

Non-scope: No C, no Lisp runtime changes, no emulator source edits. This document defines the surface area only.

Hardware spec references — this design targets the canonical 80×25 amber grid, 31-key input system, CIPHER-LINE auxiliary OLED, and Pi Zero 2 W firmware described in CLAUDE.md → “Canonical Hardware Specification”. Nothing in this doc restates those values.

Composition layer (2026-04-25). 2026-04-25-mission-composition-grammar.md extends this plan with the Mission Composition Grammar — a generative layer that lets the board compose narratively coherent multi-phase contracts from any subset of the operator’s library (replaces the hand-authored cross-pair model at scale). This plan continues to own the board surface, lifecycle, UI, and data model; the MCG plan owns the verb vocabulary, affinity tags, mission-contributions cart block, and composition algorithm.

CIPHER-LINE supersession note (2026-04-24). ADR-0015 (the CIPHER-LINE auxiliary OLED) and its companion docs/architecture/KN-86-CIPHER-LINE-Grammar-Framework.md retired every main-grid Cipher surface that earlier drafts of this plan assumed — including the MISSIONS-tab “Cipher annotation rail” on Rows 9–12, INFO-key rail expansion, phase-completion Cipher debrief on the main grid, and any per-contract Cipher commentary drawn into cartridge space. The Mission Board is now a pure event publisher to the Cipher engine; Cipher renders exclusively on CIPHER-LINE Rows 2–3 with Row 4 optionally biased via aux-* primitives. CLAUDE.md Spec Hygiene Rule 6 ratifies this boundary with a single sanctioned exception (Null’s cipher-main-grid-escape), which does not apply to the Mission Board firmware. Passages below describing what Cipher says, when, and where reflect the CIPHER-LINE model; the pre-ADR-0015 version is in docs/_archive/plans/post-v0.1/2026-04-21-cipher-voice.md for design history.


The Mission Board is a firmware (nOSh) subsystem. It is reached by selecting the MISSIONS tab from the four-tab home hub (historical design: archived docs/_archive/ui-design/KN-86-Home-Screen-Design.md; superseded by the canonical docs/ui-design/KN-86-Bare-Deck-Terminal-Spec.md, which reduced the hub to four tabs per ADR-0015 and PR #67). The board owns three things and borrows everything else.

Owned (the nOSh runtime writes these):

  1. The live board — 4–8 procedurally generated MissionInstance records in SRAM, regenerated on each refresh event.
  2. The generator state — a per-tick snapshot of inputs used to deterministically regenerate the board (seed, reputation tier, balance tier, loaded capability bits, relay overlay version).
  3. The event bus — a runtime-internal topic bus that emits mission_* events consumed by Cipher, the REPL’s read-only mirror, and any future listeners.

Borrowed (read-only in the generator):

  • DeckState — in particular operator_handle, credit_balance, reputation, cartridge_history (the canonical width is the 32-bit field in KN-86-Capability-Model-Spec.md; see Open Questions), cipher_seed, phase_chain_len, phase_chain.
  • Cartridge template region — declarative defmission / defdomain forms parsed at load time from every module whose bit is set in cartridge_history AND whose cartridge is currently inserted (OR was inserted and had its templates cached into the Relay overlay per the Relay module’s semantics).
  • Relay overlay — merged template additions from the Relay cartridge, same shape as cartridge templates, tagged with provenance.

Explicitly NOT owned:

  • Per-cartridge save data. Cartridges still own what happens inside a phase.
  • Phase chain serialization during a multi-phase mission. Phase chain data is owned by the nOSh phase handler dispatcher (separate subsystem).
  • Cipher sentence construction. The board publishes hook payloads; Cipher decides what to say.
typedef struct {
uint8_t board_seed[4]; /* LFSR snapshot at last regeneration */
uint8_t reputation_tier; /* 0=Apprentice .. 4=Legend; derived */
uint8_t balance_tier; /* 0=Broke .. 3=Flush; derived */
uint32_t capability_mask; /* subset of cartridge_history currently eligible */
uint8_t relay_overlay_ver; /* 0 if no Relay; else manifest hash prefix */
uint8_t instance_count; /* 4..8 */
uint16_t next_contract_id; /* monotonic within a deck lifetime */
} MissionBoardGen;

MissionBoardGen is pure firmware SRAM. It is not deck-state. It is rebuilt from deck-state + cartridges on every refresh and discarded on SYS-hold shutdown.

typedef struct {
uint16_t contract_id; /* stable within a deck session */
uint16_t template_handle; /* index into parsed template pool */
uint8_t source_cartridge_bit; /* which module bit in cartridge_history */
uint8_t threat_level; /* 1..6 */
uint8_t phase_count; /* 1..4 */
uint8_t domain_tags_count; /* how many entries in domain_tags */
uint8_t domain_tags[4]; /* interned symbol IDs: network, finance, ... */
uint32_t payout_credits; /* denominated in ¤ */
uint16_t reputation_delta_pass; /* +rep on success */
uint16_t reputation_delta_fail; /* -rep on abandon/fail */
uint8_t flags; /* bookmarked, bid-locked, scripted, multi-cart, ... */
uint32_t narrative_seed; /* stable LFSR handle passed with mission_* events to the Cipher engine as a seed argument, so per-contract utterances on CIPHER-LINE are reproducible without advancing the deck's global cipher_seed. See interaction-plan §3.1 Determinism fix. */
} MissionInstance;

Every field is deterministically derivable from (MissionBoardGen, template_handle, narrative_seed). Instances can therefore be regenerated after a crash without drift, which matters for suspended missions (phase_chain persistence) and for REPL inspection sanity.


The board presents on the MISSIONS tab. Per archived docs/_archive/ui-design/KN-86-Home-Screen-Design.md (now superseded by docs/ui-design/KN-86-Bare-Deck-Terminal-Spec.md), the tab bar is on Row 23 and the action bar is on Row 24. Per CLAUDE.md Spec Hygiene Rule 5, Row 0 is the nOSh runtime status bar and Row 24 is the nOSh runtime action bar.

Row 0 [STATUS BAR: handle ¤credits REP bar threat-ceiling]
Row 1 CONTRACTS BOARD 0x21D84E00 / 6
Row 2 [>] INFILTRATE SIGMA NODE T4 ¤15,000 NET · FIN
Row 3 [ ] FORENSIC DIVE: CIPHER CORP T2 ¤8,000 FIN
Row 4 [ ] DEAD-DROP: ENCRYPTED COURIER T3 ¤12,000 SIG · NAV
Row 5 [ ] SUBDUE PATROL (SCRIPTED) T2 ¤7,500 NET · λ
Row 6 [ ] SURVEILLANCE IMPLANT T1 ¤6,000 NET · *persist
Row 7 [ ] AUDIT THE INTRUSION TRAIL T4 ¤18,000 NET · FIN · 2-PHASE
Row 8
Row 9 (rows 8..13 reserved — blank list padding, never occupied by Cipher)
Row 10
Row 11
Row 12
Row 13
Row 14 (rows 14..22 reserved for inspector or drill-in from CAR)
Row 22
Row 23 HOME [MISSIONS] REPL LINK STATUS
Row 24 CAR=detail CDR=next QUOTE=bookmark EVAL=bid INFO=scan SYS=menu
[CIPHER-LINE auxiliary OLED, independent surface]
Row 1 HANDLE ¤15,420 · DEV · 18:03
Row 2 standard ice. clean if untripped.
Row 3 eighteen on the multi. ledger still slotted.
Row 4 (contextual; bias-shifts under aux-* primitives)
  • Zone A — Board header (Row 1). Title on left (CONTRACTS), board hash right-aligned. The hash is the top 8 hex digits of board_seed and doubles as a board-refresh receipt. Changes only on refresh.
  • Zone B — Contract list (Rows 2–8). Pattern 1 (Full List Browser) from KN-86-UI-Design-System.md. One MissionInstance per row:
    • Col 0–2: selection + bookmark glyph ([>], [ ], [◊] bookmarked).
    • Col 3–43: contract name, left-truncated with if needed.
    • Col 44–48: threat (T4).
    • Col 49–58: payout (¤15,000, right-aligned).
    • Col 59–78: compact domain tag strip — 2–3 interned tag abbreviations joined by · plus modifier glyphs (λ = scripted, *persist = surveillance-style episodic, N-PHASE for multi-phase where N > 1).
  • Zone C — Reserved padding (Rows 8–13). Blank space between the contract list and the full-screen inspector drill-in zone. Cipher does not draw here — per ADR-0015 and Spec Hygiene Rule 6, the Cipher voice lives on CIPHER-LINE, not the main grid. Per-contract Cipher commentary that earlier drafts placed in this rail now rides the CIPHER-LINE Rows 2–3 scrollback, driven by the mission_highlight_changed event push described in §Cross-capability hooks below. The mockup leaves these rows blank so a future board-UX iteration (e.g., summary stats, hotkey legend, or a concentrated-bid meter) can land here without a Cipher migration.
  • Zone D — Inspector drill-in (Rows 14–22). Reserved. When operator presses CAR on a contract, Pattern 5 (Detail Inspector) expands into Rows 14–22 rather than pushing a full-screen modal — this keeps the list visible and the tab context alive. CAR a second time from inside the inspector opens the full-screen Pattern 5 (rows 1–22). BACK collapses one step at a time. This differs from the generic list→modal pattern in the UI Design System and is the board’s one deliberate divergence; rationale is that the board is a hub, not a terminal leaf.
  • Zone E — Action bar (Row 24). Context-sensitive key hints. Differs by mode:
    • List mode: CAR=detail CDR=next QUOTE=bookmark EVAL=bid INFO=scan SYS=menu
    • Inspector mode: EVAL=accept BACK=collapse QUOTE=bookmark INFO=more λ=macro-bid
    • Empty board (cartridge-less, no runtime bounties): CDR=tab SYS=menu (reminds operator to insert a cartridge)
  • No cartridge, no runtime bounties loaded: Zone B becomes a single row: (insert a capability module to populate the board). The board also emits a one-shot mission_board_empty event into the Cipher event stream (see §Cross-capability hooks → Cipher voice); whether the Cipher engine speaks an “insert” prompt on CIPHER-LINE is a mode-selector decision per the Grammar Framework, not a main-grid assertion by the board.
  • Suspended mission in phase_chain: Row 1 is replaced with RESUME: <contract name> — PHASE N/M. Zone B shows the continuation as the first entry with a persistent *resume modifier glyph, then regular generated contracts below it. EVAL on this entry resumes; QUOTE converts it to a bookmarked dormant continuation (design-level wish; see Open Questions on whether the nOSh runtime will honor a non-resume path).

All keys follow the Lisp semantics in KN-86-Input-System-Architecture.md and the list-browser conventions in KN-86-UI-Design-System.md. Board-specific bindings:

KeyList modeInspector modePayload emitted
CARDrill into highlighted contract → collapse into Zone D inspector (row 14–22). Second CAR promotes inspector to full-screen (rows 1–22).Commit to accept + enter first phase (equivalent to EVAL in inspector).`mission_drilled{contract_id, depth: 1
CDRMove highlight to next contract; wraps.Scroll inspector content 3 rows.mission_highlight_changed{contract_id, prev_id}
CONSPair two bookmarked contracts into a session plan (stores the pair in a volatile session_plan slot). The board pushes mission_plan_paired into the Cipher event stream; any pairing commentary appears on CIPHER-LINE if the mode selector elects to speak. Useful for planning multi-cartridge nights without committing.mission_plan_paired{contract_ids[2]}
QUOTEBookmark highlighted contract into quote slot (numpad 1–8). Glyph flips [ ][◊]. Persists across refresh as long as the contract ID is still in the pool; otherwise QUOTE degrades gracefully to a “stale bookmark” marker that CDR skips.Bookmark the inspected contract.mission_bookmarked{contract_id, quote_slot}
EVALBid on highlighted contract. Firmware shows Pattern 5 confirm dialog in Zone D.Accept contract (final commit).mission_bid{contract_id} then (on confirm) mission_accepted{contract_id, ...}
BACKReturn to HOME tab.Collapse Zone D inspector.mission_inspector_collapsed{contract_id}
LAMBDABind a macro to “bid on a contract matching these criteria”. Quick-replay fires an EVAL once the board regenerates a matching contract. This is the board’s killer feature for veterans.Replay the last recorded lambda against the inspected contract’s tags.mission_macro_recorded{lambda_slot, predicate_tags[]} / mission_macro_invoked{lambda_slot}
ATOMTest if highlighted contract is single-phase (phase_count == 1). Plays sfx-confirm on true, sfx-select on false. Useful for experts planning short sessions.mission_atom_tested{contract_id, result}
NILClear any in-progress CONS pairing; clear session plan if CONS pair already formed.Cancel an in-progress EVAL confirmation.mission_plan_cleared{}
INFOShort scan — main-grid Zone D inspector briefly reveals a summary strip (threat breakdown + expected phase cartridges) at Rows 14–16 for 2 seconds, then collapses. The board also pushes mission_info_requested into the Cipher event stream; any editorial annotation on the scan fires on CIPHER-LINE Rows 2–3 if the mode selector elects to speak. Double-tap = open full Pattern 5 inspector (Zone D full-screen).Expand/collapse detail rows (reveals reward scaling, restrictions).`mission_info_requested{contract_id, depth: 1

Grammar consistency tests:

  • QUOTE on a contract then EQ on a later contract reports whether two contracts share the same template_handle (not the same instance; the board refreshes). This keeps EQ semantics honest across refresh boundaries.
  • ATOM on a suspended mission entry returns false (multi-phase by definition); on a scripted single-puzzle mission, returns true — scripted-ness is a modifier, not a phase count.
  • CAR on a contract whose required cartridge is not currently inserted triggers sfx-error plus a 1-line main-grid error banner on Row 1 (INSERT <MODULE>) and publishes mission_required_cartridge_missing into the Cipher event stream so the engine may emit an insert-nudge fragment on CIPHER-LINE Rows 2–3 (mode-selector’s call). This is a pre-flight check, not a generation-time filter; see “Lifecycle” for why.

The board is constructed lazily. Tab-switching to MISSIONS is the public entry point. Internally:

  1. First entry per power cycle. Firmware calls mission_board_init(&g_state) on boot. This does NOT generate contracts yet — it parses templates from all currently-inserted cartridges into the template pool and emits mission_board_ready{template_count, domain_tags[]}. Cost is parsing only; no randomness yet.
  2. Tab enter. On first switch into MISSIONS, firmware calls mission_board_regenerate(trigger: :tab_enter). Emits mission_board_refreshed{...}.
  3. Tab re-enter. No regeneration. Board persists across HOME ↔ MISSIONS ↔ REPL tab cycling within a session.
TriggerSourceNotes
:first_tab_enternOSh runtimeOne-shot per power cycle.
:contract_completedPhase handler dispatcher on successThe completed contract is replaced in place; other entries retained. Emits mission_board_partial_refresh{removed_id, added_id}.
:contract_abandonedPhase handler dispatcher on abandonCompleted-but-failed returns a lower-threat replacement. Small reputation penalty already applied upstream.
:cartridge_insertedCartridge detect pinFull regenerate if the new cartridge’s bit was not in capability_mask. Emits mission_board_refreshed{reason: :new_capability}.
:cartridge_ejectedCartridge detect pinContracts requiring the ejected cartridge are not removed — they survive as “locked” rows with a *insert modifier and a stale-bookmark visual treatment. Operator can re-insert to re-enable.
:sys_refreshSYS → “Refresh Board”Explicit player refresh, costs 1 reputation point. Emits mission_board_refreshed{reason: :sys_explicit, rep_cost: 1}.
:relay_appliedRelay module update flowTemplate pool merge; full regenerate to pull in new template entries.

Each regeneration advances deck_state.cipher_seed by exactly instance_count + 1 LFSR steps and stores the post-advance snapshot back to deck state. This makes every board hash trivially reproducible for QA: given {pre-seed, reputation_tier, balance_tier, capability_mask, relay_overlay_ver}, the board is deterministic.

Key invariant: The Mission Board advances its own board-seed LFSR (used for generator inputs and for minting each MissionInstance.narrative_seed) and does not share the deck’s cipher_seed. Per interaction-plan §3.1 Determinism fix, Cipher receives a mission’s narrative_seed as an event-payload argument and uses it to compose deterministic per-contract fragments on CIPHER-LINE without advancing the deck’s global LFSR. The deck’s cipher_seed still advances on utterance boundaries driven by other events (boot, cart-load, debrief, ambient), but board regeneration bursts do not consume it. This keeps board-driven Cipher commentary replayable for QA and prevents the board from starving the rest of Cipher’s memory budget.

  • BACK in list mode returns to HOME. The board is retained in memory; next MISSIONS tab entry is free.
  • SYS-hold (2s) per KN-86-Input-System-Architecture.md forces a boot return and wipes MissionBoardGen.
  • Power-off detection serializes nothing from the board (it’s pure SRAM rebuild state), but deck state’s new cipher_seed value persists.
Highlight contract → EVAL
→ mission_bid{contract_id} (audible: sfx-confirm; the board also pushes the event into the Cipher stream, Cipher may emit a pre-commit fragment on CIPHER-LINE)
→ Pattern 5 confirm dialog in Zone D (restrictions summary + payout + cartridge requirements)
→ EVAL again to confirm
→ mission_accepted{contract_id, threat_level, phase_count, domain_tags[]}
→ Firmware hands off to phase handler dispatcher (Phase 1)
→ Contract row vanishes from board, replaced after first phase boundary
→ BACK to cancel (emits mission_bid_cancelled{contract_id})

Per ADR-0010-icebreaker-lisp-sketch.md and ADR-0007-scripted-mission-ffi.md, cartridges author templates in Lisp. The nOSh runtime consumes the parsed declarative form — not the lambdas — during template pool registration. Lambdas only execute at generate-time and accept-time, not during board population.

Declarative fields the board reads:

(defmission "MERIDIAN EXTRACT"
(:class network crypto) ; → domain_tags
(:threat-range 2 5) ; clamp against reputation tier
(:base-payout 800) ; → payout_credits after scaling
(:phases 2) ; → phase_count
(:requires ice-breaker signal) ; → required_capability_mask
(:scripted? false) ; → λ flag
(:persistence-duration nil) ; → *persist flag if non-nil
(:hint-text "Extract the payload, then decrypt.") ; → inspector copy
(:generator-phase 1 <lambda>) ; invoked at generate-time only
(:acceptance-contract <lambda>)) ; invoked at phase-end only

Board pool registration happens when:

  1. A cartridge is inserted.
  2. cart-init completes successfully.
  3. The nOSh runtime walks defmission forms and registers each one with a template_handle. Lambdas are held as opaque Fe bytecode handles — the board itself never interprets them.

Tag interning: Each :class symbol is hashed into a small interned table (shared between cartridge, firmware, Cipher, and REPL). Collisions are detected at registration time and rejected with a main-grid error banner (firmware’s standard cart-load error path); the board also pushes a :cart-load-error event into the Cipher event stream so the engine may annotate the rejection on CIPHER-LINE if the mode selector elects to speak.

See MissionInstance struct in “Responsibilities & owned data”. An instance is a reification of a (template_handle, narrative_seed) pair with all generator inputs already baked in. Once generated, the instance is immutable until replaced. The instance’s narrative_seed is the sole input that Cipher needs to produce deterministic commentary — no deck-state snooping.

FieldAccessWhen
operator_handlereadCipher commentary templating (OPERATOR {handle}).
credit_balancereadBalance tier derivation (generator input).
reputationreadReputation tier derivation (generator input, threat clamp). Also read by phase handler on bid/abandon.
cartridge_historyreadDetermines which multi-cartridge templates are even eligible. The board will NOT generate a contract requiring a bit the operator has never loaded.
cipher_seedread + writeAdvanced on every regeneration.
phase_chain_len, phase_chainreadDetect suspended mission; inject as first row with *resume flag.
lambda_slotsread + writeLAMBDA key records a “bid predicate” macro into an existing lambda slot (not a keystroke macro; see Open Questions).
quote_slotsread + writeQUOTE key stores a contract_id in a quote slot.

Each hook below defines the concrete event name, field names, and contract shape so the Cipher-voice, REPL, and nEmacs designs can bind to specific channels. All events flow through a single firmware topic bus (nosh_event_publish(topic, payload) — exact C signature deferred to the embedded-systems agent). Subscribers declare their topics at subsystem init; the bus is fan-out only, no per-subscriber filtering.

Render surface. Per ADR-0015 and the CIPHER-LINE Grammar Framework, Cipher renders exclusively on the CIPHER-LINE auxiliary OLED (Rows 2–3 scrollback, Row 4 contextual). The Mission Board draws nothing on CIPHER-LINE; it only pushes structured events into the shared Cipher event stream via cipher-push-event, and the nOSh runtime generator decides — via the mode selector and coherence stack — whether, when, and how to speak. Silence is a first-class outcome and the correct one for most board-refresh ticks. There is no main-grid Cipher rail on the MISSIONS tab. Cipher does not respect Mission Board animation frames; the target is the Grammar Framework’s normal per-utterance budget on CIPHER-LINE, not a main-grid paint deadline.

Events the board emits onto the Cipher event stream:

Each mapping below pairs a board event with its Cipher event-type keyword per Grammar Framework §3 baseline types. The nOSh runtime (not the board) calls cipher-push-event at each emission; the board’s job is to produce the structured payload and hand it off. The narrative_seed field of a MissionInstance rides the event so the Cipher composer can seed a per-contract scratch LFSR without advancing the deck’s global cipher_seed (interaction-plan §3.1 Determinism fix).

Board eventCipher event typePayload (board contribution)Default affectNotes
mission_board_refreshed:observation{trigger, instance_count, max_threat_level, min_threat_level, has_multi_phase, has_scripted, relay_overlay_ver}:routineSilence is the common case. Board-summary fragments only fire if mode-selector elects to speak under the current beat.
mission_highlight_changed:observation{contract_id, prev_id, template_handle, threat_level, phase_count, domain_tags[], payout_credits, narrative_seed, requires_swap}:routineSeeds per-contract fragment from narrative_seed; mode selector may bias toward annotate under high-threat highlights.
mission_bid:action{contract_id, threat_level, payout_credits, narrative_seed}:routinePre-commit editorial may fire at low rep tiers; higher tiers usually silent.
mission_accepted:mission-start{contract_id, threat_level, phase_count, domain_tags[], narrative_seed, starts_phase: 1}:routinenOSh runtime transitions the beat to :mission-brief on this event. After hand-off, Cipher’s scope is the phase handler dispatcher, not the board.
mission_suspended_resume_available:phase-advance{contract_id, phase_index, phase_count, required_cartridge_name, narrative_seed}:significantFires on MISSIONS tab entry whenever phase_chain_len > 0. The :significant affect tag keeps the resume-context sticky in the event ring so Cipher can annotate it across beats; no main-grid override.
mission_required_cartridge_missing:anomaly{contract_id, required_cartridge_name}:anomalousPre-flight CAR failure. The main-grid error banner carries the literal instruction; CIPHER-LINE fragment, if any, is editorial (mode-selector’s call).
mission_plan_paired:action{contract_ids[2], narrative_seed}:routineCONS-key pairing from list mode.
mission_info_requested:observation{contract_id, depth, narrative_seed}:routineINFO-key short scan / full inspector open.
mission_board_empty:observation{reason: :no_cartridge or :no_templates}:routineOne-shot on degenerate-state entry; lets Cipher optionally render an “insert” prompt on CIPHER-LINE.

What Cipher may read from the board (not via events):

The Cipher engine may call a read-only snapshot accessor mission_board_peek(MissionBoardSnapshot *out) to get the current instance list and MissionBoardGen. This supports composing summary fragments like six contracts. one multi-phase. from the Grammar Framework’s observe mode. The snapshot is a stable copy valid until the next refresh event.

What the Mission Board must NOT do:

  • Draw any glyph on CIPHER-LINE. Only the nOSh runtime Cipher engine and the aux-* primitive handlers do. The board’s only path to CIPHER-LINE is the event stream.
  • Draw any Cipher glyph on the main grid. Cipher is OLED-exclusive per CLAUDE.md Spec Hygiene Rule 6; the Mission Board is not the sanctioned exception (Null is).
  • Advance the deck’s cipher_seed directly. The board advances its own board-seed LFSR and mints narrative_seed values; cipher_seed advancement happens inside the Cipher engine when an utterance actually renders.

Cipher vocabulary wiring: On mission_highlight_changed, the Cipher engine pulls domain vocabulary entries for every tag in domain_tags[] from the merged vocab frame (firmware universals + every loaded cart’s cipher-grammar :vocabulary pools). The vocab frame is published by nOSh core via vocab_frame_updated on cart-insert / cart-eject — see interaction-plan §4.1. The board does not curate vocabulary itself.

Goal: The REPL is read-only per ADR-0005-ffi-surface.md Tier 3. It can inspect the board, replay generation for a given seed, and validate acceptance contracts offline — but cannot bid, accept, bookmark, or mutate deck state.

REPL primitives added or reaffirmed:

Lisp nameTierSignatureReturnsNotes
mission-board3() → listList of mission-instance records.Pure read of mission_board_peek output.
mission-instance3(contract-id) → recordRecord with fields :contract-id :template-handle :threat-level :phase-count :domain-tags :payout-credits :narrative-seed :flags.Returns nil if the id is not in the live board.
mission-board-gen3() → record{:board-seed :reputation-tier :balance-tier :capability-mask :relay-overlay-ver :instance-count}.Pure read of MissionBoardGen.
mission-regenerate-preview3(seed rep-tier balance-tier cap-mask overlay-ver) → listList of instances that WOULD be generated.Never mutates cipher_seed or the live board. Forks an isolated LFSR. Essential for teaching and for QA.
mission-simulate-accept3(contract-id script-output) → record`{:result :pass:fail :failed-clauses}`.
mission-domain-tags3() → listInterned tag symbols currently known to the board.Shared with nEmacs palette (see below).

Board events mirrored into REPL history:

The REPL subscribes to mission_board_refreshed, mission_highlight_changed, mission_accepted, mission_required_cartridge_missing and writes a one-line log entry to the REPL scrollback, prefixed ; so the entries are inert s-expression comments:

; 0x21D84E00 board-refreshed trigger=:tab-enter n=6 max-threat=4
; 0x21D84E14 highlight mid=0x1023 tags=(network finance)
; 0x21D84E2F accepted mid=0x1023 phases=2

This gives operators a timeline they can copy/paste into bug reports without running any code.

Tutorial hook: The first-boot REPL tutorial per ADR-0002 gains a scripted step titled “INSPECT THE BOARD” which runs (mission-board) followed by (length (mission-board)). This uses only the primitives above.

Goal: nEmacs authors scripted-mission solutions and, post-launch, authors templates. The board exposes the vocabulary and shapes nEmacs needs for its predictive palette and for its acceptance-contract dry-run feature.

Board → nEmacs palette enrichment:

Palette signalSourcenEmacs use
mission-domain-tagsmission_board_peekAt cursor positions accepting a domain tag (e.g., a :class slot in a defmission template under edit, or the argument position of a REPL mission-domain-tags call), nEmacs surfaces the interned tag set first, ahead of generic recency.
template-field-schemaParsed cartridge templatesWhen editing defmission forms, nEmacs knows which keyword slots exist and their arity.
active-contract-vocabularymission_highlight_changed payload + loaded defdomain entriesWhen a mission is loaded into nEmacs (scripted-mission flow), the palette prioritizes that mission’s cartridge vocabulary.

Board → nEmacs scripted-mission integration:

When nEmacs opens in scripted-mission editing mode, the nOSh runtime passes a ScriptedMissionContext containing:

{
contract_id,
template_handle,
input_value, /* from (:input-template …) evaluation */
expected_script_hint, /* from (:expected-script …) declaration */
acceptance_contract_handle, /* opaque Fe bytecode handle */
grants_mask, /* FFI tier-2 capabilities per ADR-0007 */
narrative_seed
}

nEmacs exposes a dedicated key (design-level: EVAL in the nEmacs editor chrome) to run the script against input_value and call mission-simulate-accept. nEmacs renders :failed-clauses node-scoped — each failed clause highlights its related s-expression.

Events nEmacs emits back:

EventPayloadnOSh runtime reaction
nemacs_script_submitted{contract_id, script_source, script_bytecode_handle}nOSh runtime invokes the acceptance contract. On pass: emits mission_accepted{contract_id, ...} with scripted flag set. On fail: emits mission_script_failed{contract_id, failed_clauses[]} back to nEmacs.
nemacs_script_abandoned{contract_id}nOSh runtime returns to board list; contract remains.

Palette refresh: Whenever the board emits mission_board_refreshed, nEmacs invalidates its mission-domain-tags cache so newly-inserted cartridges contribute tags immediately.

Snippet library binding: Per ADR-0002 the snippet library ships in v1 and is per-deck-portable. The board tags each accepted-and-passed scripted-mission solution with its template_handle. nEmacs lists snippets filtered by current highlighted contract’s template_handle on demand. This is a pull-from-board, push-to-library wiring — nothing new in the board other than exposing template_handle on mission_accepted.


  1. cartridge_history width: 16 bits or 32? KN-86-Capability-Model-Spec.md declares uint32_t cartridge_history and justifies the width as “14-module launch library plus 18 future expansion slots”. KN-86-Bare-Deck-Terminal-Spec.md declares uint16_t cartridge_history in the same struct. These two canonical specs disagree on the actual size of a field this design depends on. The board’s capability_mask currently follows the 32-bit Capability Model. PM: please reconcile before embedded-systems starts. The bare-deck value must change, or the capability model must narrow and find a different expansion path.

  2. Mission Board as tab vs. as post-boot screen. KN-86-Capability-Model-Spec.md § “The Mission Board” says “It is the primary interface the operator sees after boot.” Archived docs/_archive/ui-design/KN-86-Home-Screen-Design.md (superseded by docs/ui-design/KN-86-Bare-Deck-Terminal-Spec.md) places it as the MISSIONS tab within the Home hub and has HOME as the landing screen. This design assumes the tab model (it is both newer and more concrete), but two documents that should agree do not. PM: decide canon; update the loser.

  3. Cipher engine latency under a burst of mission events. (Resolved by ADR-0015 decoupling.) Earlier drafts required the (now-retired) main-grid Cipher rail to repaint within one animation frame of a highlight change. ADR-0015 moved Cipher to CIPHER-LINE and the Grammar Framework moved the engine to a mode-selector + coherence-stack composition step that may elect silence. There is no longer a paint-deadline shared with the Mission Board’s main-grid render. The residual question — whether the CIPHER-LINE utterance noticeably lags the main-grid highlight during rapid CDR browsing — is a Grammar Framework / firmware tuning matter, not a board-design constraint. No action for the board agent.

  4. LAMBDA predicate macros vs. keystroke macros. KN-86-Input-System-Architecture.md § LAMBDA describes keystroke macros stored as 32-step key-event sequences. The Mission Board’s proposed LAMBDA binding (“bid on contract matching these criteria”) is a higher-level predicate macro. Either: (a) introduce a second lambda-slot type and distinguish them at recall, or (b) compile the predicate macro down to a short keystroke sequence that re-plays only while a matching contract is highlighted, or (c) drop this binding and treat LAMBDA on the board as “no-op”. This design tentatively picks (a) but it is a real grammar question that affects other subsystems.

  5. Multi-phase contracts and ejected cartridges. The lifecycle table says contracts requiring an ejected cartridge survive as locked rows. mission_board_peek exposes them to the REPL and to the Cipher engine via snapshot. Whether CIPHER-LINE should emit editorial fragments for a *insert-flagged highlight — or stay silent and let the main-grid banner carry the insert instruction — is a mode-selector tuning question for the Grammar Framework, not a board surface decision. Design-punt; needs playtesting.

  6. Does SYS → Refresh Board really cost 1 reputation? Capability Model spec says “costs a small reputation penalty”. One point is a guess. Asking for Josh’s call at the PM-synthesis step — could also be a tiny credit cost, or nothing at higher reputation tiers.

  7. REPL mission-simulate-accept vs. hint integrity. mission-simulate-accept is powerful — it lets an operator brute-force an acceptance contract by repeatedly tweaking the script. This is arguably a feature (empowers learning) and arguably a design bug (breaks the hint ladder). ADR-0007 does not explicitly forbid it, but ADR-0002 hint phrasing assumes limited attempts. Gameplay design agent: your call on whether simulate-accept is gated behind a debug flag or is always on.

  8. Narrative-seed vs. cipher-seed reuse. (Resolved.) Each instance carries a narrative_seed distinct from the deck’s cipher_seed. Per interaction-plan §3.1 Determinism fix (ratified 2026-04-21 PM call) and the CIPHER-LINE Grammar Framework §9 Determinism, the Cipher engine accepts the seed as an event-payload argument and does not advance the deck’s global LFSR during board-driven commentary. No open action for the board agent.

  9. Relay overlay tag-collision resolution. If a Relay-shipped template introduces a new :class tag that collides with an existing cartridge tag, which wins? This design says “cartridge wins, firmware publishes a main-grid warning banner and a :cart-load-warning event into the Cipher stream so CIPHER-LINE may optionally annotate”, but Relay’s whole point is content updates — a Relay version-bump that says “this tag used to mean X, now means X′” would be surprising. PM: decide with the Relay spec owner.


End of design.