Removes kilo, kimi, kimi-code, copilot, windsurf, augment, trae, qwen, hermes, cline, codebuddy and pi end to end: capability descriptors, installer branches and converters (bin/install.js 14.9k -> 11.2k lines), TypeScript converters, hook surfaces and runtime homes, review lanes qwen/kimi-code, the two pi migrations, Kimi payload normalization in the hook guards, dead hostBehaviors vocabulary, launcher home probes, fixtures, runtime-specific tests and the prose that presented them as supported. Installer output for the six kept runtimes is byte-identical to before the prune. The Kimi tool-vocabulary tests in workflow-guard, read-guard and read-injection-scanner are left in place pending a decision.
3.2 KiB
3.2 KiB
id, title, group
| id | title | group |
|---|---|---|
| 3911 | Hooks Declare Their Crash Policy | v1.7.0 Features |
Purpose: Give every shipped enforcement hook (hooks/*.js, hooks/*.sh) a
named, auditable termination vocabulary instead of a bare process.exit(N)
scattered per file — and make a hook's fail-open/fail-closed choice a
declaration a reviewer can see, rather than an inference from which literal
integer follows process.exit( in its outer catch.
What changed (ADR-3889 Phase 7, #3911):
hooks/lib/hook-exit.js(hand-written) exposesallow(payload)→ exit 0,deny(payload, stderrPayload?)→ exit 2, andcrash(onCrash, payload), which dispatches toallow/denyper aHOOK_ON_CRASHpolicy the caller must supply —crash()has no default policy, so a hook cannot fail open by omission.- Every one of the 19 enforcement hooks under
hooks/*.jsnow declaresconst ON_CRASH = HOOK_ON_CRASH.ALLOW(orDENY) once, with a hook-specific comment naming why, and callscrash(ON_CRASH, payload)from its outer catch instead of a bareprocess.exit(0)/process.exit(2). No hook's effective exit code changed — this is a naming-and-declaration migration, not a behavior change. hooks/lib/cli-exit.jsandhooks/lib/exit-code-registry.jsare new, generated, git-tracked copies of the exit-code seam (src/cli-exit.cts/msd-core/bin/shared/exit-codes.json), so a shipped hook can terminate correctly on a raw, unbuilt clone without depending onmsd-core/bin/lib/tsc output. Generated byscripts/gen-hooks-cli-exit.cjsandscripts/gen-exit-code-registry.cjs, both--checked bynpm run lint:generated-sync.terminateNowgained an optional third argument,stderrPayload, so a deny can send a full JSON body to stdout and a distinct plain-text reason to stderr — needed becausemsd-write-guard.js(a host's native hook bus may read stderr verbatim back to the model) always sent only the bare reason string on fd 2. The two streams are now written in independent try/catch blocks: previously a payload that failed to serialize on fd 1 aborted before fd 2 ever wrote, producing a deny with an empty stderr reason.- Two hooks are deliberately not migrated to
deny():msd-read-injection-scanner.js(PostToolUse — its harness reads the block decision from the JSON response body, not the exit code) andmsd-cursor-subagent-start.js(follows Cursor's ownsubagentStartprotocol, which readspermission: "deny"from the JSON body at exit 0). Both still useallow()/crash()for their no-op and crash paths. msd-phase-boundary.sh,msd-session-state.sh, andmsd-validate-commit.shgainedset -euo pipefail, andmsd-validate-commit.sh's three swallow-and-pass sites (the opt-in config read, JSON command extraction, and theisGitSubcommandclassifier) now distinguish a genuine negative from "could not run" — on the latter they emit a stderr diagnostic and exit 0 instead of silently allowing every commit (#3838).
See Declare a hook's crash policy for the full how-to, and ADR-3889 for the exit-code registry this vocabulary is layered over.