Reduce exit codes to invocation, transport and assertion - #86
Merged
Merged
Conversation
The exit code now answers whose fault the failure is: 71 means the invocation was rejected and no request was attempted, 92 means the request produced no usable response, and 93 means a response arrived and at least one assertion failed. Previously the code was a property of whichever layer happened to fail, which produced five codes with accidental boundaries: the same typo exited 103 with a 40-line usage dump from the command line but 71 with one line from the environment; a redirect bound and a failed assertion shared 93; and running with zero assertions -- detected before any request -- reported the transport-failure code (#25). Failures are now tagged with their category where they are understood, in doOnce, and carried to the exit in a typed error that survives the retry wrapper. Retry exhaustion reports the category of the last attempt. An untagged error falls into the transport bucket: wrong by at most one category, never reported as a usage mistake. Cobra's own error reporting is silenced in favour of the same one-line dief path every other error takes. BREAKING CHANGE: exit codes 91 and 103 are gone (both now 71), and transport failures moved from 93 to 92; 93 now always means an assertion failed. Scripts that only test for non-zero are unaffected. Closes #25 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TiEcuZhTyL5XbrCwyiHau8
korya
force-pushed
the
korya-feat-exit-codes
branch
from
August 9, 2026 03:19
3b588ac to
ccbc891
Compare
korya
marked this pull request as ready for review
August 9, 2026 03:22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A CI job reading the exit code could not tell an unreachable service from one that answered wrongly, nor a forgotten flag from a network failure. The five codes had accidental boundaries — assigned by whichever internal layer failed, not by what kind of failure it was: the same typo exited
103(with a 40-line usage dump) from the command line but71(one clean line) from the environment; a run with zero assertions reported the transport-failure code (#25); and the distinction the docs promise — "a broken service vs a broken invocation" — was exactly the one the codes didn't make.Solution
Three non-zero codes, each answering "whose fault?":
71— the invocation was rejected, no request attempted;92— no usable response arrived;93— a response arrived and an assertion failed.Before → After:
-X, URL)Failure category is decided where it's understood — inside the attempt, where transport and assertion failures part ways — and carried to the exit in a typed error that survives the retry wrapper via
%w/errors.As, so retry exhaustion inherits the last attempt's category. An untagged error defaults to the transport bucket: wrong by at most one category, never misreported as the caller's mistake. Cobra's own error printing is silenced so every failure exits through the same one-line path.This is a deliberate breaking change, pre-1.0:
91/103are gone and transport failures leave93. Scripts testing non-zero are unaffected; the README carries a migration note.Other Changes
TestE2EExitCodesnow covers all three categories plus both invocation channels — including the CLI-vs-environment typo pair that used to exit differently, and the zero-assertion case flipped out of the known-issues file per its protocol (closes Zero assertions is reported as "Cannot perform request" with exit 93 #25).Related:
🤖 Generated with Claude Code
https://claude.ai/code/session_01TiEcuZhTyL5XbrCwyiHau8