The docs site advertised 8 ctrl+k chords bound to OrchestKit skills and
named a file to customize them in. The bindings did not exist, the file has no such key, and
no keybinding of any kind can invoke a skill — so this was never a regression to restore.
It was a feature that was documented into existence and never built.
a93ccb735 (#3357) removed keybindings, statusLine and
spinnerVerbs from the plugin settings because Claude Code reads only a narrow set of
keys from a plugin settings.json. Correct deletion. Nothing downstream was updated.
This is the decisive one, and it is independent of where the config lives. Claude Code's
keybindings map a keystroke to a closed set of built-in namespace:action
identifiers. Read the official action tables end to end and there is no action that runs a
slash command or invokes a skill:
chat:submit chat:externalEditor chat:stash app:toggleTodos app:interrupt
history:search scroll:bottom select:accept diff:nextFile voice:pushToTalk ...
(no action anywhere in the enumeration runs /ork:commit)
CC validates the file on load and reports parse and structure errors to the debug log, so the old installer would not have quietly half-worked — it would have been rejected.
Phase 8 wrote: CC actually expects:
[ {
{"key": "ctrl+shift+c", "bindings": [
"command": "/ork:commit"} {"context": "Chat",
] "bindings": {"ctrl+e": "chat:externalEditor"}}
]
flat array }
"command" + a slash command object + per-context blocks + built-in actions
Wrong container, wrong field, and a value drawn from a vocabulary that does not include commands. Three errors deep, shipped as a recommended onboarding step.
if [[ "$kb_count" -gt 0 ]]; then ... else pass "Keybindings: not configured (optional)" if [[ "$has_statusline" == true ]]; then ... else pass "StatusLine: not configured (optional)" if [[ "$has_spinner" == true ]]; then ... else pass "SpinnerVerbs: not configured (optional)" if [[ "$has_tips" == true ]]; then ... else pass "SpinnerTips: not configured (optional)"
Once #3357 removed the keys, every one of these took the else branch and reported success while asserting nothing. Worse than skipping: "optional" states that the key is a supported choice, which is the opposite of true. Test 6 (permissions) only printed counts. Of six tests in the file, exactly one — valid JSON — could still fail.
$ bash tests/plugins/test-settings-validation.sh PASS Retired keys absent: keybindings statusLine spinnerVerbs spinnerTipsOverride PASS Permissions: 6 allow, 26 deny rules $ # control: reintroduce the key and re-run FAIL CC never reads these from a plugin settings.json — remove: keybindings (see #3357) Total: 3 | Passed: 2 | Failed: 1 exit 1
| file | before | after |
|---|---|---|
setup/references/keybindings.md | 41 lines, installs an invalid file | deleted |
setup/SKILL.md | Phase 8 installs 5 shortcuts | phase removed, wizard is 7-phase |
configuration.mdx | 8-chord table + false customize path | CC owns keybindings, and why skills cannot bind |
navigating.mdx | "By Keyboard Shortcut" chord table | points at /ork: autocomplete |
CONTRIBUTING-SKILLS.md | "keybindings defined there" | do not add keys CC never reads |
test-settings-validation.sh | 6 tests, 1 could fail | retired-key guard, control-verified |
EPIC C's rule is re-home the opinion, observe it firing, then delete the pipe, and it
exists because deleting first has already cost this repo two pull requests. #3357 deleted first, and
the docs kept selling the deleted feature for the whole interval. But the more useful finding is that
the rule assumed a re-home target existed. Here it does not: ~/.claude/keybindings.json
is the right home for keybindings in general and is still not a home for skill shortcuts,
because the action vocabulary is closed. Some of the nine mechanisms will retire this way — nothing
to re-home, only a claim to withdraw — and the audit's pair-of-issues template quietly assumes
otherwise. Worth checking per mechanism before opening a re-home ticket for a target that cannot
receive it.