ADR-0022: Keyboard layout finalization (legends, shifts, multi-tap)
Notion task: GWP-284 — Draft ADR-0022 finalizing 30-key keyboard layout
Context
Section titled “Context”The KN-86 has carried a 31-key layout (14 function + 16 numpad + 1 TERM) since the Pi pivot, but several pieces remained provisional:
- Right-column legends. The numpad’s fourth column was specified as
÷ × − +(calculator-style arithmetic) — workable for math, but it left high-frequency Lisp punctuation (/,;,:,") unreachable except by the multi-tap1-key punctuation cycle. Lisp surface code wants single-press access to those glyphs. - Bottom row outer keys. The bottom row’s outer two slots were left as digit-grid padding. Phone-authentic layout puts
*and#there, but those slots also need to surface the parentheses(and)somewhere reachable — under the prior spec,(and)were not typeable except by hunting through punctuation cycles where they do not actually live. - Key-1 secondary glyph. Earlier drafts implied an
@shift on the1key (phone convention). For a Lisp-authoring deck, backtick (`, the quasiquote glyph) is far higher-frequency than@. - Key-9 letter group. Drafts of
ADR-0016§6 used bothWXYZ(Nokia convention, four letters) andXYZ(three) at different points.WXYZis the canonical Nokia mapping and the2026-04-21-nemacs.mdbrief already used it. case-toggleanddeleteplaceholders.ADR-0016§6 lines 100–101 left two bindings unresolved during multi-tap literal entry: a case toggle (proposed:/) and a delete key (proposed:*), both flagged “final during implementation.” With the new right-column and bottom-row legends locked, both keys now carry confirmed primary glyphs and the resolution can be made on the same surface that hosts them.
Josh and the PM iterated the layout in conversation on 2026-04-26 and Platform Engineering captured the agreed-upon legends in a screenshot (docs/device/hardware/keyboard-layout-final-2026-04-26.png). This ADR ratifies the screenshot, resolves the ADR-0016 §6 placeholders, and runs the project-wide doc-sweep required by CLAUDE.md Spec Hygiene Rule 3.
Forcing functions
Section titled “Forcing functions”- The QMK keymap (KBD-04,
firmware/kn86-keyboard/keymap.c) needs the legend manifest before the keycap art order is placed. Blank caps ship on v0.1; UV-printed legends are a polish pass and the print file needs to be authoritative. - Cartridge authors are starting to spec inputs against the layout. Without locked legends, every gameplay spec re-debates whether
/is a primary or a shift, and whether(and)are typeable. ADR-0016§6 has been awaiting placeholder resolution since 2026-04-24. Two more weeks without resolution and the placeholders start propagating into firmware code as TODOs.
Constraints
Section titled “Constraints”- Physical key count is unchanged. 30 nOSh-routed keys + TERM (31 physical).
ADR-0018and the canonical Keys row are the source of truth — this ADR does not move them. - Function-block (left side) layout is unchanged. All 14 left-side keys keep their position, function, and Lisp semantics. The single cosmetic change is that the keycap legend for
LAMBDAis printed asFN; the Lisp slot, key code, and runtime semantics remainLAMBDA. - Multi-tap letter mapping on keys 2–8 is unchanged. This ADR confirms
WXYZon key 9 and the1-key punctuation cycle fromADR-0016§6; keys 2–8 are not touched. - Row 0 / Row 24 ownership is out of scope. Per
ADR-0015, Row 0 is the firmware status bar and Row 24 is the firmware action bar. The Row 0/24 question was raised on 2026-04-26 and explicitly left alone. - No restating canonical hardware values. Per
CLAUDE.mdSpec Hygiene Rule 1, this ADR references the Keys row and does not re-derive switch / keycap / controller values.
Decision
Section titled “Decision”We finalize the 31-key keycap legend manifest, the right-column / bottom-row primary + shift pairings, the 1-key secondary glyph, the key-9 letter group, and resolve the ADR-0016 §6 line 100–101 placeholders for case-toggle and delete.
Canonical visual reference
Section titled “Canonical visual reference”
The image above is the source of truth for the printed legends. Where this ADR’s text and the image disagree, the image wins and this ADR is wrong; fix the ADR.
1. Function block — 14 keys, left side (legends only; semantics unchanged)
Section titled “1. Function block — 14 keys, left side (legends only; semantics unchanged)”The 14 function keys retain their physical positions and Lisp semantics. Printed legends are:
[QUOTE] [ CONS] [ NIL ] [ FN ][INFO ] [ CAR ] [APPLY] [ SYS ][LINK ] [ BACK] [ CDR ] [ ATOM][========= EVAL =========] [EQ ] [TERM]FNis the printed legend for the canonicalLAMBDAkey. Cosmetic alias for keycap engraving — chosen for visual brevity (2 chars vs. 6) and operator legibility from arm’s reach. The Lisp slot remains'lambda, the key code remainsKN86_KEY_LAMBDA, and every doc string / handler signature continues to refer to “LAMBDA” as the key name.EVALis a 1.75U keycap centered over Col 0;EQis 1U at Col 2;TERMis a 1.5U keycap at Col 3 (matrix position (3,3) perdocs/software/runtime/input-dispatch.md§1A). All other keys on the deck are 1U.- Color coding (per
docs/software/runtime/input-dispatch.md§2) is unchanged: amber for Lisp primitives (CAR, CDR, CONS, NIL, ATOM, EQ); white for action verbs (EVAL, QUOTE, FN/LAMBDA, APPLY); red for navigation (LINK, BACK); gray for nOSh runtime (INFO, SYS, TERM).
2. Numpad — 16 keys, right side (phone layout, with legends + shifts)
Section titled “2. Numpad — 16 keys, right side (phone layout, with legends + shifts)”Update — partially superseded by ADR-0031 §3.1 (2026-06-06): The Ferris Sweep right half cannot host a 4-column phone numpad — it has 5 columns × 3 rows + 2 thumbs = 17 positions. The numpad reorganizes around the Sweep geometry as a 3×3 digit cluster (1-2-3 / 4-5-6 / 7-8-9) on the inner three columns, with 0 on the inner thumb (RT0), ENT on the outer-pinky bottom (R20), and TERM on the outer thumb (RT1). The
* #outer-corner phone glyphs move to the SHIFT layer. The right-column punctuation primaries (/ ; -) and,.move to the outer two columns. The digit-to-letter binding for Nokia multi-tap (§6: 2=ABC … 9=WXYZ) is fully preserved — letters travel with digit identity, which is what carries muscle memory. The phone-shape framing inADR-0016§5 is amended accordingly. See ADR-0031 §3.1 for the as-adopted Sweep layout and §3.3 for the newdeletebinding.Update — shift-secondary manifest KEC-completed by ADR-0044 (2026-06-21): The shift-secondary set in the table below (and the §3 right-column rationale) predates the KEC Lisp standard (ADR-0037) and left
'<>=?unreachable (</?hard blockers). ADR-0044 §2 drops the KEC-irrelevant shift glyphs! $ % ^ & #and promotes< > = ? '(keys4-5-6→< = >), and adds an unprinted L2 for the demoted long tail. The §51-key backtick shift is preserved. The right-column semantic pairs (/→",;→:,-→+) survive. See ADR-0044 §2 for the as-adopted shift manifest. Phone-style numpad perADR-0016§5. Each key has a printed primary legend and (where applicable) a printed shift secondary above it on the keycap, plus the Nokia multi-tap letter group above-right for keys 2–9.
| Position | Primary | Shift | Multi-tap letters |
|---|---|---|---|
| R1C1 | 1 | ` (backtick) | — |
| R1C2 | 2 | — | ABC |
| R1C3 | 3 | — | DEF |
| R1C4 | / | " | — |
| R2C1 | 4 | — | GHI |
| R2C2 | 5 | — | JKL |
| R2C3 | 6 | — | MNO |
| R2C4 | ; | : | — |
| R3C1 | 7 | — | PQRS |
| R3C2 | 8 | — | TUV |
| R3C3 | 9 | — | WXYZ |
| R3C4 | - | + | — |
| R4C1 | * | ( | — |
| R4C2 | 0 | . | — |
| R4C3 | # | ) | — |
| R4C4 | ENT | — | — |
Position-and-key-code invariant. The KN86 key code KN86_KEY_PAD_<N> continues to map to the digit <N> regardless of physical row order — i.e. the QMK firmware emits KC_KP_1 from the top-left numpad position, the Linux HID layer translates to SDL_SCANCODE_KP_1, and the runtime resolves to KN86_KEY_PAD_1. This ADR changes printed legends and the secondary glyphs that those keys produce in shifted-or-cycled contexts; it does not move scancodes or rename key codes.
Naming legacy. The right-column key codes are still KN86_KEY_PAD_DIV, KN86_KEY_PAD_MUL, KN86_KEY_PAD_SUB, KN86_KEY_PAD_ADD — these names predate the legend swap and are preserved (mass rename would touch FFI surface, gameplay specs, the Lisp slot table per ADR-0012, and the QMK keymap audit). The names denote position, not engraved character. The bottom-row code KN86_KEY_PAD_DOT likewise denotes position; under the new layout the printed primary at R4C2 is 0, with . as the shift secondary, and the position now electrically corresponds to KN86_KEY_PAD_0. The two outer bottom-row keys (*/( and #/)) ride on KN86_KEY_PAD_DOT (R4C1) and KN86_KEY_PAD_ENTER-adjacent (R4C3); a follow-up firmware story (see Implementation Queue) will adjust the position-to-code mapping if required by the QMK keyboard.json. The legend manifest in §1 of this ADR is canonical for printing; the code-name mapping is canonical for software dispatch.
3. Right-column rationale (replacing ÷ × − +)
Section titled “3. Right-column rationale (replacing ÷ × − +)”The pre-2026-04-26 spec assigned R1–R3 of the right column to ÷ × − (with + on R4C4). That arrangement bought a calculator’s worth of arithmetic at the cost of every high-frequency Lisp punctuation glyph. The new column trades pure arithmetic for Lisp punctuation while preserving access to all four arithmetic operators:
+is still reachable: shift--on R3C4 produces+.*is now bottom-row primary at R4C1 — direct, single-press./is now top-row primary at R1C4 — direct, single-press.÷and×glyphs are no longer printed on the keycaps. Cartridges that need division and multiplication operators in surface text use/and*(Lisp / C / mathematics convention, no readability loss).
The right-column shifts are paired with their primaries by semantic affinity rather than alphabetical accident:
- R1:
/primary,"shift — both string-related (regex / path-separator on the primary, string-quote on the shift). - R2:
;primary,:shift — list metadata (statement separator on the primary, keyword qualifier on the shift, both Lisp idiom). - R3:
-primary,+shift — arithmetic pair.
4. Bottom-row rationale (* ( / 0 . / # ) / ENT)
Section titled “4. Bottom-row rationale (* ( / 0 . / # ) / ENT)”Phone-authentic outer keys: * and # sit where they sit on every phone keypad. The shift secondaries give us the parentheses, which were unreachable on the prior spec (the 1-key punctuation cycle from ADR-0016 §6 cycles . , ' " : ; ! and does not contain ( or )).
0 carries . as its shift, recovering the decimal-point glyph that the prior spec’s PAD_DOT key supplied. Numeric data entry (Black Ledger transaction lines, Cipher Garden key guesses, LFSR predictions) gains . via shift instead of by hunting for a dedicated key.
5. Key-1 secondary: backtick (`)
Section titled “5. Key-1 secondary: backtick (`)”Backtick replaces @ as the printed shift on the 1 key. Three reasons:
- Lisp frequency. Backtick is the quasiquote glyph (
(foo ,bar)macro templating). For a Lisp-authoring deck, this is the highest-frequency punctuation glyph not already in the multi-tap punctuation cycle (. , ' " : ; !) or on a function key. - Single-use semantics. Quasiquote is a leading character — operators don’t cycle through it. Single-press access pairs naturally with the comma already in the punctuation cycle, so
\(foo ,bar)` macro templating types from one finger zone (1, 1, 1, comma). @is rarely used in Lisp. Splice-unquote (,@) is the only common idiom and it composes from comma + at on a US keyboard already; the deck rarely needs@standalone, and when it does, an in-multi-tap shift-cycle entry is acceptable.
6. Key-9 letter group: WXYZ (Nokia convention)
Section titled “6. Key-9 letter group: WXYZ (Nokia convention)”The Nokia multi-tap mapping per ADR-0016 §6 places four letters on the 9 key: W, X, Y, Z. The 2026-04-21-nemacs.md plan already used this; this ADR confirms it as canonical and removes any drafts that read XYZ (three letters).
7. Case-toggle and delete (closing ADR-0016 §6 lines 100–101)
Section titled “7. Case-toggle and delete (closing ADR-0016 §6 lines 100–101)”Both placeholders resolve to context-polymorphic dispatch consistent with ADR-0016 §3:
- Case toggle:
/(primary at R1C4).- In default scopes (cart gameplay, REPL prompt outside literal entry, navigation): types the literal character
/. - In
:nemacs-literaland:prompt-textkeymap scopes (multi-tap literal entry active): toggles case of the most recently committed character, then case of subsequent letters until toggled again.
- In default scopes (cart gameplay, REPL prompt outside literal entry, navigation): types the literal character
- Delete:
*(primary at R4C1).- In default scopes: types the literal character
*. - In
:nemacs-literaland:prompt-textkeymap scopes: deletes the most recently committed character (or the in-progress multi-tap cycle character if mid-cycle).
- In default scopes: types the literal character
The active scope is always visible — Row 24 (firmware action bar, ADR-0015) and CIPHER-LINE Row 1 (modeline, ADR-0016 §4) both render the current scope label, so the operator never has to guess which behavior is active. This is the same context-polymorphism pattern that runs through ADR-0016 §3 (cursor position determines what primitive keys mean) — applied to two more keys whose default identity is “type this glyph” and whose multi-tap-literal identity is “edit the buffer.”
The two literal-scope keymaps are the only scopes affected. REPL navigation, nEmacs nav, cart gameplay, and the bare-deck terminal all see / and * as the printed character.
Options Considered
Section titled “Options Considered”Option A: Keep the prior ÷ × − + right column (status quo) (rejected)
Section titled “Option A: Keep the prior ÷ × − + right column (status quo) (rejected)”Preserves a calculator’s worth of arithmetic glyphs.
Rejected because: the deck’s primary surface text is Lisp source and operator-typed strings, not arithmetic expressions. ÷ and × are not used in any Lisp / C / mathematics surface text within the project; / and * are the operators every cart and the REPL actually consume. Pure-arithmetic legends sacrifice the high-frequency punctuation of /, ;, :, " for symbols nobody types.
Option B: Modifier-key approach (dedicated SHIFT) (rejected)
Section titled “Option B: Modifier-key approach (dedicated SHIFT) (rejected)”Add a SHIFT modifier key to the function block; print primaries only on the keycaps; shifts produced by SHIFT + key.
Rejected because: the function block is full (14 keys plus TERM, with EVAL at 1.75U and TERM at 1.5U). Adding a SHIFT means losing a Lisp primitive or a system key. The keyboard authoring boundary in ADR-0018 (“keyboard firmware stays dumb”) would also have to relax to support SHIFT layering, and v0.1 explicitly does not use QMK layers. Printed shift legends on the keycaps deliver the same affordance without a modifier key — operators read the secondary directly.
Option C: Context-sensitive primary (current approach) (ACCEPTED)
Section titled “Option C: Context-sensitive primary (current approach) (ACCEPTED)”Single printed primary + shift on each key, with / and * carrying scope-dependent secondary behavior in :nemacs-literal / :prompt-text only.
Chosen because: preserves the Lisp punctuation density needed by the surface text, preserves all four arithmetic operators, recovers parentheses, resolves the ADR-0016 §6 placeholders without adding keys or modes, and aligns with the context-polymorphism pattern already proven in ADR-0016 §3.
Trade-off Analysis
Section titled “Trade-off Analysis”- Cognitive overhead. Operators in default scope see
/and*and type those characters. Operators in multi-tap literal entry see the same keys edit the buffer. The cost is one mode-aware mental model — paid once, free thereafter, and the active scope is always rendered on-screen so there is no hidden state. The win is two free editing affordances on keys that were already on the deck. - Doc surface. The right-column legend swap and the bottom-row legend additions touch
ADR-0016,docs/software/runtime/input-dispatch.md,docs/device/hardware/keyboard.md, andCLAUDE.md. All updated in this PR (see Documentation Updates). - Firmware impact.
KN86_KEY_PAD_DIV/_MUL/_SUB/_ADDcodes stay; their printed legends change. The QMK keymap source (firmware/kn86-keyboard/keymap.c) does not need a code change for the legend swap — the firmware still emits the same scancodes, and the runtime still resolves them to the same KN86 codes. Shift-secondary dispatch (producing"/:/+/(/)/./ backtick when the operator presses-and-holds shift, or however the shift gesture is finalized) is a follow-up firmware story called out in the Implementation Queue. - Hardware impact (legend printing). The keycap art order for the UV-print pass needs the new manifest. v0.1 ships with blank caps per
ADR-0018§Construction, so this ADR has zero impact on the v0.1 build itself; it locks the manifest for the polish-pass print job. - Cost we accept. No
÷or×glyphs on the printed keycaps; the operator types division and multiplication using/and*in surface text. The two-glyph dual-identity for/and*is a minor mental-load tax in exchange for recovering case-toggle and delete without adding keys.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Lisp punctuation density.
/,;,:,"reachable in single-press / shift; backtick reachable as1-shift. The deck’s printed surface now matches the language operators actually write on it. - Parentheses are typeable.
(and)reachable as*-shift /#-shift. Cart authors can compose s-expressions on the bare numpad without resorting to the punctuation cycle (which never carried(or)in the first place). case-toggleanddeleteresolved without new keys.:nemacs-literaland:prompt-textscopes get two editing affordances on keys that already exist; default scopes are unaffected.- Manifest locked for keycap art. The polish-pass UV-print order is unblocked.
Negative / Accepted costs
Section titled “Negative / Accepted costs”- Two keys carry context-polymorphic behavior.
/and*change identity between default scopes and multi-tap literal entry. Mitigated by the always-visible scope label on Row 24 and CIPHER-LINE Row 1. - Code-name vs. printed-legend mismatch on the right column.
KN86_KEY_PAD_DIVis printed/, etc. Documented inline (§2 of this ADR’s Decision); a future pure-rename ADR could align names if the cost of the touch is ever worth paying. - Doc surface to sweep.
ADR-0016§6,docs/software/runtime/input-dispatch.md§3D,docs/device/hardware/keyboard.md, andCLAUDE.mdKeys row all need touch-up. All updated in this PR per Spec Hygiene Rule 3.
What we’ll revisit
Section titled “What we’ll revisit”- Shift gesture finalization. The shift secondary is rendered on the keycap; how the operator invokes the shift (long-press, hold-and-tap, or a two-key chord) is a firmware-side decision that lands in the input-dispatch foundation task (
ADR-0016Implementation Queue). The legend manifest does not commit to a particular gesture — only that the secondary glyph is reachable. @accessibility. If a future cart genuinely needs@as a standalone glyph (currently no cart in the launch set does), it can be added to the1-key punctuation cycle alongside the current 7 punctuation glyphs.KN86_KEY_PAD_*rename. If the legend / code-name mismatch ever becomes a real source of confusion (current assessment: it doesn’t, the names denote position), a rename ADR can land. The cost is FFI surface, gameplay specs, the Lisp slot table perADR-0012, and the keymap audit. Not paying that cost in this ADR.
Documentation Updates (REQUIRED — part of the decision, not aspirational)
Section titled “Documentation Updates (REQUIRED — part of the decision, not aspirational)”This ADR lands in a single PR that includes the following updates per CLAUDE.md Spec Hygiene Rule 3:
-
docs/adr/ADR-0022-keyboard-layout-finalization.md— this file. -
docs/device/hardware/keyboard-layout-final-2026-04-26.png— canonical visual reference (renamed from the macOS auto-generated filename for Markdown link safety). -
CLAUDE.md— Canonical Hardware Specification “Keys” row extended with a one-line reference to this ADR. -
docs/adr/ADR-0016-nemacs-repl-input-model.md§6 — placeholders on lines 100–101 closed; cross-link to ADR-0022; key-9 confirmed asWXYZ; right-column reference updated. §Known Unknowns items 2–3 (Case toggle / Delete key) marked closed by this ADR; item 4 extended with1-key backtick shift. -
docs/software/runtime/input-dispatch.md§1 + §3D + §8D + §9 — numpad reference table refreshed with primary + shift + multi-tap columns; ASCII grid art in §1 updated to show/ ; - * 0 # ENTinstead of÷ × − + . 0 ENT +; laptop keymap right-side annotation refreshed; design invariant #8 cross-references ADR-0022. Cross-link to ADR-0022 throughout. -
docs/software/runtime/bare-deck-terminal.md— APPENDIX keycap diagram updated to the ADR-0022 manifest; cross-link added. -
docs/software/runtime/prototype-architecture.md— Key Layout (31 keys) ASCII diagrams refreshed;LAMBDA → FNprint-alias note added; cross-link added. -
kn86-emulator/assets/laptop.keymap— header comment ASCII layout reference refreshed; row labels updated to phone-layout legends per ADR-0022. -
docs/device/hardware/keyboard.md— embed canonical visual reference; cross-link to ADR-0022; canonical-layout-reference section enumerates the manifest highlights. -
firmware/kn86-keyboard/keymap.cheader comment — phone-vs-calculator diagram refreshed with the new manifest; right-column / bottom-row shift secondaries documented; code-name vs. printed-legend invariant explained. -
firmware/kn86-keyboard/README.md— phone vs. calculator layout diagram refreshed; Nokia multi-tap key roles section updated with ADR-0022 §7 closure for case-toggle and delete; cross-references section extended. -
docs/adr/README.md— chronological ADR index extended. - Project-wide grep sweep for stale references — see PR description for grep counts.
Delegated (queued in Notion under the KN86 tag, blocked on this PR merge):
-
firmware/kn86-keyboard/keymap.cheader comment — refresh the calculator-vs-phone diagram with the new legend manifest. The QMK source itself does not change for the legend swap (scancodes are unchanged); the diagram does. - Shift-gesture firmware foundation — pick the long-press / chord gesture for invoking shift secondaries; wire dispatch in the input pipeline. Owner: C Engineer, depends on this PR merge.
- Keycap UV-print art order — assemble the legend manifest from §1–§2 of this ADR’s Decision into an art file and submit. Owner: Hardware, on the polish-pass cadence (post-v0.1).
Implementation Queue
Section titled “Implementation Queue”Tracked in Notion Tasks DB under the KN86 tag:
| Task | Owner | Priority | Depends on |
|---|---|---|---|
| QMK keymap header comment refresh (legend manifest) | Platform Engineering | Low | This PR merge |
| Shift-secondary gesture + dispatch (firmware) | C Engineer | Medium | This PR merge + ADR-0016 input foundation |
Case-toggle + delete handlers in :nemacs-literal / :prompt-text | C Engineer | Medium | ADR-0016 literal-entry foundation |
| Keycap UV-print art order (polish pass) | Hardware | Low | Post-v0.1 cadence |
Cart gameplay-spec sweep — references to ÷ / × (none expected) | Gameplay Design | Low | This PR merge |
Known Unknowns / Follow-ups
Section titled “Known Unknowns / Follow-ups”- Shift gesture (long-press vs. chord).
The keycap manifest renders the shift glyph; the gesture for invoking it lands in theClosed by ADR-0031 §3.2 (2026-06-06): LSHIFT is a physical thumb key (LT1, left outer thumb) on the Ferris Sweep; QMKADR-0016input foundation. Conservative starting default: long-press (≥ 400 ms, matching theKEY_LONG_PRESSthreshold fromADR-0016§9). Revisit during firmware bring-up.MO(L1)momentary layer activation. No long-press, no chord, no timing tax. KN86_KEY_PAD_*code-name alignment. Names denote position, not legend. Rename only if the mismatch becomes a real confusion vector. Currently no.@accessibility. Not on the printed manifest. Add to the1-key punctuation cycle if a cart needs it.- R4C1 / R4C3 position-to-code mapping. The bottom-row outer keys carry new printed primaries (
*and#); the QMK keyboard.json position-to-code mapping (currentlyKC_PDOTat R4C1 andKC_PENT-adjacent at R4C3) needs verification against the new physical positions when KBD-01 PCB arrives. Documented as a firmware follow-up.
Narrative (for design history)
Section titled “Narrative (for design history)”The right-column swap was the unlock. For most of the project, the numpad’s fourth column has read as a calculator — ÷ × − + arranged in the conventional vertical-arithmetic order. That arrangement was a holdover from when the deck was framed as “a Z80 with a 4-row numpad,” and the cost of preserving it through every redesign since the Pi pivot has been the absence of /, ;, :, " from the deck’s directly-typeable surface. Lisp source code lives on punctuation; the prior spec made operators chase those glyphs through a multi-tap cycle that sat one finger over from the 0 key. With the right column reassigned to Lisp punctuation and the four arithmetic operators preserved (/ and * as primaries on R1 / R4; + as a shift on -; - as a primary on R3), the deck’s printed surface and the language operators write on it finally agree.
The bottom-row reassignment is the smaller half of the same idea. * and # are phone-authentic — they sit where every phone-keypad operator’s muscle memory expects them. The shift secondaries deliver the parentheses, which the prior spec left untypeable except by hunting through a punctuation cycle that did not actually contain them. That gap had been quietly papered over in every gameplay spec by assuming the operator would compose s-expressions in the REPL through some unstated combination of CONS and CAR-applied-to-quoted-forms; with ( and ) printed on the keycaps as shifts, that fiction can drop and operators can just type the parens.
The case-toggle and delete placeholders had been nagging at ADR-0016 since 2026-04-24. The temptation in those situations is always to add a key — split a 1U into two 0.5U slabs, or steal a function key’s slot for a modifier. Both options were on the table during the conversation that produced this ADR. Both lose. The deck’s footprint is fixed, the function block is full, and the cleanest resolution turned out to be the same context-polymorphism that runs through the rest of ADR-0016 §3: the cursor position (or in this case, the active keymap scope) determines what the key means. / types / when you’re at the REPL prompt; / toggles case when you’re inside a multi-tap literal-entry buffer. The operator never has to guess which because the active scope is rendered on Row 24 and on CIPHER-LINE Row 1. There is no hidden state.
The thing that makes this ADR easy to live with is that none of it is load-bearing for anything below the surface. The QMK firmware emits the same scancodes it always has. The Linux HID layer translates them the same way. The runtime resolves them to the same KN86_KEY_* codes. The Lisp slot table per ADR-0012 is unchanged. The FFI surface from ADR-0005 is unchanged. What changes is what gets printed on the keycaps, what the shift gesture produces, and what two keys do inside one specific literal-entry scope. Five kinds of update across four documents — and the deck is finally ready to print.