Exit Codes
The canonical 0/1/2 exit-code convention that makes ADLC gates deterministic in CI.
Exit Codes
Every ADLC gate speaks the same three-value language on exit. This is what lets CI wire a
dozen tools together and branch on a single number: a gate either passed, hit an
operational problem, or failed. Tools produce these codes through pass(), opError(),
and gateFail() in @adlc/core. They never call process.exit
with a bare number.
The canonical convention
The distinction between 1 and 2 matters: 1 is "I couldn't tell," 2 is "I looked
and the answer is no." CI treats 2 as a hard block on the change, while 1 signals a
setup or environment problem to fix before the gate can render a verdict. Collapsing the
two (for example, exiting 1 when a check actually fails) would let a genuine gate
failure read as a fixable hiccup.
In CI
Because the convention is uniform, gating logic is trivial and identical for every tool:
adlc spec-lint spec.md
case $? in
0) echo "pass, continue" ;;
1) echo "operational error — fix setup and re-run" ;;
2) echo "gate failed — block the change" ;;
esacConsistency across the suite
The convention holds across the toolkit. A few patterns worth knowing:
- Multi-verb tools map each verb to the same three codes. For example
gate-manifestrecordexits0on a successful append and1on bad--dataJSON, whileverifyexits0when the hash chain is intact and2when it is broken. --prompt-onlyalways exits0. Printing a prompt is not a gate verdict; it is a successful "here is the prompt" response, so every LLM-backed tool exits0in this mode regardless of the underlying change.- Help and usage. Requesting
--helpexits0; invoking a tool with missing required arguments prints usage and exits1(an operational error: the tool could not run).