Skip to content

First-Party Programs — Implementation Roadmap


0. Ground state (what is actually built today)

Section titled “0. Ground state (what is actually built today)”

The roadmap is anchored on a real, working exemplar, not a green field. Before sequencing anything, here is the verified state of the tree on main:

  • AmberCalc (#7) is fully built and wired. runtime/programs/ambercalc/ambercalc.lsp is a complete pure-Lisp system screen (grid, SES formula model, sort, GoTo, type-routing, :world ingestion via *launch-world*, dossier-commit export). It ships a ctest (test_ambercalc) and a cross-context render test (test_blackledger_ambercalc_render).
  • The FFI seam exists and is bound. nosh_lisp_bridge.c binds launch-app (Tier 1) + dossier-commit / dossier-has? (Tier 2); dossier.c implements the store; recorder_drive.c carries the world-carrying launch-app hook (--cart-dispatch). The ADR-0049 signatures are real C, not aspirational.
  • The screen router already reserves all 13 program ids. screen_router.c k_programs[] maps every ADR-0042 id (conduit … knsalk) to a screen name, with a NULL activation sexp for the not-yet-built ones; AmberCalc’s row carries "(ambercalc/load-world!)". Registering a new program is: author the screen module, add it to the router’s k_libs load list, and (if it needs a launch hook) fill in its activation sexp. This is not greenfield router work — it is filling reserved slots.
  • The knEmacs engine substrate is vendored and pulled. runtime/vendor/kec-lisp/editor/ carries 52-mode.lsp (major-modes) and 85-minibuffer.lsp (command-by-name) from kec-lisp ADR-0004 (KEC_REV 29af766) — but see the Phase-0 footgun in §6: they are NOT yet in the CMake embed manifest, so they are non-callable on-device today.
  • The demo recorder exists. kn86rec (runtime/tools/kn86rec/) records both a cell-API --screen and a --cart-dispatch flow into a real-screen GIF (GWP-605/610). This is the QA-demo tool every slice’s exit criterion uses.

The build/test matrix is green in-tree (runtime 120+ ctests). The build pattern, the FFI, the router, and the recorder are all proven by AmberCalc — this wave is “do it five more times, on the engine, and extract the shared library when the consumers line up.”


Phase 0 · Foundation — gates LIVE behavior

Section titled “Phase 0 · Foundation — gates LIVE behavior”

Goal. Stand up the substrate that live-world programs need: the central World Engine, the opaque live :world handle + tick delivery, the screen-router activation wiring, and the kec-lisp editor-module embed manifest. Phase 0 does not gate static/cold-case programs (see the cleavage in §2) — a cold-case RIPSAW reads a frozen world region and needs none of the live machinery.

Work items.

  1. World Engine implementation [spec = GWP-626]. The central generator: seed + cart-archetype → the single shared mission world-region (one coherent generated world per mission instance, per enrichment-contract.md §4 — one region, not one per program). The world is the union of the per-program slices (bzbx’s WIRE layer, RIPSAW’s RELATIONSHIP layer, AmberCalc’s table, etc.), coherent across them (the host bzbx finds is owned by the entity RIPSAW maps). Includes:
    • the World Clock — hybrid: move-driven by default (a tick is a move/budget unit on cold cases), cart wall-clock heat where a cart opts in (per the move-driven vs heat note in MEMORY / the program specs’ “tick-aware” sections);
    • the typed-event bus — clock events (node-online, new-filing, stream-appears) the live programs subscribe to and render live.
  2. The opaque live :world handle + tick delivery [spec = GWP-625, ADR-0049 amendment]. Promote the AmberCalc-era *launch-world* snapshot-binding into the opaque, GC-rooted, mission-instance-scoped FE_TPTR handle ADR-0049 §2 already specifies as the contract, plus the live read path: program-side accessors that see mission-state changes (a node revealed stays revealed) and the tick/event delivery onto a live handle. :invalid-world on a stale handle. This is the hard gate for every live-reveal behavior (bzbx → CONDUIT live reveal; RIPSAW live-case filings).
  3. Screen-router activation wiring. For each program in this wave, fill its k_programs[] slot: add the screen module to the router’s k_libs load list and supply its activation sexp (the (<prog>/load-world!) analogue of AmberCalc’s row). The ids are already reserved — this is completion, not new registration. (Per-program; lands incrementally with each program, but the wiring mechanism is validated once here against a first non-AmberCalc program.)
  4. kec-lisp editor-module embed-manifest wiring (the footgun — see §6). Add 52-mode.lsp (major-modes) and 85-minibuffer.lsp (command-by-name) — and any other editor modules the modes call — to the KEC_EDITOR_SRCS list in runtime/CMakeLists.txt, and confirm the KN86_NEMACS_LIB integration is on the build path the programs link. Without this, define-major-mode and completing-read are silently unbound on-device even though the files are vendored.

Exit criteria.

  • A World Engine ctest: seed + archetype → a world region is deterministic and coherent across at least two program slices (e.g. a host in the WIRE layer is owned by an entity in the RELATIONSHIP layer).
  • A live-handle ctest: a handle survives a mission-state mutation (revealed-stays-revealed), delivers a tick event, and raises :invalid-world after a mission-instance reset.
  • A router ctest: a non-AmberCalc program id activates its registered screen through the live router path (the test_screen_router surface).
  • define-major-mode and completing-read resolve in the System-tier context a program loads into (an embed-manifest smoke test), proving the editor tier is callable.

Phase 0 is partly parallelizable with Phase 1. Items 1–2 (World Engine + live handle) are the long pole and gate Phase 2, not Phase 1. Item 4 (embed manifest) gates every mode-based program including cold-case RIPSAW, so it is the true critical-path Phase-0 item and must land first. See §2/§3.


Phase 1 · First vertical slice — RIPSAW (cold-case / static world)

Section titled “Phase 1 · First vertical slice — RIPSAW (cold-case / static world)”

Goal. Build the first non-AmberCalc program end-to-end as a knEmacs major-mode, on a static world it does not need the live handle for — proving the program-as-mode construction model and the slice checklist (§5), and producing the first consumer-side half of the shared tabulated-list library.

Why RIPSAW, why static. RIPSAW’s own spec is explicit: “the cold-case (v1 Black Ledger) path is static and works today”; only live-case org events (a filing appearing mid-mission) depend on the opaque live handle. So RIPSAW can read a frozen world region (materialized once at launch, no ticks) and proceed in parallel with Phase-0 items 1–2. It only needs the embed manifest (Phase-0 item 4) and the static slice of the World Engine’s RELATIONSHIP layer (a generated org graph with no clock).

Work items.

  1. ripsaw-mode skeletonruntime/programs/ripsaw/, a define-major-mode on the engine, dual-panel (foldable org/directory tree via the engine tree-cursor CAR/CDR/BACK + an entity-detail/linkage panel). Registered via deck/register-screen + the router slot (Phase-0 item 3 mechanism).
  2. The org/entity-graph world-schema — the entity / edge / directory-node node types from the RIPSAW spec, materialized lazily over the static RELATIONSHIP slice. :hidden? edges are the clues; the visible skeleton is the haystack.
  3. The generic verb floor — QUERY (minibuffer completing-read over entity names/attrs — the EUDC narrowing), EXPAND/COLLAPSE, descend/ascend, INSPECT, FOLLOW (mgr→report, owner→account), COMMIT-to-DOSSIER. Ships bare-launchable.
  4. DOSSIER wiringdossier-commit linkage facts (org:<id>:employs:<person>, acct:<id>:owns, corp:<id>:controls:<shell>, org:<id>:shell-links) with confidence-by-recon-depth; dossier-has? gates (a previously-mapped org pre-seeds the tree). Uses the bound bridge primitives from §0 — no new FFI.
  5. Extract the shared tabulated-list library — RIPSAW’s entity-attribute/linkage detail panel is a tabulated-list. Build it inside RIPSAW first (design-for-extraction: keep the generic half behind a seam), do not pre-extract. (See §2 for the consumer-order correction — RIPSAW is not the first consumer; bzbx is. The rule-of-three extraction triggers in Phase 2, not here. RIPSAW builds its own table behind a seam so Phase 2 can harvest it.)
  6. ctest — headless tests for the tree-cursor navigation, QUERY narrowing, FOLLOW-the-hidden-edge, and dossier-commit/dossier-has? round-trip, on the test_ambercalc/test_screen_router link surface (no new C — pure-Lisp program).
  7. QA demo GIF — a real-screen kn86rec recording (the cold-case Black Ledger RIPSAW recon: PROFILE filings → shell links → name a beneficiary → commit).

Exit criteria.

  • RIPSAW loads bare (deck’s own DOSSIER directory as the “org of one”), and enriched (a static generated org graph via :world).
  • The full verb floor is exercised by ctest; a hidden ownership edge is FOLLOWed and committed; a dossier-has? pre-seed de-risks a second launch.
  • A kn86rec GIF of the cold-case recon flow is attached to the story.
  • RIPSAW’s tabulated-list lives behind a seam, ready to be the harvest target in Phase 2.

Phase 2 · Remaining programs (bzbx, CONDUIT, Kommander, kn9, Keyring)

Section titled “Phase 2 · Remaining programs (bzbx, CONDUIT, Kommander, kn9, Keyring)”

Goal. Build the remaining five, each a mode + world-schema + verb floor + DOSSIER + tests + demo GIF (§5). Extract the shared tabulated-list / feed library when the consumer count justifies it. bzbx requires the live handle (Phase-0 items 1–2 complete) for its live reveal; the others can use static or live worlds per their specs.

Work items (per program — each is a full §5 vertical slice).

  • bzbx (bzbx-mode, SPLIT: engine primary + optional deferred cell-API capture waterfall). World = the WIRE layer (host/port/service/link/dns/stream). Verb floor: PING/PROBE, TRACEROUTE, RESOLVE, FETCH, CONNECT, CAPTURE-VIEW. The capture viewer is the top-style tabulated-list — bzbx is consumer #1 of that table. Live-handle dependent: the bzbx → CONDUIT live reveal (a host revealed in the run-region mid-mission, before it’s a DOSSIER fact) needs the opaque live handle. The durable-key path (commit :reachable, CONDUIT gates on it) works without it, but the live reveal is the headline interaction — so bzbx is gated on Phase-0 items 1–2.
  • CONDUIT (conduit-mode, clean knEmacs mode, comint/shell-mode lineage). A session is a buffer (scrollback + input line). Gates on bzbx’s revealed/:reachable host. Contributes the comint / session-buffer generic half (extract when the second comint consumer arrives — bzbx’s applet output is that consumer).
  • Kinoshita Kommander (kommander-mode, clean knEmacs mode, Dired×2 / Sunrise lineage). Two tabulated-list panels (name/size/mtime/tags), MARK + verb (COPY/MOVE/DELETE), clone-to-passive-pane, AVFS-into-archives, open-terminal-into-dir = (launch-app :conduit …). Another tabulated-list consumer.
  • kn9 (kn9-mode, clean knEmacs mode, Gnus/Mutt lineage). Group buffer (tabulated-list) + thread tree (tree-cursor) + message buffer. The group buffer is identical in shape to the Mission Board feed — kn9 + Mission Board are the feed-engine pair.
  • Keyring (keyring-mode, SPLIT: engine primary + thin cell-API crack output animation). Key MANAGEMENT is a tabulated-list (id/kind/owner/state) + INSPECT detail; CRACK input is grammar (select lock + method + EVAL), only the crack OUTPUT is animated. Another tabulated-list consumer. CIPHER narration → OLED only.

Shared-library extraction (the rule-of-three trigger).

  • tabulated-list has five consumers across this wave (bzbx #1, RIPSAW #2, kn9 #3, Kommander #4, Keyring #5). Per ADR-0046 build-then-extract: each program builds its own table behind a seam first; extract the shared tabulated-list library when the 2nd/3rd consumer lands (i.e. after bzbx + RIPSAW, confirmed by kn9). Do not pre-build the generic table.
  • The feed engine (Mission Board + kn9, same tabulated-list shape) is the feed-reader specialization of that table — extract it once the table library exists and kn9’s group buffer + the Mission Board feed are the two confirmed consumers.
  • comint (CONDUIT + bzbx applet output) is a separate extraction on the same discipline.

Exit criteria.

  • Each of the five programs: loads bare + enriched, full verb floor under ctest, DOSSIER round-trip, a kn86rec GIF.
  • The bzbx → CONDUIT live reveal is demonstrated (a host revealed mid-run in bzbx is openable in CONDUIT before it is a durable DOSSIER fact) — proving the live handle end-to-end.
  • The shared tabulated-list library is extracted and ≥3 programs consume it (the rule of three is satisfied, not speculatively pre-built).
  • The feed engine backs both kn9 and the Mission Board.

Goal. Wire the campaign threads that span programs, and prove one worked multi-program mission end-to-end.

Work items.

  1. Campaign threads.
    • bzbx → Keyring → CONDUIT → Kommander — bzbx sniffs a credential fragment that de-risks a Keyring crack; the cracked key pre-auths a CONDUIT session on a bzbx-revealed host; Kommander stages exfil off that session (open-terminal-into-dir handoff in reverse).
    • RIPSAW → AmberCalc → DOSSIER — RIPSAW follows a hidden ownership edge to an account; the account hands off to AmberCalc (the spreadsheet lens); the audit finding commits to DOSSIER. (AmberCalc is already built — this thread is wiring, not a new program.)
  2. Cross-mission dossier-has? gating — a key committed in one mission de-risks or hard-gates an objective in a later mission (the recon → knowledge bridge made load-bearing across mission instances).
  3. One worked multi-program mission — a single contract that requires ≥3 programs in sequence, with the World Engine’s coherence (one host, owned by one entity, holding one account) visible across the bzbx/RIPSAW/AmberCalc slices, demonstrated as a kn86rec walkthrough.

Exit criteria.

  • Both campaign threads run end-to-end on a generated world.
  • A cross-mission dossier-has? gate is shown to block, then unblock after the prerequisite recon.
  • A multi-program mission GIF demonstrates the coherent shared world across program handoffs.

The load-bearing cleavages:

  • Static-world vs live-world. Programs that read a frozen world region (materialized once at launch, no ticks) need only the embed manifest + a static World Engine slice — they can start before the live handle. Programs whose headline interaction is a live reveal (a node revealed mid-run, a tick event rendered) are gated on the opaque live handle (Phase-0 items 1–2). Cold-case RIPSAW = static → Phase 1, parallel with Phase 0. bzbx’s live reveal = live → Phase 2, after the handle.
  • tabulated-list / feed extraction timing (rule of three). Build the table inside the first consumer, extract at the 2nd/3rd. bzbx is consumer #1, RIPSAW #2, kn9 #3. Extraction triggers in Phase 2 — RIPSAW (Phase 1) builds its table behind a seam but does not extract.
graph TD
    subgraph P0["Phase 0 · Foundation"]
        EM["embed-manifest wiring<br/>(52-mode + 85-minibuffer → KEC_EDITOR_SRCS)"]
        WE["World Engine<br/>(GWP-626): generator + World Clock + event bus"]
        LH["opaque live :world handle + tick delivery<br/>(GWP-625, ADR-0049 amendment)"]
        RW["router activation wiring<br/>(fill reserved k_programs slots)"]
    end

    subgraph EX["Built exemplar (on main)"]
        AC["AmberCalc #7<br/>+ launch-app/dossier-commit/dossier-has? bound<br/>+ kn86rec recorder"]
    end

    subgraph P1["Phase 1 · RIPSAW (STATIC / cold-case)"]
        RIP["ripsaw-mode + org-graph schema + verb floor<br/>+ DOSSIER wiring + table-behind-a-seam"]
    end

    subgraph P2["Phase 2 · remaining five"]
        BZ["bzbx (LIVE reveal) — table consumer #1"]
        CO["CONDUIT (static-ok) — comint core"]
        KM["Kommander (static-ok) — table consumer #4"]
        K9["kn9 (static-ok) — table #3 + feed pair"]
        KR["Keyring (split) — table consumer #5"]
        TL["EXTRACT shared tabulated-list lib<br/>(rule of three: bzbx + RIPSAW + kn9)"]
        FE["EXTRACT feed engine<br/>(kn9 + Mission Board)"]
    end

    subgraph P3["Phase 3 · cross-program threading"]
        TH["campaign threads + cross-mission gating<br/>+ one worked multi-program mission"]
    end

    AC -. proves build pattern / FFI / router / recorder .-> P1
    AC -. proves build pattern .-> P2
    EM --> RIP
    WE -->|static slice| RIP
    RW --> RIP

    EM --> BZ
    EM --> CO
    EM --> KM
    EM --> K9
    EM --> KR
    WE --> BZ
    LH -->|live reveal HARD-GATES| BZ
    WE -. static slice ok .-> CO
    WE -. static slice ok .-> KM
    WE -. static slice ok .-> K9

    BZ -->|revealed host| CO
    RIP -. table behind seam .-> TL
    BZ --> TL
    K9 --> TL
    TL --> FE
    K9 --> FE

    RIP --> TH
    BZ --> TH
    CO --> TH
    KM --> TH
    K9 --> TH
    KR --> TH
    AC --> TH
    LH --> TH

Section titled “3. Recommended build order + parallelization”

Critical path: embed manifest → RIPSAW slice → (World Engine + live handle) → bzbx → CONDUIT → cross-program threading.

OrderWorkCan run concurrently withGated by
1Phase-0 item 4 (embed manifest)nothing — do first
2aPhase 1 RIPSAW (static)2b (World Engine + live handle)embed manifest only
2bPhase-0 items 1–2 (World Engine + live handle)2a (RIPSAW)embed manifest (for any mode smoke test)
3abzbx3b (CONDUIT/Kommander/kn9 static portions)live handle (2b)
3bCONDUIT, Kommander, kn9, Keyring (static slices)3a (bzbx), each otherembed manifest + World Engine static slices
4Extract tabulated-list + feed + comint libsbzbx + RIPSAW + kn9 (rule of three)
5Phase 3 threadingall programs in the thread + live handle

The key parallelization win: RIPSAW (cold-case, static) and the World Engine + live-handle foundation run concurrently. RIPSAW proves the program-as-mode model and the slice checklist on a static world while the long-pole live machinery is built beside it; bzbx (the first live-reveal program) then lands on a proven slice pattern and a finished live handle. Phase 0’s only hard pre-req for RIPSAW is the embed manifest.

Validation of the RIPSAW-first call — CONFIRMED

Section titled “Validation of the RIPSAW-first call — CONFIRMED”

RIPSAW-first is the right call. Reasoning:

  1. It is the cleanest static program with a real cart behind it. Cold-case Black Ledger gives RIPSAW a concrete, already-designed world (the shell-company org graph) and a concrete demo (PROFILE → shell links → name the beneficiary). It needs zero live machinery — its spec says the cold-case path “works today.” That lets the first non-AmberCalc slice prove the construction model (mode skeleton, verb floor, DOSSIER wiring, slice checklist) without waiting on the World Engine + live handle long pole.
  2. It exercises the most engine surface of any single program. RIPSAW uses the tree-cursor (CAR/CDR/BACK + fold), completing-read (EUDC QUERY), AND tabulated-list (the detail panel). Proving all three on one program de-risks the engine substrate for the entire wave — if the embed manifest or the engine tier is wrong, RIPSAW surfaces it first and loudly.
  3. It is the natural design-for-extraction seed for the shared table. RIPSAW is tabulated-list consumer #2; building its table behind a seam (without extracting) sets up the rule-of-three harvest in Phase 2 cleanly.
  4. It pairs with an already-built neighbor. The RIPSAW → AmberCalc → DOSSIER thread reuses the one program already shipped. Building RIPSAW first means the first cross-program thread (Phase 3) is RIPSAW + AmberCalc — two done programs — not two new ones.

One refinement (not a counter): the prompt skeleton calls RIPSAW “the first consumer” of tabulated-list. Per the merged specs, bzbx is consumer #1; RIPSAW is #2. This does not change the RIPSAW-first build order (bzbx is gated on the live handle and must come later), but the roadmap should — and does — say RIPSAW builds its table behind a seam without extracting, and the extraction triggers in Phase 2 once bzbx + RIPSAW + kn9 are the confirmed three. Treating RIPSAW as “the program that designs the table for extraction” rather than “the first consumer” keeps the rule-of-three honest.


  1. The embed-manifest footgun (highest-confidence, do-first). 52-mode.lsp and 85-minibuffer.lsp are vendored under runtime/vendor/kec-lisp/editor/ but are not in the KEC_EDITOR_SRCS CMake list today, and KN86_NEMACS_LIB defaults OFF. A program calling define-major-mode or completing-read will fail with “unbound symbol” on-device even though the file is present and CI may stay green if no test exercises it. Mitigation: Phase-0 item 4 lands first, with an embed-manifest smoke test that calls define-major-mode + completing-read in the program’s System-tier context. Re-check on every kec-lisp sync (per MEMORY: KEC_CORE_SRCS / editor manifest is duplicated across CMakeLists and is curated for the arena).
  2. The live-handle ADR-0049 amendment (GWP-625) is a hard gate for Phase-2 bzbx. bzbx’s headline interaction — the live bzbx → CONDUIT reveal — cannot ship until the opaque live handle + tick delivery exist. The AmberCalc-era *launch-world* is a snapshot binding, not the live, mission-scoped, mutation-visible handle ADR-0049 §2 describes. If GWP-625 slips, bzbx’s live reveal slips with it (the durable-key fallback still works, but it is not the designed experience). Mitigation: start GWP-625 + GWP-626 concurrently with RIPSAW so the handle is ready when bzbx starts; keep bzbx’s durable-key path as the de-risked floor.
  3. Per-cart save vs. run-state confusion. The world region is run-state (resets at the mission-instance boundary; a stale handle raises :invalid-world); DOSSIER facts are durable (sanctioned-write, cross-mission). The danger is a program treating a :world read as durable or trying to raw-write DOSSIER. Mitigation: the slice checklist (§5) makes “durable consequence only via dossier-commit, never a raw write; :world reads are never persisted” an explicit gate; the live-handle ctest asserts :invalid-world after reset.
  4. World Engine coherence is unspecified detail (GWP-626 owns it). The per-program world schemas (the exact node shapes for WIRE / RELATIONSHIP / table / filesystem) are per-program design; the World Engine must generate them coherently (one host owned by one entity holding one account). If the generator produces incoherent slices, the cross-program threads (Phase 3) break. Mitigation: the Phase-0 exit criterion requires a coherence ctest across ≥2 slices before any Phase-2 program reads a generated world.
  5. tabulated-list premature extraction. The temptation is to build a generic table first (speculative generality — exactly Option C that ADR-0046 rejected). Mitigation: the roadmap forces build-behind-a-seam in RIPSAW + bzbx, extract only at the confirmed third consumer (kn9). No generic table before two real consumers exist.
  6. Host CMake duplication (per MEMORY). Pure-Lisp programs (the common case here, like AmberCalc) need no host CMakeLists touch. But any program that adds a runtime/src/*.c TU referenced by a host-linked target must add it to both hosts/emulator and hosts/device NOSH_SRCS or the hosts fail to link while runtime CI stays green. Mitigation: prefer pure-Lisp programs (the engine-mode model makes this the default); flag any C-seam addition for the dual-host-CMake check.

5. Definition of a program vertical slice (the repeatable checklist)

Section titled “5. Definition of a program vertical slice (the repeatable checklist)”

Every program in this wave follows the same slice, generalized from the built AmberCalc exemplar. A slice is “done” when all six gates pass:

  1. Mode skeletonruntime/programs/<name>/<name>.lsp, a define-major-mode (<name>-mode) on the knEmacs engine, registered via deck/register-screen + its k_programs[] router slot (and deck/register-char if it is a composer screen). Bare-launchable. Side of the ADR-0046 scope line declared (clean engine mode, or SPLIT with a thin/optional cell-API passenger).
  2. World-schema — the program’s slice of the World Engine’s world region (its node/edge types), read through :worldlazily materialized, static or live per the program’s spec. Bare-launch (no :world) degrades to the program’s honest default world (never raises — enrich-never-gate).
  3. Verb floor — the generic, bare-launchable verb set from the program’s spec, wired to the engine substrate: tree-cursor navigation, completing-read for command-/query-by-name, and the program’s domain verbs. Carts overlay domain verbs on top; the floor ships without a cart.
  4. DOSSIER wiring — durable findings emitted only through (dossier-commit <key>) (sanctioned; never a raw write), with confidence-by-recon-depth; gates read via (dossier-has? <key> &key min-conf). :world reads are never persisted; the only durable consequence is the committed fact.
  5. Tests — headless ctest on the test_ambercalc / test_screen_router link surface (pure-Lisp programs need no new C): verb-floor exercise, navigation, DOSSIER round-trip, bare-vs-enriched launch, and (for live programs) the live-handle reveal + :invalid-world-after-reset assertion.
  6. Demo GIF — a real-screen kn86rec recording of the program’s headline flow (the QA-demo gate every KN-86 code story ends on — GWP-605), attached to the story; the task goes to Review, not Done.

6. The embed-manifest call-out (Phase-0, restated for emphasis)

Section titled “6. The embed-manifest call-out (Phase-0, restated for emphasis)”

Because this is the single most likely silent failure in the whole wave, it is restated as its own section. The kec-lisp editor tier is vendored but not fully embedded:

  • Present in runtime/vendor/kec-lisp/editor/: 52-mode.lsp, 85-minibuffer.lsp (the major-mode + command-by-name substrate ADR-0046 designates as the engine).
  • Missing from KEC_EDITOR_SRCS in runtime/CMakeLists.txt — so kec_mkembed never bakes them into the embed header, and the symbols are non-callable on-device despite the files existing on disk.
  • KN86_NEMACS_LIB defaults OFF.

Phase-0 item 4 is: add the mode + minibuffer modules (and any editor modules they transitively require) to the embed manifest, confirm the build path the programs link has them, and add a smoke test that calls define-major-mode and completing-read so the gap can never reappear silently. Per MEMORY, the embed manifest is duplicated across multiple CMakeLists and curated for the 32 KB arena — re-verify on every kec-lisp sync. No program in this wave is buildable until this lands.