Skip to content

fix: usage errors exit 3, disjoint from the honest lane - #30

Merged
chaoz23 merged 2 commits into
mainfrom
fix/usage-errors-exit-3
Aug 25, 2026
Merged

chaoz23 merged 2 commits into
mainfrom
fix/usage-errors-exit-3

Conversation

@chaoz23

@chaoz23 chaoz23 commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Closes #1's exit-semantics half. This makes table-kit fully conformant — the first member other than srdcheck to reach PASS.

The defect

FAMILY.md clause 1: exit 2 is the honest lane — a cannot-adjudicate answer a consuming agent routes to a human without retrying. A malformed call is the opposite. table-kit returned 2 for both.

The fix, and why it needed care

ConfigError was doing double duty. Unknown commands and flags raised it — but so do legitimate refusals that correctly exit 2 (init: file exists; consumed: id not open). A blanket change would have moved those too and broken the honest lane instead of fixing it.

Added UsageError, a ConfigError subclass caught first in main(), so only malformed calls move:

invocation before after
init (fresh) 0 0
qc findings 1 1
init (file exists) — a real refusal 2 2
unknown command 2 3
unknown flag 2 3
report --zzz-bad-option 2 3

Docs

SKILL.md and tool.json updated — and while there, the exit-2 lane is now documented honestly. It previously read "can't do that" with usage-error examples only, omitting the incomplete-coverage lane that qc and report actually emit (detector.py:614-619, report.py:496-503). That omission was the other half of #1.

New tests assert usage errors exit 3 and that init on an existing file stays on 2, because over-applying this change was the real risk.

Pin bump

Bundled, and required: chaoz23/srdcheck#88 removed this waiver, and the ratchet fails a waiver that no longer fires.

After this, table-kit has no waivers at all. Remaining issues (#24, #25, #26) are hygiene the gate doesn't probe.

🤖 Generated with Claude Code

chaoz23 and others added 2 commits August 25, 2026 13:31
FAMILY.md clause 1: exit 2 is the honest lane -- a first-class
cannot-adjudicate verdict a consuming agent routes to a human WITHOUT
retrying. A malformed invocation is the opposite: the caller should fix the
call and retry.

Sharing one code made the two indistinguishable, so an agent following the
docs escalated its own bad calls to a human as if they were rulings. Exit 3
is the family precedent, set by srdcheck.

ConfigError was doing double duty: unknown commands and flags raised it, but
so did legitimate refusals that correctly exit 2. Added UsageError (a
ConfigError subclass, caught first) so only malformed calls move. Verified
`init` on an existing file still exits 2.

SKILL.md and tool.json updated to match -- a code change that leaves the
contract prose stale is the exact defect this whole effort exists to catch.
Regression tests assert usage errors exit 3 AND that legitimate refusals stay
on 2, because the risk here is over-applying the change.

Also bumps the family-conformance srdcheck pin to the first baseline that no
longer waives HONEST_LANE_OVERLOAD; both must land together or the ratchet
fails with STALE BASELINE.

Closes #1

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test_unknown_command_refuses and the unknown/duplicate/missing-flag cases
asserted exit 2. They were right for the old contract; the contract changed.

Also moves duplicate-flag and flag-missing-its-value onto UsageError. Those
are malformed calls in exactly the same sense as an unknown flag, and leaving
them on 2 would have made the separation arbitrary.

Verified the boundary holds: init-on-existing-file, consumed-id-not-open,
report-insufficient-data and the symlink-ledger refusal all still exit 2.
Those are verdicts, not bad calls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chaoz23
chaoz23 merged commit b11cb61 into main Aug 25, 2026
7 checks passed
@chaoz23
chaoz23 deleted the fix/usage-errors-exit-3 branch August 25, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TK-001] Make QC/report coverage, exit semantics, and finding lifecycle authoritative

1 participant