Development record
cobra parents with no RunE are not Runnable, so cobra prints help and exits 0 WITHOUT validating args: an unknown sub-verb reads as success to a script. Confirmed on 'abcd docs nonsense' and 'abcd guard nonsense' (both exit 0); 'abcd memory nonsense' and 'abcd history nonsense' exit 1 because those parents have a RunE. spc-30 fixed the disembark parent (a retired sub-verb spelling must refuse, not silently succeed) by giving it a RunE that prints help, so cobra.NoArgs then refuses a stray token at exit 2. The same one-line fix is owed to every other RunE-less parent — sweep them so no unknown sub-verb anywhere exits 0.
cobra parents with no RunE are not Runnable, so cobra prints help and exits 0 WITHOUT validating args: an unknown sub-verb reads as success to a script. Confirmed on 'abcd docs nonsense' and 'abcd guard nonsense' (both exit 0); 'abcd memory nonsense' and 'abcd history nonsense' exit 1 because those parents have a RunE. spc-30 fixed the disembark parent (a retired sub-verb spelling must refuse, not silently succeed) by giving it a RunE that prints help, so cobra.NoArgs then refuses a stray token at exit 2. The same one-line fix is owed to every other RunE-less parent — sweep them so no unknown sub-verb anywhere exits 0.
Frontmatter
| id | iss-266 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-266-runeless-parents-exit-zero-on-unknown.md
open on GitHub ↗ · commit history ↗
mentions3
- dcn-26081700000000032026-08-17 — Command parents owe TWO guarantees, not one (iss-266, #293).
- iss-267Hook sub-verb names are now a compatibility contract, not a cushion. iss-266 makes every parent runnable, so an unknown sub-verb exits 2 instead of printing help at exit 0. On the Claude Code hook plane exit 2 is the BLOCKING status: exit 2 on UserPromptSubmit blocks the prompt, and exit 2 from 'guard hook' on PreToolUse blocks every Bash tool call -- and the PreToolUse wrapper in hooks/hooks.json treats 2 as a recognised code, so its 'FAILED TO RUN ... UNGUARDED' safety net does not fire. hooks.json ships with the plugin git clone while the binary is fetched from the latest release by hooks/bootstrap.sh, so the two can skew. No live instance today (every sub-verb hooks.json names exists at v0.5.1), and the fail-closed direction is intended. But a future rename of 'guard hook' or any 'hook <sub-verb>' must now carry an alias rather than rely on the old silent exit-0 cushion: against a skewed hooks.json/binary pair it would brick a session where it previously degraded silently. Found by the security review of the iss-266 fix.
- spc-30the-lifeboat-verdict-says-what-it-is-disembark-oracle-become