Skip to content

CIPHER-LINE Grammar — S-Expression Format

The wire grammar that produces CIPHER-LINE utterances. Cartridges contribute production fragments and vocabulary pools through a cipher-grammar block in the .kn86 container; the runtime ships a baseline grammar covering all five modes and the standard event types.

Related:

  • software/runtime/cipher-voice.md — the engine that executes this grammar (event stream, memory store, mode selector, coherence stack, style controls). The full Grammar Framework lives there; this doc is the contract-surface extract for implementers.
  • adr/ADR-0015 — the auxiliary OLED hardware + render budget the grammar produces text for.
  • adr/ADR-0006 — the .kn86 container that ships cipher-grammar blocks.

Non-terminals are symbols with a leading colon or written as bare symbols inside grammar blocks. By convention:

  • Slot-filling non-terminals (pulled from the current event) use the event-key names: :actor, :target, :location, :from, :to.
  • Memory-drawn non-terminals use :memory-* prefixes.
  • Cart-namespaced non-terminals use the cart tag as a prefix (e.g., :ice-breaker/ice-class), which become callable from cart-supplied productions.

A production maps a non-terminal to a list of weighted alternatives:

(<non-terminal> -> (<weight> <expansion>) (<weight> <expansion>) ...)

<expansion> is a list of literals and non-terminals. Literals are strings. Non-terminals are symbols starting with :. Weights are positive integers; the engine normalizes per production at load time.


(utterance
-> (1 (:mode-observe))
(1 (:mode-annotate))
(1 (:mode-reflect))
(1 (:mode-drift)))
; note: mode selection itself is done by the mode selector (see
; cipher-voice.md §6); the grammar's (utterance) rule is only used
; when a rare path skips the selector (e.g., test harness).
(:mode-observe
-> (3 (:subject) " " (:verb-present) " " (:object) (:coda?))
(2 (:subject) ". " (:verb-present) ".")
(3 (:location) ". " (:heading?))
(2 (:actor) ". " (:verb-past-participle) "."))
(:mode-annotate
-> (3 (:deictic) " " (:affect-word) ".")
(2 (:affect-word) ".")
(2 "that " (:event-kind) ". " (:affect-word) "."))
(:mode-reflect
-> (3 (:deictic) ". " (:memory-fragment) (:coda?))
(2 "same " (:memory-keyword) ". " (:memory-fragment))
(2 (:memory-fragment) ". then. " (:memory-fragment)))
(:mode-drift
-> (3 (:memory-fragment) ".")
(2 (:memory-deictic) ". " (:memory-fragment))
(1 "the " (:memory-keyword) ". " (:memory-when)))
(:deictic
-> (2 "this place")
(2 "here")
(2 "here again")
(1 "same light")
(1 "this hour")
(1 "this corridor")
(1 "this silence")
(1 "same pressure"))
(:coda?
-> (5 "")
(2 ".")
(1 " —")
(1 " " (:affect-word)))
(:affect-word
-> (2 "quiet")
(2 "wrong")
(2 "familiar")
(1 "colder now")
(1 "loud")
(1 "clean")
(1 "dry")
(1 "thin"))
(:heading?
-> (3 "")
(1 "north.")
(1 "back.")
(1 "still."))
(:event-kind
-> ; filled at runtime from event.:type
(1 :event-type-slot))

Cartridges extend :subject, :object, :location, :verb-present, :verb-past-participle, :memory-keyword, and :event-kind with domain-appropriate vocabulary. Cartridges may also add new non-terminals prefixed with their cart tag (e.g., :ice-breaker/ice-class), which become callable from cart-supplied productions.


When the engine expands :subject, :object, :location, :from, :to, it pulls from the triggering event’s corresponding field. If the field is missing, it falls back to the cartridge’s default vocabulary for that slot; if the cart has no default, it falls back to the runtime’s generic vocabulary ("it", "here", "the place", etc.). Slot filling never fails loudly.

:memory-fragment expansion samples a past event and runs it through a reduced grammar — an abbreviated version of :mode-observe — producing a clipped past-tense fragment. This is how reflect and drift speak memory.


Production alternatives are sampled with lfsr_next(cipher_seed) and weight-proportional selection, same mechanism as mode selection (see cipher-voice.md §6). Deterministic. Cart-supplied productions compose into the runtime’s productions at load time per the merge rules below.


Cartridges ship grammar contributions in a cipher-grammar block in the .kn86 container. Full block-form syntax + merge rules are documented in cipher-voice.md §10. Quick summary:

  • Vocabulary pools merge additively. Cart’s :subject words are appended to the runtime’s base pool with default weight 1.
  • Productions merge by weight. A cart-supplied production for an existing non-terminal is appended with its declared weight. The engine normalizes across the combined set at merge time. Runtime-shipped productions carry weight 10 by default — a cart adding a weight-2 production is a minority voice, not a dominant one.
  • Cart-namespaced non-terminals (prefixed with the cart tag) are isolated to that cart’s productions.

On cart unload, the cart’s overlay is rolled back from the active grammar tables. See cartridge-lifecycle.md for the lifecycle event ordering.