cf086f8c04
The browser's hand-written key classifier was a second spelling of default_keymaps(), held honest by a lockstep test over a hand-written list -- doctrine(a_consolidation_that_keeps_its_compatibility_test_is_unfinished)'s exact shape. So the TABLE moved to improvise-protocol and improvise-command keeps only what needs a CmdRegistry. Evidence is binding_dump_diff, not the suite: 4021 resolved (mode, key, modifier) lookups over sixteen modes, BYTE-IDENTICAL, with the dump kept as an opt-in test and proved discriminating by renaming one binding. The boundary that did NOT move is the better one: key -> intent is shared, intent -> Command stays client-side, and the named list of what still needs the host is now a list of COMMAND NAMES rather than keys -- so a rebinding no longer invalidates it. AND A LOCKSTEP TEST FOUND A LIVE BUG, BY A COMMA. CellDisplay::text has been through format_f64; App::display_value_at_cursor has not. Seeding an editor from the drawn text and committing turns a stored 1200 into the string '1,200' -- display rounding fed back into the model, which design-principles 4 forbids outright. CellDisplay::edit_text puts the unformatted value on the wire, and it repairs web/app.js's click-to-edit, which had been seeding its input from td.textContent since it was written. That bug is on the live page today for any value with a thousands separator. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH