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.mdextends 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-contributionscart 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.mdretired 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 viaaux-*primitives.CLAUDE.mdSpec Hygiene Rule 6 ratifies this boundary with a single sanctioned exception (Null’scipher-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 indocs/_archive/plans/post-v0.1/2026-04-21-cipher-voice.mdfor design history.
Responsibilities & owned data
Section titled “Responsibilities & owned data”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):
- The live board — 4–8 procedurally generated
MissionInstancerecords in SRAM, regenerated on each refresh event. - 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).
- 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 particularoperator_handle,credit_balance,reputation,cartridge_history(the canonical width is the 32-bit field inKN-86-Capability-Model-Spec.md; see Open Questions),cipher_seed,phase_chain_len,phase_chain.- Cartridge template region — declarative
defmission/defdomainforms parsed at load time from every module whose bit is set incartridge_historyAND 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.
Generator state struct (design sketch)
Section titled “Generator state struct (design sketch)”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.
MissionInstance shape
Section titled “MissionInstance shape”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.
UI zones
Section titled “UI zones”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.
Default layout — MISSIONS tab
Section titled “Default layout — MISSIONS tab”Row 0 [STATUS BAR: handle ¤credits REP bar threat-ceiling]Row 1 CONTRACTS BOARD 0x21D84E00 / 6Row 2 [>] INFILTRATE SIGMA NODE T4 ¤15,000 NET · FINRow 3 [ ] FORENSIC DIVE: CIPHER CORP T2 ¤8,000 FINRow 4 [ ] DEAD-DROP: ENCRYPTED COURIER T3 ¤12,000 SIG · NAVRow 5 [ ] SUBDUE PATROL (SCRIPTED) T2 ¤7,500 NET · λRow 6 [ ] SURVEILLANCE IMPLANT T1 ¤6,000 NET · *persistRow 7 [ ] AUDIT THE INTRUSION TRAIL T4 ¤18,000 NET · FIN · 2-PHASERow 8Row 9 (rows 8..13 reserved — blank list padding, never occupied by Cipher)Row 10Row 11Row 12Row 13Row 14 (rows 14..22 reserved for inspector or drill-in from CAR)Row 22Row 23 HOME [MISSIONS] REPL LINK STATUSRow 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 ofboard_seedand 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. OneMissionInstanceper 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-PHASEfor multi-phase where N > 1).
- Col 0–2: selection + bookmark glyph (
- 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_changedevent 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
CARon 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.CARa second time from inside the inspector opens the full-screen Pattern 5 (rows 1–22).BACKcollapses 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)
- List mode:
Empty / degenerate states
Section titled “Empty / degenerate states”- 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-shotmission_board_emptyevent 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 withRESUME: <contract name> — PHASE N/M. Zone B shows the continuation as the first entry with a persistent*resumemodifier 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).
Input grammar
Section titled “Input grammar”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:
| Key | List mode | Inspector mode | Payload emitted |
|---|---|---|---|
| CAR | Drill 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 |
| CDR | Move highlight to next contract; wraps. | Scroll inspector content 3 rows. | mission_highlight_changed{contract_id, prev_id} |
| CONS | Pair 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]} |
| QUOTE | Bookmark 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} |
| EVAL | Bid 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, ...} |
| BACK | Return to HOME tab. | Collapse Zone D inspector. | mission_inspector_collapsed{contract_id} |
| LAMBDA | Bind 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} |
| ATOM | Test 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} |
| NIL | Clear any in-progress CONS pairing; clear session plan if CONS pair already formed. | Cancel an in-progress EVAL confirmation. | mission_plan_cleared{} |
| INFO | Short 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:
QUOTEon a contract thenEQon a later contract reports whether two contracts share the sametemplate_handle(not the same instance; the board refreshes). This keeps EQ semantics honest across refresh boundaries.ATOMon 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.CARon a contract whose required cartridge is not currently inserted triggerssfx-errorplus a 1-line main-grid error banner on Row 1 (INSERT <MODULE>) and publishesmission_required_cartridge_missinginto 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.
Lifecycle
Section titled “Lifecycle”Invocation
Section titled “Invocation”The board is constructed lazily. Tab-switching to MISSIONS is the public entry point. Internally:
- 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 emitsmission_board_ready{template_count, domain_tags[]}. Cost is parsing only; no randomness yet. - Tab enter. On first switch into MISSIONS, firmware calls
mission_board_regenerate(trigger: :tab_enter). Emitsmission_board_refreshed{...}. - Tab re-enter. No regeneration. Board persists across HOME ↔ MISSIONS ↔ REPL tab cycling within a session.
Refresh triggers
Section titled “Refresh triggers”| Trigger | Source | Notes |
|---|---|---|
:first_tab_enter | nOSh runtime | One-shot per power cycle. |
:contract_completed | Phase handler dispatcher on success | The completed contract is replaced in place; other entries retained. Emits mission_board_partial_refresh{removed_id, added_id}. |
:contract_abandoned | Phase handler dispatcher on abandon | Completed-but-failed returns a lower-threat replacement. Small reputation penalty already applied upstream. |
:cartridge_inserted | Cartridge detect pin | Full regenerate if the new cartridge’s bit was not in capability_mask. Emits mission_board_refreshed{reason: :new_capability}. |
:cartridge_ejected | Cartridge detect pin | Contracts 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_refresh | SYS → “Refresh Board” | Explicit player refresh, costs 1 reputation point. Emits mission_board_refreshed{reason: :sys_explicit, rep_cost: 1}. |
:relay_applied | Relay module update flow | Template pool merge; full regenerate to pull in new template entries. |
Seed advancement rules
Section titled “Seed advancement rules”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.
Dismissal
Section titled “Dismissal”BACKin list mode returns to HOME. The board is retained in memory; next MISSIONS tab entry is free.SYS-hold (2s) perKN-86-Input-System-Architecture.mdforces a boot return and wipesMissionBoardGen.- Power-off detection serializes nothing from the board (it’s pure SRAM rebuild state), but deck state’s new
cipher_seedvalue persists.
Bid/accept flow
Section titled “Bid/accept flow”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})Data model
Section titled “Data model”Template shape (cartridge side)
Section titled “Template shape (cartridge side)”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 onlyBoard pool registration happens when:
- A cartridge is inserted.
cart-initcompletes successfully.- The nOSh runtime walks
defmissionforms and registers each one with atemplate_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.
Instance shape (post-generation)
Section titled “Instance shape (post-generation)”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.
Deck-state touchpoints
Section titled “Deck-state touchpoints”| Field | Access | When |
|---|---|---|
operator_handle | read | Cipher commentary templating (OPERATOR {handle}). |
credit_balance | read | Balance tier derivation (generator input). |
reputation | read | Reputation tier derivation (generator input, threat clamp). Also read by phase handler on bid/abandon. |
cartridge_history | read | Determines which multi-cartridge templates are even eligible. The board will NOT generate a contract requiring a bit the operator has never loaded. |
cipher_seed | read + write | Advanced on every regeneration. |
phase_chain_len, phase_chain | read | Detect suspended mission; inject as first row with *resume flag. |
lambda_slots | read + write | LAMBDA key records a “bid predicate” macro into an existing lambda slot (not a keystroke macro; see Open Questions). |
quote_slots | read + write | QUOTE key stores a contract_id in a quote slot. |
Cross-capability hooks
Section titled “Cross-capability hooks”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.
→ Cipher voice
Section titled “→ Cipher voice”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 event | Cipher event type | Payload (board contribution) | Default affect | Notes |
|---|---|---|---|---|
mission_board_refreshed | :observation | {trigger, instance_count, max_threat_level, min_threat_level, has_multi_phase, has_scripted, relay_overlay_ver} | :routine | Silence 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} | :routine | Seeds 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} | :routine | Pre-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} | :routine | nOSh 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} | :significant | Fires 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} | :anomalous | Pre-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} | :routine | CONS-key pairing from list mode. |
mission_info_requested | :observation | {contract_id, depth, narrative_seed} | :routine | INFO-key short scan / full inspector open. |
mission_board_empty | :observation | {reason: :no_cartridge or :no_templates} | :routine | One-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.mdSpec Hygiene Rule 6; the Mission Board is not the sanctioned exception (Null is). - Advance the deck’s
cipher_seeddirectly. The board advances its own board-seed LFSR and mintsnarrative_seedvalues;cipher_seedadvancement 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.
→ dev REPL
Section titled “→ dev REPL”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 name | Tier | Signature | Returns | Notes |
|---|---|---|---|---|
mission-board | 3 | () → list | List of mission-instance records. | Pure read of mission_board_peek output. |
mission-instance | 3 | (contract-id) → record | Record 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-gen | 3 | () → record | {:board-seed :reputation-tier :balance-tier :capability-mask :relay-overlay-ver :instance-count}. | Pure read of MissionBoardGen. |
mission-regenerate-preview | 3 | (seed rep-tier balance-tier cap-mask overlay-ver) → list | List 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-accept | 3 | (contract-id script-output) → record | `{:result :pass | :fail :failed-clauses}`. |
mission-domain-tags | 3 | () → list | Interned 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=2This 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.
→ nEmacs
Section titled “→ nEmacs”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 signal | Source | nEmacs use |
|---|---|---|
mission-domain-tags | mission_board_peek | At 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-schema | Parsed cartridge templates | When editing defmission forms, nEmacs knows which keyword slots exist and their arity. |
active-contract-vocabulary | mission_highlight_changed payload + loaded defdomain entries | When 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:
| Event | Payload | nOSh 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.
Open questions / contradictions
Section titled “Open questions / contradictions”-
cartridge_historywidth: 16 bits or 32?KN-86-Capability-Model-Spec.mddeclaresuint32_t cartridge_historyand justifies the width as “14-module launch library plus 18 future expansion slots”.KN-86-Bare-Deck-Terminal-Spec.mddeclaresuint16_t cartridge_historyin the same struct. These two canonical specs disagree on the actual size of a field this design depends on. The board’scapability_maskcurrently 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. -
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.” Archiveddocs/_archive/ui-design/KN-86-Home-Screen-Design.md(superseded bydocs/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. -
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.
-
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. -
Multi-phase contracts and ejected cartridges. The lifecycle table says contracts requiring an ejected cartridge survive as locked rows.
mission_board_peekexposes 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. -
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.
-
REPL
mission-simulate-acceptvs. hint integrity.mission-simulate-acceptis 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. -
Narrative-seed vs. cipher-seed reuse. (Resolved.) Each instance carries a
narrative_seeddistinct from the deck’scipher_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. -
Relay overlay tag-collision resolution. If a Relay-shipped template introduces a new
:classtag 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-warningevent 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.