Files
msd-core/docs/reference/exit-codes.md
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

68 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Exit code reference
> **Generated file — do not edit by hand.**
> This page is generated from the exit-code declaration
> (`msd-core/bin/shared/exit-codes.json`) by `scripts/gen-exit-code-docs.cjs`
> and kept honest by a drift guard in `npm run lint:generated-sync` (which runs
> `node scripts/gen-exit-code-docs.cjs --check`). Any manual edit is overwritten
> on the next generation run. To register a new code, add an entry to the
> declaration and run `node scripts/gen-exit-code-registry.cjs --write && node
> scripts/gen-exit-code-docs.cjs --write`.
See also: [ADR-3889 — one exit-code registry](../adr/3889-process-exit-contract.md) —
[Adopt the v2 exit contract](../how-to/adopt-the-v2-exit-contract.md) —
[JSON error mode](../json-errors.md)
---
## Registered codes (6)
Every process-level exit code `msd-tools`, its hooks, and its scripts may terminate
with, by name, meaning, and the module that owns it.
| code | name | meaning | owning module | authorized by |
|---|---|---|---|---|
| 2 | `HOOK_DENY` | Hook protocol deny — the harness blocks the tool call | `hook-adapter` | ADR-3889 |
| 64 | `USAGE` | Caller error — bad argv, unknown subcommand, missing argument | `generic` | ADR-3889 |
| 66 | `NO_INPUT` | Ran; zero units were in scope, and that emptiness is known to be genuine | `generic` | ADR-3889 |
| 69 | `UNAVAILABLE` | Could not run — prerequisite absent, input unreadable, scope unestablished | `generic` | ADR-3889 |
| 70 | `INTERNAL` | Self-failure — crash, timeout, killed subprocess | `generic` | ADR-3889 |
| 80 | `DEGRADED` | Ran to completion and is reporting a condition through its result payload rather than as a process failure | `msd-tools` | ADR-3889 + ADR-2980 |
---
## Reserved bands
The registered codes above are not chosen freely — each falls inside one of a
fixed set of allocatable bands (ADR-3889 §1). A code outside these bands can
never be registered; validation rejects it before it reaches the tables above.
This is what makes an unfamiliar number in a CI log actionable: look up its
band first, then its registered name if it has one.
| Band | Meaning |
|---|---|
| `0`–`1` | **Free — never allocatable.** `0` is the universal "succeeded" convention and `1` is the universal "failed, no further detail" convention across nearly every CLI ecosystem. Registering either here would collide with that universal meaning instead of adding a distinct, named signal — so the registry leaves both permanently unallocated. |
| `2` | Reserved exclusively to the Claude Code hook-protocol deny (`HOOK_DENY`) — owned by `hook-adapter` and no other module. |
| `3`–`13` | **Node-reserved.** Node.js itself assigns meaning to this range (e.g. internal JavaScript errors, fatal exceptions, invalid argument errors) before a MSD process ever gets a chance to project its own outcome. Allocating one of these would be indistinguishable from a Node-level failure the process never intended to report. |
| `14`–`63`, `79`, `126+` | Outside every allocatable band — not Node-reserved, but also not opened for MSD use. `126`+ additionally collides with the shell convention for "command not executable" / "signal N" (`128+N`), which a process exit code must never impersonate. |
| `64`–`78` | **Generic band.** Codes any module may use for caller-facing, non-domain-specific outcomes (bad argv, no input in scope, a missing prerequisite, an internal crash). |
| `80`–`125` | **Domain band.** Codes reserved for a specific product surface's own vocabulary — currently only `msd-tools`' `DEGRADED` (a completed run reporting a condition through its payload rather than as a process failure). |
## The v1/v2 exit contract
ADR-3889 §4 adds a **version projection** on top of this registry, not a second
registry: every registered name above projects to the *same* code under both
contract versions, with one deliberate exception — `DEGRADED`. Under the
default, backward-compatible `v1` contract, a payload-carried error
(`output({error})`) still exits `0`, exactly as
[ADR-2980](../adr/2980-payload-carried-error-is-a-degraded-result.md) ratified
for the pre-existing call sites that already depended on that behavior — **64**
call sites across 9 modules per that ADR's own amendment (its original text
said ~60). Under the
opt-in `v2` contract, the same outcome exits `80` (`DEGRADED`) instead, so a
caller that wants to branch on the exit code alone — without parsing stdout —
can opt in without breaking every existing consumer. See
[Adopt the v2 exit contract](../how-to/adopt-the-v2-exit-contract.md) for how to
turn this on, and [JSON error mode](../json-errors.md) for the full fault vs.
degraded-result channel taxonomy this registry sits underneath.