Skip to content

ADR-0029: The Mission Runner

ADR-0028 names the runtime subsystem that owns contract generation (Mission Control). It does not specify what happens when the operator presses [EVAL] and accepts a contract. The implicit model in orchestration.md v1.0 was “the runtime initiates the first phase” — the runtime called the cart’s phase handler directly, and the operator interacted with cart UI until the handler returned.

That model conflicts with three things we’ve already committed to:

  1. ADR-0002 §1 — the REPL is a release-default first-class onboard utility with current-mission context implied but never specified.
  2. ADR-0007 — scripted missions: the operator writes a Lisp program that drives a mission to a result. This requires a Lisp environment with mission state bound, which the implicit model doesn’t provide.
  3. ADR-0016 — nEmacs and the REPL share a unified subsystem; nEmacs-authored scripts should be able to drive missions, not just sit in a separate buffer.

Form KN86-MCMB-001 (Josh, 2026-05-03) §5 names the post-acceptance environment the Mission Runner: a Lisp REPL with the mission struct bound, where the operator drives capability calls via (load-capability ...), branches on result structs, sequences Hot Swaps, and ultimately calls (complete-mission ...). The Mission Runner is not a separate buffer — it’s the existing REPL with three extra bindings and one extra builtin available while a contract is active.

  • ADR-0028 introduces (load-capability ...) as the cart-invocation mechanism. Something has to call it. That caller is the Mission Runner.
  • Scripted missions (ADR-0007) need a host environment. Without naming the Mission Runner, ADR-0007’s “mission Lisp code” is floating — there’s no spec for where it runs.
  • ADR-0029 is small but load-bearing. Without it, ADR-0028’s contract generation has no consumer.
  • No new buffer. REPL+nEmacs is one subsystem (ADR-0016). Mission Runner mode must be additive to that subsystem, not a parallel surface.
  • Standalone REPL must continue to work. Pressing TERM on the bare deck (no contract active) still gives the operator the standard REPL — Mission Runner symbols are unbound, calling them raises :no-active-mission.
  • Snippet library, history ring, FFI access policy unchanged. All of repl.md §4–§6 remains authoritative for the Runner; only the bound environment differs.
  • Capability calls must be opaque to the runner by default. The runner sees a result struct; it does not pump the cart’s main loop or observe internal phase state during a call.

The post-acceptance environment for an active contract is the Mission Runner: the existing REPL/nEmacs subsystem with mission-context bindings layered on. Specifically:

  1. Mission Runner is REPL mode + bindings, not a separate surface. The toast overlay, keymap, history ring, snippet library, and FFI access policy from repl.md §1–§6 apply unchanged.
  2. Bindings provided on contract acceptance:
    • current-mission — the mission struct (record type defined in §3)
    • mission-params — property list extracted from the struct (:threat, :seed, :objectives, …)
    • phase-chain — list of phase requirement records, hydrated from UDS phase_chain
    • (load-capability ...) — Mission-context FFI builtin (per ADR-0005 amendment)
  3. Capability calls are opaque. (load-capability :module :seed seed) transfers control to the cart’s gameplay loop, runs to completion, and returns a result struct ({:outcome :trace :extracted :turns :bonuses}). The runner does not observe the cart’s internal state during the call. Reactive handlers from inside the runner during a capability call are an OQ (mission-control.md §7 OQ-4); not in scope for v1.
  4. Hot Swap is implicit. If (load-capability ...) names a registered-but-not-inserted module, the runtime emits a Hot Swap prompt; the call blocks until the cart is inserted (or the operator aborts).
  5. Contract resolution. (complete-mission current-mission) finalizes the contract — payout, reputation, provenance. (abandon-mission) (or [NIL] from the Runner’s keymap) ends the contract with :result-failure affect, the configured penalty, and a board refresh. Both unbind the Runner symbols.
  6. Standalone REPL mode. With no active contract, current-mission etc. are unbound; calling them raises :no-active-mission. This is the only behavioral difference between modes.
  7. The Runner inherits the unified ADR-0016 dispatch. TERM is still context-polymorphic (CIPHER-LINE seed capture and other higher-priority surfaces win); within a Mission Runner session, the priority-5 default is the Runner’s REPL surface.

Option A: Mission Runner = REPL + bindings (additive). (ACCEPTED)

Section titled “Option A: Mission Runner = REPL + bindings (additive). (ACCEPTED)”

What it is. Land Mission Runner mode as an environment layer over the existing REPL. Three new bindings, one new builtin, no new buffer.

Chosen because it preserves ADR-0016’s unified subsystem promise, gives ADR-0007 scripted missions a real host, and reuses every piece of infrastructure already shipped (toast, history, snippets, keymap, FFI policy). The standalone REPL keeps working unchanged — operators who never run a mission still have a useful Lisp environment.

Option B: Dedicated Mission Runner buffer.

Section titled “Option B: Dedicated Mission Runner buffer.”

What it is. A new top-level surface alongside the REPL toast and nEmacs editor. Distinct keymap, distinct framebuffer, distinct history.

Rejected because it duplicates ADR-0016’s machinery, introduces a third surface to keep coherent, and forces the operator to learn new keys during the most attention-loaded moment of play (contract acceptance → execution). The bindings layered onto the existing REPL are sufficient.

Option C: Cart-driven loop (status quo: implicit model in orchestration.md v1.0).

Section titled “Option C: Cart-driven loop (status quo: implicit model in orchestration.md v1.0).”

What it is. Runtime calls the cart’s phase handler directly on contract acceptance; the cart owns the operator’s attention until it returns.

Rejected because it breaks ADR-0007 (scripted missions have no host) and ADR-0002 (the REPL has no mission context). It also makes multi-phase contracts brittle — coordination between phases lives in cart code, which means cart-pair authors have to coordinate on calling conventions out-of-band.


DimensionOption A (chosen)Option B (dedicated buffer)Option C (cart-driven)
Operator cognitive load✓ same surface, three new symbols✗ third surface to learn✓ no new surface
Reuses ADR-0016 unified subsystem✗ duplicates machinery✗ orthogonal
Hosts ADR-0007 scripted missions✗ no host
Multi-phase coordination✓ runner sequences via Lisp✓ same✗ ad-hoc cart-pair coupling
Standalone REPL still useful✓ unchanged✓ unchanged◐ unchanged but isolated from missions
Implementation cost◐ binding plumbing only✗ full new surface✓ already implicit
Future extensibility (reactive handlers, sub-missions, scripted scoring)✓ Lisp environment composes✓ same✗ blocked at handler boundary

Cost: the runtime needs binding-management plumbing for current-mission etc. — small, scoped to Mission Control’s accept/complete/abandon hooks.


  • ADR-0007 scripted missions have a defined host environment.
  • ADR-0028 contract generation has a defined consumer.
  • Multi-phase missions become Lisp programs — sequencing, branching, and Hot Swap planning are first-class operator concerns.
  • Cart authors don’t change. Phase handlers are still the unit of cart-side gameplay; what changes is the caller (Mission Runner, not runtime main loop).
  • The standalone REPL’s release-default story (ADR-0002, GWP-315) is unaffected.
  • New FFI primitive load-capability (per ADR-0005 amendment) needs implementation in nosh.c / coproc.c.
  • Mission struct shape (record fields, serialization) is OQ-1 in mission-control.md; we ship the binding semantics first, refine the record shape in a follow-on.
  • Runner-side reactive handlers during capability calls are deferred (OQ-4). Operators who want “fire on hunter spawn” inside a capability execution wait for that ADR.
  • load-capability FFI implementation — Mission-context tier in kn86-emulator/src/nosh.c.
  • Mission struct shape — OQ-1; needs an ADR or spec doc once the first two cross-capability schemas land.
  • Reactive handlers (OQ-4) — separate ADR if/when scripted missions outgrow the opaque-call model.
  • Teststests/test_mission_runner.c covering binding lifecycle (accept → bound, complete → unbound, abandon → unbound, error paths).
  • (complete-mission ...) / (abandon-mission) FFI — paired with load-capability in ADR-0005 amendment.


ADR-0028 names what makes contracts; ADR-0029 names what runs them. The Mission Runner is not a new buffer or a new surface — it is the REPL we already ship, with three extra bindings and one extra builtin available while a contract is active. The operator who has spent six months writing snippets at the bare-deck REPL discovers, at contract-acceptance time, that the same surface now has current-mission bound and (load-capability ...) available. They can drive the mission interactively at the prompt, or run a script they wrote in nEmacs that does it for them. Both are first-class. Carts don’t change — phase handlers are still the cart-side unit of gameplay. What changes is the caller. The runtime no longer reaches into a cart and pumps its handler directly; the runner does, on the operator’s behalf, when the Lisp program says so.