Skip to content

Language policy

All code is authored in KEC Lisp. A C file is justified only when it is the FFI layer itself. This is a standing project rule, stated by the product owner on multiple occasions and codified here on 2026-08-02. It binds every contributor and every agent brief.

C exists in this tree to give Lisp its primitives. Everything above the primitive layer is KEC Lisp: gameplay, screens, programs, carts, mission and economy logic, generation, scheduling policy, and test cases.

A new .c file, or new logic inside an existing one, is acceptable only when the deliverable is the FFI layer:

  • binding seams (*_bind functions, bridge primitives in nosh_lisp_bridge.c);
  • glue that crosses the boundary into Lisp (event-bus or scheduler callbacks whose body is an invocation of a Lisp entry point);
  • device drivers and realtime kernels that primitives expose (the framebuffer blitter, the PSG synthesis inside the audio callback, SPI/UART/OLED transport);
  • the interpreter embedding and arena plumbing;
  • thin test runners (lifecycle only; cases live in .lsp suites, per the testing rule in documentation-standard.md and the repository conventions).

Logic never goes in C again. When a change touches an existing C core that holds logic, the change migrates that logic to Lisp behind a thin seam rather than extending the C.

C files carrying logic that predates this codification, tracked for migration under GWP-777:

FileLogic to migrate
runtime/src/board_screen.cCountdown, verb policy, row/detail formatting

Migrated under GWP-777: the mission-board generator (runtime/system-image/lib/mission/board-gen.lsp: template pool, eligibility gate, tier and instance-count tables, threat clamp, payout formula). mission_board.c holds mechanisms only (board memory, event subscriptions, the 32-bit LFSR behind the gen-draw seam, the accept sink, marshalling). The tier and broke-bonus math the old board_screen.c row named reached the screen through mission_board.c and moved with it; the remaining row is the screen-side policy board_screen.c itself holds.

Migrated already on the ICE Breaker sprint stack (PRs #202 to #204): the mission-end pricing (runtime/system-image/lib/mission/pricing.lsp), the contract reader API (mission/contract.lsp over one raw primitive), and the sweep arming policy (sweep/sweep.lsp); the corresponding C files hold mechanisms only. Migrated since: the objective-graph policy (mission/objectives.lsp, GWP-750, PR #208).

Migrated on the map’s wave 1 (GWP-808): the baseline selection-weight policy (mission/baseline.lsp), the first-boot tutorial policy (sys/tutorial.lsp), the headless demo-deck seed (deck/demo-seed.lsp), the NoshAPI draw helpers (cart/stdlib.lsp), the bare-deck display state (deck/deck.lsp), and the aux bounty and dialog logic (deck/aux.lsp); the corresponding C files (nosh_baseline.c, tutorial.c, tutorial_wiring.c, deck.c, nosh_stdlib.c, deck_screen.c, deck_aux.c) hold mechanisms only.

The full source-by-source plan (dispositions, waves, the retained-C inventory) is docs/software/runtime/c-to-lisp-migration-map.md (GWP-781); wave 1 is DONE there.

The seams themselves (board_screen_bind, the accept sink, the event publishes) stay in C: they are the FFI layer.

  • Every engineering brief carries this rule.
  • Review rejects a PR that adds C logic outside the FFI layer.
  • The capability boundary argument (“the engine owns mission truth”) governs which CONTEXT may mutate state; it does not license writing the algorithm in C. The binding set enforces ownership; the implementation language is free to be Lisp.