Skip to content

ADR-0044: KEC-complete keyboard layout — SHIFT reorganization, unprinted L2 symbol layer, EQ authoring binding

Notion task: GWP-554 — Issue ADR-0044: KEC-complete keyboard layout


ADR-0031 §3.1 finalized the Ferris Sweep 34-key layout with two bound layers — L0 BASE and L1 SHIFT (MO(L1) on the left outer thumb) — and left L2 reserved and unbound (“Reserved for v2 cart-defined verbs or an optional ALPHA layer”). ADR-0022 had earlier reassigned the numpad’s fourth column away from calculator arithmetic (÷ × − +) toward Lisp punctuation, and locked the shift secondaries. That work predated the KEC Lisp language standard (ADR-0037): the shift layer was populated with the conventional ASCII symbol set, not with the specific punctuation KEC Lisp actually requires.

Josh derived the unique punctuation set KEC Lisp requires19 characters 21 characters (corrected per Amendment 2026-07-10 — the original derivation counted 19, missing ! and %) in two roles:

  • Reader / structural (special-cased by the Fe kernel reader): ( ) " . ; ' ` , @ (only as ,@) \ (only inside strings).
  • Identifier / operator (baked into kernel symbol names, no word alias): < (<, <= — kernel primitives), > (>, >=, and -> in number->string etc.), = (=, ==, /=, <=, >=), + - * / (arithmetic; - is also kebab-case in nearly every multi-word name), ? (the entire predicate family — nil? pair? number? …), : (keyword symbols / type-of results like :number); plus, per Amendment 2026-07-10: ! (the bang-mutation suffix — vector-set! matrix-set! blob-set! hash-set! hash-del! vector-fill! matrix-fill! set-seed!) and % (the private-name prefix on Core internals — %append %qq … — and the format directive character — %d %x …).

Characters not required: ! # & % ^ $ ~ | # & ^ $ ~ | (corrected per Amendment 2026-07-10) — none are reader-special and none appear in any standard name. (! in particular: KEC has no bang-mutation conventionset / setcar are words.) Withdrawn: KEC does carry the bang-mutation convention — the kernel ships eight bang primitives and kn-86’s own authored Lisp uses bang names throughout (add!, set-cell-field!, repl/submit!, …). See Amendment Log.

  1. The keyboard cannot type idiomatic KEC Lisp as it stands. Intersecting the 19 required characters (as then derived; 21 per Amendment 2026-07-10) with the as-shipped layout (BASE: , / . ; -; SHIFT: ` ! @ " ) $ % ^ : ( * & # + \) leaves five required characters unreachable: ' < > = ?. Two of them are hard blockers with no in-language workaround:
    • << and <= are Fe kernel primitives, punctuation-only. You cannot write any ordering comparison without it.
    • ? — the entire stdlib predicate family (nil?, pair?, number?, bound?, even?, …) carries ? in the name. You cannot call a standard predicate without it. = (equality / <= / >=), > (ordering / -> converters), and ' (quote shorthand) round out the gap.
  2. The shift budget is being spent on the wrong characters. The L1 SHIFT layer currently dedicates six slots to KEC-irrelevant ASCII! $ % ^ & #. Reclaiming those for the five missing characters is a net −1 (one slot to spare). The fix lives inside the existing layer count; the problem was allocation, not capacity.
  3. GWP-407 (QMK keymap retarget) is about to encode the layout. The keymap implementation task targets ADR-0031 §3.1 + ADR-0032 §3 as written. Implementing the §3.1 SHIFT manifest verbatim would bake the KEC gap into firmware. This ADR must land before the keymap is authored.
  4. The keycap legends predate KEC Lisp. The 14 function-block legends (CAR / CDR / CONS / NIL / QUOTE / EVAL / APPLY / FN / ATOM / EQ + the runtime verbs INFO / LINK / BACK / SYS) were chosen as Lisp primitives + gameplay verbs before the KEC vocabulary was named. They need an audit against KEC’s actual kernel symbols.
  • The T9 dialpad is preserved. The Nokia digit-to-letter binding (2=ABC … 9=WXYZ, ADR-0016 §6) and the physical 3×3 digit cluster + 0-on-thumb are not touched. This ADR moves only shift secondaries and an unprinted layer, never a base digit.
  • The four thumb keys are locked. ADR-0032 §4: none of LT0 EVAL, LT1 LSHIFT, RT0 0, RT1 TERM may have its tap binding sacrificed. The L2 activation gesture must respect this.
  • The function block is preserved. All 14 left-half functions keep their position, Lisp semantics, and gameplay verbs (load-bearing across the 17 module specs in lisp-paradigm.md). No keycap-art changes.
  • Not every layer is printed. Per Josh’s direction, L2 is muscle-memory only — the deck leans on the L0/L1 printed surface plus Nokia T9 multi-tap and aggressive token prediction (TAGS, ADR-0009 / ADR-0016 §7) to make authoring natural, rather than printing a third legend tier on the caps.
  • Spec Hygiene Rules 1–3 (canonical-spec.md): one canonical statement (the Keys row), no forked specs, full doc cascade in this PR.

A three-layer keyboard model in which every KEC-required character is reachable in ≤1 hold and printed on a keycap, the rare ASCII long tail lives on an unprinted L2, and the function-block legends are confirmed (with one authoring-binding clarification).

LayerPrinted?ActivationCarries
L0 BASEyes (primary legend)defaultT9 dialpad, 5 punctuation primaries, function block, thumbs — unchanged from ADR-0031 §3.1
L1 SHIFTyes (secondary legend)hold LSHIFT (LT1, MO(L1)) — unchangedwith L0’s five primaries, 20 of the 21 KEC-required characters reachable in ≤1 hold (reorganized — §2; ! on the 9-key shift per Amendment 2026-07-10)
L2 SYMno (muscle memory)hold LSHIFT + TERM together (tri-layer — §3)the rare ASCII long tail: `# & $ % ^ ~
L2 QWERTY (added by ADR-0062 §1)yes (second top corner)press abc on L1an unmodified QWERTY; every modifier sits on a thumb, so no letter is displaced. LT1 is renamed LAYER by ADR-0062 §2.

BASE right half is unchanged (reproduced for reference; digits + 5 punctuation primaries):

InnerIdx Index Middle Ring Pinky
Top 1 2 3 / ,
Home 4 5 6 ; .
Bottom 7 8 9 - ENT
Thumb 0 (inner) TERM (outer)

NEW L1 SHIFT right half — drop the six glyphs then judged KEC-irrelevant (! $ % ^ & #), promote < > = ? ' (as amended 2026-07-10: ! is KEC-required after all and takes the 9-key shift slot that was the spare):

InnerIdx Index Middle Ring Pinky
Top ` ? @ " )
Home < = > : (
Bottom * ' ! + \
Thumb . (inner) TERM (outer) (9-key shift = ! per Amendment 2026-07-10)

Per-key change ledger (the inner three columns are the digit keys; their shift secondary changes — the base digit never moves):

Position (base key)Old shiftNew shiftNote
InnerIdx-Top (1)``kept — backtick/quasiquote, consistent with ADR-0022 §5
Index-Top (2)!?predicate suffix — sits directly above =
Middle-Top (3)@@kept — only as ,@ splice
InnerIdx-Home (4)$<
Index-Home (5)%=
Middle-Home (6)^>
InnerIdx-Bottom (7)**kept
Index-Bottom (8)&'quote shorthand
Middle-Bottom (9)# !spare (reserved) ! per Amendment 2026-07-10 — the bang-mutation suffix (vector-set!, hash-set!, set-seed!, …)
Ring/Pinky columns" ) : ( + \" ) : ( + \unchanged — the ADR-0022 §3 semantic pairs (/", ;:, .(, ,), -+, ENT→\) all survive

The 4-5-6 → < = > home-row triplet is the centerpiece: the comparison operators read left-to-right on the strongest fingers, and the compounds roll naturally — <= (4→5), >= (6→5), == (5→5), /= (/base→5), -> (-base→6), ,@ (,base→3).

Result (as amended 2026-07-10): 20 of the 21 KEC-required characters are on a printed cap — BASE , / . ; - + SHIFT ` ? @ " ) < = > : ( * ' + \ ! — with % deliberately on the unprinted L2 (§4). The two hard blockers < and ? are first-class. The L1 SHIFT left half remains passthrough (function keys retain their base KN-86 keycodes while LSHIFT is held), unchanged from ADR-0031 §3.1.

3. L2 activation — LSHIFT + TERM tri-layer

Section titled “3. L2 activation — LSHIFT + TERM tri-layer”

L2 is reached by holding LSHIFT (LT1) and TERM (RT1) simultaneously — the standard QMK “both-thumb adjust layer” idiom (update_tri_layer / layer-state function of which keys are physically down). It threads the ADR-0032 §4 constraint cleanly:

  • No thumb tap is sacrificed. Tapping TERM alone is still TERM dispatch; tapping LSHIFT-position alone is still the momentary L1 modifier. L2 lights only while both thumbs are held. There is no tap-hold timing tax on any hot key — layer state is a pure function of which keys are currently down.
  • No mouse-click reassignment (the specific §4 prohibition) — L2 is a layer, not a pointer binding.
  • Ergonomics: left thumb + right thumb held → all eight fingers free to type the symbol. This is the same gesture family operators know from Miryoku/Corne-class boards.

Bring-up validation item: confirm no collision between a held TERM-in-L1 and any TERM long-press context binding (ADR-0016 §9). There should be none — L2 requires LSHIFT to be co-held, and a TERM long-press alone (no LSHIFT) is untouched.

4. L2 contents — the unprinted long tail

Section titled “4. L2 contents — the unprinted long tail”

L2 carries the ASCII not on BASE/SHIFT that cart authors and display/string text occasionally want (as amended 2026-07-10: ! left this layer for the printed SHIFT, and % — KEC-required — deliberately stays here; see below):

L2 right half (unprinted; LSHIFT + TERM held):
InnerIdx Index Middle Ring Pinky
Top (—) # & [ ]
Home $ % ^ { }
Bottom ~ | _ (—) (—)

The five demoted ASCII (# & $ % ^ — six before Amendment 2026-07-10 promoted !) plus brackets/braces (paired open/close on the Ring/Pinky columns, mirroring the parens) plus ~ | _. Because L2 is unprinted, this arrangement is a firmware-bring-up tuning detail, not a keycap-art commitment — it may be refined during GWP-407 without an ADR amendment, provided the set is covered.

% is the one KEC-required character that deliberately lives on L2 (Amendment 2026-07-10). Both of its roles are rare-by-design in operator-authored code: %-prefixed names are Core internals the prefix itself marks as not-for-calling, and format directives (%d, %x, …) are occasional and typed inside string literals. Measured across the entire authored kn-86 Lisp corpus, % occurs in the single digits per tree while ! occurs in the hundreds. The printed SHIFT has no second spare, and every current L1 resident outranks % on frequency — demoting one to make room would invert the layout’s organizing principle. L2 is %’s sanctioned home, not an oversight. The L2 left half is passthrough/reserved in v0.1. The ADR-0016 §6 1-key T9 punctuation cycle (. , ' " : ; !) is unchanged and remains the in-literal-entry path; L2 is the direct path usable in any scope.

5. Keycap legend audit + EQ authoring binding (closes Josh’s Q2)

Section titled “5. Keycap legend audit + EQ authoring binding (closes Josh’s Q2)”

The 14 function-block legends were audited against the actual Fe kernel + KEC Core vocabulary (ADR-0037). Thirteen of fourteen are correct and unchanged:

  • CAR · CDR · CONS · QUOTE · ATOM · NIL · FN map to real Fe kernel symbols (car cdr cons quote atom nil fn) and carry established gameplay verbs across the 17 module specs. Note FN is now more KEC-accurate than the retired “LAMBDA” label — Fe’s lambda primitive is literally fn. ADR-0022 §1’s brevity choice turns out to also be the correct keyword.
  • EVAL · APPLY are canonical gameplay verbs (execute / replay) and authoring concepts; their KEC Core surface symbols are bound in the keymap per ADR-0037.
  • INFO · LINK · BACK · SYS are correctly non-Lisp runtime / navigation verbs — they are not KEC keywords and should not be.

The one correction — EQ. KEC has no eq: identity/equality is the word is, and numeric equality is = / == / /= (now directly typeable on the SHIFT home row, §2). The EQ cap therefore names a concept whose KEC symbol differs from the legend. Decision: keep the EQ keycap — it is a clear “identity check” gameplay verb, canonical across every module spec, and relabeling it would ripple through 17 specs for zero gameplay gain — but bind its authoring-insert to KEC’s is (exact binding confirmed against the KEC Core vocabulary during GWP-407). This is a keymap-binding + documentation clarification with no keycap-art change.

The two bottom-left v2 spares (L20 / L21) remain reserved — authoring primitives are not promoted onto them; the deck covers the rest of the KEC vocabulary through T9 + token prediction per the §Constraints direction.


Option A: Reorganize the printed SHIFT only (2 layers) (rejected — too little)

Section titled “Option A: Reorganize the printed SHIFT only (2 layers) (rejected — too little)”

Drop ! $ % ^ & #, promote < > = ? ' into the freed SHIFT slots; no L2. Closes the gap with every KEC char printed, no new gesture.

Rejected because: it strands the six demoted ASCII glyphs on the T9 punctuation cycle only (reachable solely in literal-entry scope). Cart authors writing display strings / comments in other scopes lose direct access. The marginal cost of adding L2 (one tri-layer gesture, zero printed legends) is low enough that keeping direct access is worth it.

Option B: Reorganize SHIFT + add an unprinted L2 long-tail (3 layers) (ACCEPTED)

Section titled “Option B: Reorganize SHIFT + add an unprinted L2 long-tail (3 layers) (ACCEPTED)”

Option A’s SHIFT reorg for all 19 KEC chars (as then derived; 21 per Amendment 2026-07-10), plus an unprinted L2 (LSHIFT+TERM) for the rare ASCII. Frequency-correct: the frequent comparison/predicate operators sit on the printed shift; the rare ASCII lives in muscle memory. Matches Josh’s “don’t print every layer; lean on T9 + TAGS” direction.

Option C: Comparison cluster on the SHIFT-left half (2 layers) (rejected)

Section titled “Option C: Comparison cluster on the SHIFT-left half (2 layers) (rejected)”

Put < > = ? on the currently-passthrough SHIFT left half (semantic affinity with the predicate-heavy Lisp primitives); ' fills the spare right slot; keep most current ASCII on SHIFT-right.

Rejected because: it puts the highest-frequency operators (comparisons) into a same-hand-as-LSHIFT chord (LSHIFT is the left thumb). Frequency argues for the opposite hand. It also keeps the KEC-irrelevant ASCII on prime printed real estate. Option B’s 4-5-6 → < = > on the right hand is both more comfortable and more mnemonic.

Option D: Add L2 but reach the missing chars only on L2 (keep SHIFT as-is) (rejected)

Section titled “Option D: Add L2 but reach the missing chars only on L2 (keep SHIFT as-is) (rejected)”

Leave the printed SHIFT untouched; put < > = ? ' on the unprinted L2.

Rejected because: it buries the most common operators (<, =, ? are everywhere in real Lisp) on the unprinted, two-thumb layer while leaving KEC-irrelevant symbols (! $ % ^ & #) on the easy printed shift. Exactly inverted frequency. The unprinted layer is for the rare tail, not the staples.


  • Discoverability vs. surface area. Every KEC-required glyph except the deliberately-L2 % is printed (L0/L1) (Amendment 2026-07-10). The long tail is unprinted (L2) — acceptable because those glyphs are rare in KEC authoring and the gesture is a familiar idiom. Net: the printed surface now matches the language; the hidden surface holds only what belongs hidden.
  • One new gesture. LSHIFT+TERM tri-layer is the only new motor pattern. It costs nothing on the hot path (pure layer-state function, no timing) and sacrifices no thumb tap.
  • Keycap art. L1 secondary legends change for six keys (2 4 5 6 8 shifts + 9, which gains ! rather than going blank per Amendment 2026-07-10). v0.1 ships blank caps (ADR-0018), so this only affects the eventual UV-print manifest — zero impact on the v0.1 build. The function block (L0) art is unchanged.
  • Software dispatch. The QMK keymap (GWP-407) gains an L2 layer + tri-layer + the EQ→is authoring binding. The Linux HID → nOSh keycode path is unchanged (same KN86_KEY_* codes; only secondary glyph dispatch changes). No FFI or runtime change.
  • Cost accepted. Six Five (per Amendment 2026-07-10 — ! returned to the printed shift) ASCII glyphs move from a one-hold printed shift to a two-hold unprinted layer. None of them is KEC-required except %, whose L2 residence is deliberate (§4); all remain reachable (L2 in any scope; the T9 cycle in literal entry). Acceptable.

  • The deck can finally type idiomatic KEC Lisp. The two hard blockers (<, ?) and the three soft gaps (=, >, ') are all printed and ≤1 hold.
  • Frequency-correct layout. Comparison/predicate operators on the strong home-row of the printed shift; rare ASCII in muscle memory.
  • The < = > triplet is a teaching mnemonic — “shift the 4-5-6 row.”
  • L2 puts the reserved layer to work without printing a third legend tier, matching the T9 + TAGS authoring philosophy.
  • The legend audit confirms the function block and upgrades the justification for FN (now keyword-accurate, not just shorter).
  • One spare SHIFT slot (9-key shift) for a future KEC need. (Consumed by Amendment 2026-07-10: the 9-key shift now carries ! — which turned out to be exactly the future KEC need it was reserved for.)
  • Six Five ASCII glyphs demote from printed shift to unprinted L2 (still reachable) (per Amendment 2026-07-10). Accepted — not KEC-required, except %, whose L2 residence is deliberate (§4).
  • One new gesture (LSHIFT+TERM) operators must learn for the long tail. Mitigated by it being a community-standard idiom and only needed for rare symbols.
  • EQ legend ≠ EQ authoring symbol (is). Mitigated: the cap is a gameplay verb; the authoring binding is documented; = is independently typeable.
  • Doc + keymap cascade. ADR-0031/0022/0016 footnotes, canonical-spec.md Keys row, input-dispatch.md, the firmware keymap README, and GWP-407 all update. Tracked below.
  • F1 — GWP-407 keymap update. The QMK retarget implements the §2 SHIFT reorg, the §3 tri-layer L2, the §4 L2 set, and the §5 EQ→is authoring binding — all as amended 2026-07-10 (! on the 9-key shift; the L2 set less !) — not the ADR-0031 §3.1 SHIFT manifest as originally written. Owner: Platform Engineering. (GWP-407 acceptance criteria updated to cite this ADR.)
  • F2 — Per-key authoring-insert table. The exact KEC symbol each function key contributes in :repl-prompt / :nemacs-* scopes (especially EQ→is, ATOM, EVAL, APPLY) is defined against the KEC Core vocabulary during the input-model implementation (ADR-0016 Implementation Queue). Owner: C Engineer.
  • F3 — Keycap UV-print manifest. The eventual print job uses the §2 L1 manifest. Owner: Hardware, post-v0.1 polish cadence.

Documentation Updates (REQUIRED — part of the decision, not aspirational)

Section titled “Documentation Updates (REQUIRED — part of the decision, not aspirational)”

Per canonical-spec.md Spec Hygiene Rule 3, this ADR lands in a single PR including:

  • docs/adr/ADR-0044-kec-complete-keyboard-layout.md — this file.
  • docs/adr/README.md — ADR-0044 row at the top of the index; status notes on 0031/0022/0016 updated to cite amendment by ADR-0044.
  • docs/adr/ADR-0031-ferris-sweep-keyboard-adoption.md — amendment footnote at §3.1 (L1 SHIFT reorganized; L2 bound; tri-layer activation) pointing here.
  • docs/adr/ADR-0022-keyboard-layout-finalization.md — amendment footnote at §2/§3 (shift-secondary manifest is KEC-completed by ADR-0044 §2).
  • docs/adr/ADR-0016-nemacs-repl-input-model.md — footnote at §6 (demoted ASCII gain a direct L2 path; cycle unchanged).
  • canonical-spec.md Keys row — replace the SHIFT-secondary clause with the KEC-complete manifest + the unprinted L2 (LSHIFT+TERM) note; cite ADR-0044. (Canonical Hardware Specification lives in the umbrella ~/src/kinoshita/CLAUDE.md — see PR description; companion edit there.)
  • docs/software/runtime/input-dispatch.md — existing stale-layout banner threaded with ADR-0044 (current SHIFT manifest, L2, EQ→is). The §1/§3D/§8D shift-secondary tables rewrite is part of the broader keyboard-sweep rewrite tracked under GWP-407 / F1 (the doc was already flagged stale for ADR-0031).
  • firmware/kn86-keyboard/README.mddoes not exist yet (the firmware tree is a design-only skeleton per hosts/device/kn86-firmware/README.md, gated behind GWP-152). The keymap README + keymap.c land under GWP-407 and must reflect ADR-0044 §2–§4 then. Tracked as F1.
  • Project-wide grep sweep for stale claims:
    • “L2 … reserved” / “L2 leaves … unbound” — annotate as bound by ADR-0044 (permitted to remain: ADR-0031 design history with footnote).
    • Shift-secondary lists containing ! $ % ^ & # as KN-86 layout — annotate/repoint to ADR-0044 §2 (permitted: ADR-0031/0022 design history with footnotes).
    • “EQ” described as a KEC/Lisp symbol the key inserts — clarify to is per §5.

A PR that lands this ADR without ticking the in-scope ([x]) items fails review per canonical-spec.md Spec Hygiene Rule 3. The [ ] items are F1/F2 follow-ons (keymap + input-dispatch implementation) tracked under GWP-407.


For most of the project the deck’s shift layer was a conventional ASCII symbol row — ! @ # $ % ^ & and friends — chosen before the authoring language had a name. When ADR-0037 named KEC Lisp and froze the Fe kernel, the question finally had a precise answer: which punctuation does this deck actually need to type? Josh worked it out — nineteen characters — and discovered that five of them (', <, >, =, ?) weren’t reachable at all, and two of those (<, ?) had no in-language escape: you simply could not write an ordering comparison or call a standard predicate. Meanwhile the shift layer was spending six slots on symbols KEC never uses. The fix was not “find more keys” — it was “stop printing the wrong ones.” Drop ! $ % ^ & #, and the comparison operators fall onto the 4-5-6 home row reading left-to-right, < = >, which is the kind of mnemonic you only get when the layout and the language finally agree. The rare ASCII the cart authors still occasionally want went to an unprinted third layer reached by holding both thumbs — the deck leaning, as it was always meant to, on Nokia multi-tap and token prediction rather than on printing every glyph it can produce. And the keycap audit had a small gift in it: FN, chosen years earlier purely because it was shorter than LAMBDA, turned out to be the actual Fe keyword. The one real correction was EQ — KEC says is, not eq — but that one stays on the cap, because by now the keys are gameplay verbs as much as they are Lisp, and EQ means “are these the same?” to every operator who has ever drilled a wreck or compared two bands. A future reader should care because this is the moment the keyboard stopped being a generic terminal and became a KEC Lisp instrument.

(One more correction arrived nineteen days later: the count was twenty-one, not nineteen — KEC does carry a bang-mutation convention after all, and % prefixes its internals. The ! took the spare shift slot the June layout had left waiting; the % stayed in muscle memory, on purpose. See the Amendment Log.)


2026-07-10 — KEC punctuation set corrected to 21: ! promoted to the printed SHIFT, % deliberately L2 (GWP-701)

Section titled “2026-07-10 — KEC punctuation set corrected to 21: ! promoted to the printed SHIFT, % deliberately L2 (GWP-701)”

Status effect: Accepted (unchanged). Amendment pattern follows the ADR-0004 precedent: new **Amended:** header line + this log section + struck-through corrections in the body.

Background. The kec-lisp docs accuracy sweep corrected docs/language.md §Punctuation Characters from 19 to 21 (branch claude/docs-accuracy-review-b5cd9c). The June derivation audited reader specials and kernel/Core operator names but missed two identifier-tier characters:

  • ! — the bang-mutation suffix. The pinned kec-lisp rev ships eight bang primitives (vector-set! matrix-set! blob-set! hash-set! hash-del! vector-fill! matrix-fill! set-seed!), and the convention is idiomatic well beyond the kernel: kn-86’s own authored Lisp carries ~850 raw ! occurrences across carts/, runtime/programs/, and the system userland — mostly bang-named calls (add! ×111, set-cell-field! ×53, repl/submit!, keystore/commit!, …). The Context claim “KEC has no bang-mutation conventionset / setcar are words” was wrong and is withdrawn.
  • % — the private-name prefix on Core internals (%append, %qq, …) and the format directive character (%d, %x, …).

Decision. The two characters get different placements, by this ADR’s own frequency principle:

  1. ! → printed L1 SHIFT, on the 9-key spare. It is the same character class as ? — a name suffix you cannot avoid typing to call standard functions — and close behind it in practice. §2 reserved exactly one spare printed slot “for a future KEC need”; this is that need. Net-zero cost: no demotion, no keycap-art conflict (v0.1 ships blank caps; the UV-print manifest is F3 and not yet ordered), and GWP-407 has not landed, so no keymap rework — the amendment reaches firmware before the layout is encoded.
  2. % → stays on the unprinted L2, deliberately. Both roles are rare-by-design in operator-authored code: %-prefixed names are internals the prefix itself marks as not-for-calling, and format directives are occasional and typed inside string literals. Measured against the authored kn-86 corpus: single-digit % occurrences per tree vs. hundreds of !. There is no second spare printed slot, and every current L1 resident outranks % on frequency — demoting one (the lightest candidates, @ and \, are reader-special) would invert the layout’s organizing principle.

The “KEC-complete” claim is therefore scoped: the three layers still cover all 21 required characters, with 20 of 21 printed and ≤1 hold and % as the one sanctioned L2 resident.

Corrections applied in the body: header; Context count (19 → 21), identifier-tier list, and the withdrawn no-bang claim; §1 layer table; §2 intro, grid, ledger (9 shift: spare → !), and result line; §4 grid, demoted set, and the deliberate-% paragraph; Trade-off Analysis; Consequences (spare-slot bullet consumed; demotion count); F1.

Documentation cascade (this amendment’s PR): canonical-spec.md Keys row; docs/adr/README.md ADR-0044 row; ADR-0031 §3.1 banner + L2 note; ADR-0022 §2 footnote; ADR-0016 §6 footnote; companion umbrella ~/src/kinoshita/CLAUDE.md Keys-row PR (jschairb/kn86-deckline, per the PR #296/#298 precedent).