Status
Accepted. Introduced in v0.2.Context
Agents drive Sponson through--json and MCP, so every outcome must be machine-readable. Before v0.2, error codes
existed on some paths only; init had no JSON output; a mistyped flag or a bad MCP argument produced text; JSON mixed
data with display strings such as (pending ← db.x), which agents copied back into plans; and exit codes were chosen
case by case.
Decision
One envelope. Every command (init included), every failure path (usage errors, plan errors, provider failures,
unexpected crashes) and every MCP tool answers with one JSON document:
ok is true exactly when the exit code is 0. Line-level failures carry errorCode on the plan or receipt line.
Data, not display text. Unknown, secret and sensitive values are null next to an explicit state. Diffs are
DiffSide data; only renderers produce text. The parser rejects display text pasted back into a plan.
Codes are data. ERROR_CODES in packages/core/src/errors.ts lists every code with its exit code, a doc line
and, when the remedy is a flag, a cliHint. Exit codes: 0 success (including partial and stale), 1 failed, 2
refused before running (invocation, plan, reference, parameter, environment, approval), 3 lock held or lost. The
engine speaks in run options (approvedBy, reconcile); the CLI attaches the hint that names the flag.
toSponsonError() turns anything thrown into a coded error (INTERNAL as the last resort).
Documentation is generated. docs/errors.md is generated from the table by scripts/gen-docs.ts, and a test
fails when it is stale.
Consequences
- Agents branch on
code, never on prose; SKILL.md lists the common codes and what to do about each. - Adding a failure mode means adding a code: a
SponsonErrorwith an unlisted code does not type-check. - Changing a code’s exit code is a breaking change for scripts and CI, and should be treated as one.
- The MCP server returns the same envelope as the CLI, so the two cannot drift apart.