Skip to content
commission

Exit codes and the refusal shape

You will be able to branch correctly on what commission tells you — from a shell script, an agent loop, or an HTTP client — and know which failures are instructions to act on and which are genuine faults.

This page is the single definition of the codes and the refusal envelope. The CLI, the HTTP API, and the MCP server all render the same four outcomes; a surface-specific page that restated them would eventually restate one of them wrong.

The four codes

codenamewhat it meanswhat the caller should do
0okthe action succeededcontinue
1errorsomething unexpected brokereport it; do not retry blindly
2refusalthe action was understood and declinedread the message and fix it, then retry
3not foundthe named project, task, or record does not existcheck the id

The distinction between 1 and 2 is the one that matters. commission done refusing because a criterion is unticked is the system working exactly as designed. An agent that treats 2 as a crash will retry the identical command forever; an agent that treats it as an instruction fixes the thing it was told about and moves on.

Codes come from one place — EXIT in src/cli/runtime.ts — and every command goes through the same wrapper, so a new command cannot invent a fifth code.

What a refusal contains

Three fields, and every one of them exists so the reader does not have to guess:

fieldalways presentmeaning
messageyeswhat was refused, naming the offending thing by id
missingwhen applicableconcrete items, not prose — one per line
hintwhen a fix existsa runnable command with real ids already substituted

A hint never contains a placeholder you have to resolve from somewhere else, and never names a command or flag that does not exist — tests/cli-contract.test.ts checks hint text against the live command tree.

In text mode a refusal goes to stderr:

cannot complete myapp-1: 1 unchecked criteria; gate 'shipped' missing evidence [pr, test]
  missing: criterion 0: signature verified
  missing: evidence.pr
  missing: evidence.test
hint: tick each with 'commission check myapp-1 0' — only once it is actually true; attach with 'commission evidence myapp-1 pr=<ref> test=<ref>'; or --force to override (logged)

With --json, the same refusal is a single JSON object on stderr, and stdout stays empty:

{
  "error": "refusal",
  "message": "cannot complete myapp-1: 1 unchecked criteria; gate 'shipped' missing evidence [pr, test]",
  "missing": [
    "criterion 0: signature verified",
    "evidence.pr",
    "evidence.test"
  ],
  "hint": "tick each with 'commission check myapp-1 0' — only once it is actually true; attach with 'commission evidence myapp-1 pr=<ref> test=<ref>'; or --force to override (logged)"
}

error is the code name (refusal, not_found, error), not the numeric exit code. Branch on it if you are parsing; branch on the exit status if you are scripting. They cannot disagree — both are derived from the same CommissionError.

The same contract on every surface

CLIHTTPMCP
okexit 0200 / 201tool result
errorexit 1500isError
refusalexit 2422isError, refusal shape in the text
not foundexit 3404isError
stale versionexit 2409isError

HTTP returns 409 Conflict rather than 422 when a write lost an optimistic concurrency check — the caller did nothing wrong except read a moment too early:

{
  "error": "refusal",
  "message": "myapp-1 has changed since you read it: you have version 99, it is now 1",
  "missing": ["version 99"],
  "hint": "re-read it and decide again: commission show myapp-1 --json"
}

Over MCP the body of a refusal is the same three fields, rendered into the tool result and flagged isError, so a model reads the fix instead of a stack trace.

Branching on it

In a shell:

commission done myapp-1 || echo "exit $?"

An exit of 2 means the message on stderr told you what to attach. An exit of 3 means you have the wrong id. Anything else is a fault worth reporting.

Two things that deliberately do not refuse

  • Guidance drift. When the files a task was claimed under have changed, commission warns and exits 0. Refusing would strand work that is otherwise fine. See context and drift.
  • A non-preferred actor. Preference biases which task an actor is offered; it never reserves one. Being passed over produces an explanation, never a refusal. See execution policies.

Commission refuses about facts it owns. It reports about facts the repository owns.


Part of Reference.

This page is docs/reference/exit-codes.md in the Commission repository, rendered in place — the site keeps no copy of it. The repository is private, so there is no edit link to follow.