An implementation-independent Common Lisp keyboard layout language and compiler
  • Common Lisp 99.8%
  • Scheme 0.2%
Find a file
htayj 47f88cccd9 Merge origin/master before publication
Preserve the newer Phase 8 acceptance evidence while retaining the cross-machine handoff.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-28 23:34:25 -04:00
.github/workflows feat: add project composition and Manna baseline 2026-08-14 00:29:32 -04:00
bin feat: add owner-scoped holds and selector policy 2026-08-14 04:05:37 -04:00
devices test: validate generated device configurations 2026-08-28 23:32:10 -04:00
docs Merge origin/master before publication 2026-08-28 23:34:25 -04:00
guix/ivory-key build: pin Guix channel and Kanata package 2026-08-28 23:32:10 -04:00
layouts fix: account for direct rmet source 2026-08-14 13:59:11 -04:00
realizations test: validate closed Manna Kanata proposals 2026-08-14 14:24:40 -04:00
src feat: export endpoint APIs 2026-08-28 23:32:10 -04:00
tests test: validate generated device configurations 2026-08-28 23:32:10 -04:00
tools feat: harden Manna device integration boundary 2026-08-23 03:44:54 -04:00
topologies fix: account for direct rmet source 2026-08-14 13:59:11 -04:00
.envrc docs: document pinned development environment 2026-08-28 23:32:10 -04:00
.gitignore feat: implement Ivory Key compiler core 2026-08-13 23:45:05 -04:00
channels.scm build: pin Guix channel and Kanata package 2026-08-28 23:32:10 -04:00
ivory-key.asd feat: advance Kanata buffered compatibility 2026-08-14 11:06:38 -04:00
LICENSE chore: license Ivory Key under GPLv3 or later 2026-08-13 21:48:57 -04:00
manifest.scm build: pin Guix channel and Kanata package 2026-08-28 23:32:10 -04:00
manna-cadet-project.ivory feat: add provenance and validation evidence 2026-08-14 05:49:26 -04:00
PLAN.md feat: harden Manna device integration boundary 2026-08-23 03:44:54 -04:00
README.md docs: document pinned development environment 2026-08-28 23:32:10 -04:00

Ivory Key

Ivory Key is a Common Lisp implementation of a declarative keyboard-layout compiler. Its source language describes logical positions, semantic output, context state, and timed interaction rules without embedding XKB, Kanata, evdev, or firmware spellings.

The project is GPL-3.0-or-later. The architecture and intended end state are described in PLAN.md; this README describes the code that exists now.

Current status

The bootstrap currently provides:

  • a dedicated, non-evaluating S-expression lexer, parser, diagnostics, and canonical formatter;
  • typed model objects for axes, modifiers, positions, behaviors, bindings, sparse overlays, and finite timed interactions, with resolution, validation, and normalization support;
  • a deterministic project loader for explicitly imported layout, topology, device, realization, and realize composition declarations. Imports are confined to configured source roots and reject cycles, traversal escapes, and symlink escapes;
  • a reference timed-event simulator, including a fail-closed adapter for disjoint normalized ordinary bindings, sparse overlays, and supported timed interactions;
  • a realization-owned semantic output-vocabulary registry for deterministic named-key, named-symbol, and command spellings;
  • a target-neutral capability planner that preserves normalized static product tables, lists selector/modifier/resource requirements, and deterministically detects allocation collisions or exhaustion;
  • constrained XKB and Kanata emitters driven by a backend-neutral lowering-request, plus optional external-tool validation;
  • a separate QMK protocol backend for deterministic, explicitly ordered static Configurator JSON and optional firmware compilation; and
  • a conservative end-to-end compiler for exactly representable static layouts, with deterministic inspection, capability explanation, fresh build-directory emission, machine-readable manifests/source maps, and explicit refusal of unsupported semantics.

The checked-in Manna Cadet project contains the reviewed 52-key static-symbol transcription, selected selector/timed-interaction policy, closed A2/A360 native input ledgers, and exact XKB/Kanata allocations. Ivory Key can generate and validate both replacement artifact sets; it has not installed or activated either set on a keyboard. The twenty-level conformance fixture remains expressible in the abstract model but is not silently reduced to eight states. The CLI simulate command accepts a restricted declarative event stream for the whole-layout adapter's exact supported slice. It honors normalized sparse-overlay precedence and transparent fall-through for ordinary bindings, while refusing latch-dependent overlay dispatch, binding/interaction position overlap, and model patterns or actions the event machine cannot represent.

Quick start

The checked-in channels.scm and manifest.scm provide SBCL, ECL, XKB validation and state-test development files, the required Kanata 1.12.0, QMK, a C toolchain, pkg-config, and curl through Guix. With direnv installed, approve the checkout once with direnv allow; .envrc then evaluates the manifest through direnv's built-in Guix time-machine integration. The equivalent one-shot environment is:

guix time-machine -C channels.scm -- \
  shell -L guix -m manifest.scm

From this checkout, load the ASDF definition and run the hermetic tests:

sbcl --non-interactive \
  --eval '(require :asdf)' \
  --eval '(asdf:load-asd (truename "ivory-key.asd"))' \
  --eval '(asdf:test-system "ivory-key/tests")'

The CLI entry point supports:

./bin/ivory-key check FILE...
./bin/ivory-key fmt [--check] FILE...
./bin/ivory-key inventory MANNA-CADET-CHECKOUT
./bin/ivory-key dump-ir --stage parsed|typed|normalized --layout FILE [--topology FILE]
./bin/ivory-key dump-ir --stage planned|backend --layout FILE --device FILE --realization FILE [--topology FILE]
./bin/ivory-key dump-ir --stage typed|normalized|planned|backend --project PROJECT --composition NAME
./bin/ivory-key levels --layout FILE [--topology FILE]
./bin/ivory-key levels --project PROJECT --composition NAME
./bin/ivory-key simulate --layout FILE [--topology FILE] --events FILE
./bin/ivory-key simulate --project PROJECT --composition NAME --events FILE
./bin/ivory-key explain --layout FILE --topology FILE --device FILE --realization FILE
./bin/ivory-key explain --project PROJECT --composition NAME
./bin/ivory-key compile [--validate-before-publish] --layout FILE --topology FILE --device FILE --realization FILE --output DIR
./bin/ivory-key compile [--validate-before-publish] --project PROJECT --composition NAME --output DIR
./bin/ivory-key validate-build DIR

inventory reads the supplied checkout, records the baseline commit, file hashes, tool-version probes, and selected evidence lines. It does not change the checkout or install any keyboard configuration.

Project-mode commands select one named realize composition. They do not accept a mixture of --project/--composition and independent layout/device/ profile paths, because that would make the selected meaning and placement ambiguous. dump-ir project mode exposes typed, normalized, planned, and backend stages; raw parsed inspection remains a single-file mode. planned lists canonical requirements, allocation dispositions, and every fidelity grade without lowering. backend lowers only an all-exact direct request into in-memory XKB and Kanata plans; it neither emits artifact text/files nor runs validators, and refuses unsupported layouts before that later pipeline work.

Project simulation selects the composition's already resolved layout and runs the same restricted, backend-neutral event adapter as direct layout mode. The composition's device and realization remain context only: simulation does not lower a backend, prove physical equivalence, emit files, or deploy anything.

The checked-in manna-cadet-project.ivory is the auditable import graph for the frozen Manna layout, both Kinesis placements, the selected Linux profile, and named A2/A360 compositions. Both selected compositions compile to exact, source-mapped XKB/Kanata artifacts. Installation and live input validation are separate, authorization-gated operations described by the integration runbook.

When a project realization selects an output vocabulary, exact static named-key and named-symbol bindings use its explicit XKB and Kanata spellings. Missing backend mappings, missing identities, commands without an approved semantic lowering, and unsafe backend tokens refuse before any build is written. No spellings are inferred from the Manna transcription.

Documentation

compile creates a new build beneath an existing trusted output parent and refuses to overwrite one. Exact builds contain manifest.json, allocations.json, source-map.json, and REPORT.md alongside backend artifacts; source identities are relocatable and source/artifact hashes are SHA-256. Direct mappings and concrete allocations retain typed relocatable origins (or explicit programmatic unknown), never checkout paths. Compilation records no external validation claim unless validation actually ran, and it never deploys. Because portable Common Lisp lacks an atomic non-replacing directory rename, that parent must not be concurrently writable by an untrusted process. Generated artifacts and live keyboard deployment remain separate. A successful parse, normalization, in-process lowering, external tool check, and observed live input are different kinds of evidence.

The separately tagged external XKB/Kanata probe is intentionally not part of the hermetic ASDF suite:

direnv exec . sbcl --script tests/external/xkb-kanata.lisp
direnv exec . ecl -norc -shell tests/external/xkb-kanata.lisp
direnv exec . sbcl --script tests/external/manna-kanata-generated.lisp

Besides parser acceptance, it compiles a focused XKB plan and checks its compiled groups, levels, symbols, actions, and consumed/unconsumed Shift state through libxkbcommon. The Manna-specific script separately checks that the installed Kanata parser accepts both closed, deterministic device artifacts; its opt-in pinned runtime mode covers the selected interaction scheduler. None of these checks is live-device or deployment proof.