Skip to content

ADR-0040: DeckRunner — the named engine layer

For a year the runtime has been described as “nOSh, the orchestrator.” That single word hid two different layers: the platform floor that drives the hardware (renderer, audio, input mechanism, integrity cores, the host seam), and the engine that runs the game on top of it (the run loop, the screen router, Mission Control, Mission Runner, the cart loader). Conflating them has three concrete costs:

  1. The run loop has no clear owner. “What owns the game loop?” has no clean answer — and in fact the loop body (nosh_host_step) is stranded in the emulator’s SDL main.c, fused with SDL_PollEvent, never lifted into libnosh. The device host can only paint a boot HUD because the engine tier was never separated from a host.
  2. The player-facing scripting surface has no name. ADR-0029’s Mission Runner bindings and the REPL console verbs are the surface untrusted player Lisp runs against — but there is no named boundary for it, so its trust contract is implicit.
  3. Gameplay-architecture decisions had nowhere to live. The 2026-06-18 design session fixed the state model, the verb/loadout model, the capability-baseline model, and the keyboard model — all of which describe the engine, an unnamed thing.
  • The loop lift needs an owner. Moving nosh_host_step out of the host requires naming what it moves into.
  • Mission Control (ADR-0028) and Mission Runner (ADR-0029) already exist as named subsystems, but the layer they live in does not. They need a parent that is not “nOSh the whole library.”
  • The session produced a coherent block of decisions (state tiers, two FFI surfaces, verb model, loadout, keyboard) that interlock and need one durable home.
  • Do not rename or supersede Mission Control / Mission Runner. They are shipped and correct; the engine name must contain them, not replace them.
  • No code moves in this ADR. Like ADR-0039, this is a naming/architecture decision; the loop lift and the terminology sweep are tracked follow-ons.
  • No canonical hardware-spec change. All hardware values remain as in CLAUDE.md; this ADR references them.

Adopt DeckRunner as the named engine layer of the KN-86 runtime — the tier between the Platform and Content that owns the run loop and contains the mission subsystems. Specifically:

  1. DeckRunner is the engine tier (inside libnosh, beside the Platform floor — not a new repo). It owns the deck run loop, the screen router, the cart loader/executor, and the two FFI surfaces. It contains Mission Control (ADR-0028) and Mission Runner (ADR-0029) as subsystems — Mission Control is the front of a run (generate + pick a contract), Mission Runner is the back (execute it), DeckRunner is the engine both live inside.
  2. Loop ownership: the Platform owns the while (timing, frame pacing, present); DeckRunner owns the body (step). The host-facing engine API is the existing nosh_host.h, renamed and owned: init / load_cart / step / on_signal / shutdown + UDS access. Lifting nosh_host_step out of the host main.c into libnosh is the gating follow-on.
  3. Three-tier state model. Run state (volatile — the gameplay; player/REPL read+write), Config (nosh-config.toml — toggleable), Deck State / UDS (durable, game-significant — read-only to the player, written only by DeckRunner as the earned consequence of a sanctioned outcome). Durable progress changes as a result, never as a typed command.
  4. Two FFI surfaces. NoshAPI is cart-facing (ADR-0005). The DeckRunner interface is player-facing — the console verbs + scripting API (it includes the Mission Runner bindings). Player Lisp calls only the DeckRunner interface; DeckRunner re-exports selected NoshAPI on its own terms; player code never reaches raw NoshAPI or the Platform. The DeckRunner interface is the sandbox wall for untrusted player code.
  5. The device is a fantasy console, not a terminal app. It draws RGB565 pixels to the framebuffer (ADR-0036); the “terminal” is an aesthetic + a layout convention. The REPL is DeckRunner’s engine console (Quake-style dropdown, scriptable) — and, with a contract active, it is the Mission Runner.
  6. Keyboard: anchors + layers. The invariant is a small set of anchor keys (BACK / EVAL-ENT / SYS + the layer thumbs) that never change meaning, plus a layered remainder DeckRunner swaps by context (Nokia/T9 text · symbols · Lisp primitives · verb palettes · app chords). This refines ADR-0022’s absolute “the 14 primitives never remap”: the grammar (anchors + the primitive layer) is fixed; the vocabulary (verb-tools) layers. QMK holds layers electrically; DeckRunner picks which is live.
  7. Capability model: baseline vs cart-deepened. DeckRunner ships shallow, generic, first-party base apps (the Capability-Registry System tier, ADR-0030); a cart deepens its domain (augments verbs, unlocks the full interface). Enhanced capability is managed via Lambda Slots (a limited loadout) with full-swap semantics and a coupled time↔exposure swap cost.
  8. Ratify the companion spec. software/runtime/deckrunner-engine-architecture.md is canonical for the detail of items 1–7 and carries the run-execution pipeline, the verb-tier model, and the open reconciliation items.

Option A: Name the engine layer (DeckRunner) + ratify the companion spec. (ACCEPTED)

Section titled “Option A: Name the engine layer (DeckRunner) + ratify the companion spec. (ACCEPTED)”

Name the tier that owns the run loop and contains Mission Control + Mission Runner; record the session’s interlocking decisions in a companion spec this ADR ratifies.

Chosen because it gives the loop lift a destination, gives the player-facing surface a named trust boundary, and gives the gameplay-architecture decisions one durable home — without disturbing the shipped Mission Control / Mission Runner vocabulary or moving any code.

Option B: Leave the engine layer unnamed (“nOSh, the orchestrator”).

Section titled “Option B: Leave the engine layer unnamed (“nOSh, the orchestrator”).”

Keep one word for the whole runtime; describe the engine behaviour in prose where needed.

Rejected because the conflation is exactly what produced the stranded loop and the unnamed trust boundary. An unnamed layer cannot own the loop lift or the player-facing FFI contract; the costs in Context recur.

Option C: Reuse “Mission Runner” or “Mission Control” as the engine name.

Section titled “Option C: Reuse “Mission Runner” or “Mission Control” as the engine name.”

Promote one of the existing subsystem names to mean the whole engine.

Rejected because both are parts of the engine with precise, shipped meanings (ADR-0028/0029). Overloading either to also mean the containing tier would blur a boundary that is currently clean — the engine needs a distinct name that contains them.


DimensionA — name DeckRunner (chosen)B — leave unnamedC — reuse Mission Runner/Control
Gives the loop lift a destination◐ ambiguous
Names the player-facing trust boundary✓ DeckRunner interface✗ implicit◐ overloaded
Preserves Mission Control / Runner meaning✓ contains them✓ (unchanged)✗ overloads
Home for the session’s decisions✓ companion spec✗ scattered◐ awkward fit
Code moved by this ADR✓ none✓ none✓ none
Cost◐ a terminology sweep (phase-gated)✓ none now✗ re-blurs a clean boundary

Cost: a doc-and-code terminology sweep to call the engine tier “DeckRunner” — phase-gated, exactly as ADR-0039’s renames are (decision now, sweep follows).


  • The run loop has a named owner; the loop lift (nosh_host_steplibnosh) now has a destination and is unblocked.
  • The player-facing surface (the DeckRunner interface) is a named, sandboxed trust boundary — sibling to NoshAPI.
  • The state model makes the player console structurally unable to be a cheat console (UDS is write-only via sanctioned outcomes).
  • The session’s gameplay-architecture decisions have one canonical home.
  • Mission Control and Mission Runner keep their exact shipped meaning, now with a named parent.
  • A terminology sweep (“nOSh the orchestrator / engine” → “DeckRunner”) across docs and code — phase-gated, not fix-now.
  • The DeckRunner interface needs its own FFI spec + an ADR-0005 amendment before it is implementable; today it exists only as the Mission Runner bindings.
  • One open reconciliation remains: where loadout/swap management sits relative to ADR-0029’s opaque (load-capability …) boundary (companion spec §12, §16).
  • Lift the run loop into libnosh (nOSh) — implement nosh_host_step() out of the emulator main.c. Highest priority; gates the device.
  • Terminology sweep — name the engine tier “DeckRunner” across kn86-docs, and update CLAUDE.md’s runtime sections (kinoshita repo).
  • DeckRunner interface FFI spec + ADR-0005 amendment — the player-facing surface as a named boundary.
  • ADR-0022 refinement note — record the “anchors + layers” refinement of the “never remap” framing.
  • Gameplay design — swap-currency numbers, trackpoint bindings, and the loadout/load-capability reconciliation (companion spec §16).

Documentation Updates (REQUIRED — Spec Hygiene Rule 3)

Section titled “Documentation Updates (REQUIRED — Spec Hygiene Rule 3)”

Grep result (Spec Hygiene Rule 3). “DeckRunner” is a new coinage (no prior usage in the corpus). This ADR is additive and moves no code, so references that describe “nOSh, the orchestrator/engine” remain accurate descriptions of the current monolithic naming until the terminology sweep lands — each is a tracked phase-gated follow-on, not a fix-now error (same disposition as ADR-0039’s rename sweep):

  • Engine-tier naming sweepsoftware/runtime/orchestration.md and the runtime docs call the orchestrator “nOSh.” Reframe to “DeckRunner (the engine tier of nOSh)” — follow-up PR.
  • CLAUDE.md runtime sections (kinoshita repo) — “nOSh is the orchestrator” → name the DeckRunner tier. Follow-on (different repo).
  • ADR-0022 amendment note — the “never remap” framing refined to “anchors + layers.”
  • DeckRunner interface — new FFI spec + ADR-0005 amendment (follow-up PR).

ADR-0028 named what makes contracts. ADR-0029 named what runs them. Neither named the thing they live inside — the engine that owns the frame loop, routes between screens, loads carts, and hands the player a sandboxed console. For a year that thing was just “nOSh,” the same word used for the whole library and its hardware floor, and the ambiguity had a price: the game loop ended up stranded in a desktop host because there was no engine to put it in. This ADR names the engine DeckRunner and draws the line that was missing — the Platform drives the hardware and owns the clock; DeckRunner owns the tick, contains Mission Control and Mission Runner, and exposes two surfaces, one to carts and one to the player. No code moves here; the loop lift and the sweep follow. But the next time someone asks “what owns the game loop?”, there is finally a name to answer with.