Skip to content

Fix the CLI's failure paths, and test them - #26

Merged
Tippel-AI merged 1 commit into
mainfrom
fix/cli-failure-paths
Aug 31, 2026
Merged

Fix the CLI's failure paths, and test them#26
Tippel-AI merged 1 commit into
mainfrom
fix/cli-failure-paths

Conversation

@Tippel-AI

Copy link
Copy Markdown
Owner

Closes #22. Fourth step of the audit remediation.

The defect

evaluate() sat outside the try/except that turns configuration problems
into one sentence and exit 2. So a rulepack asking for a check this build cannot
perform reached the user as a traceback and exit 1.

Exit 1 is this tool's word for a rule failed. A pipeline could not tell

your endpoint is not compliant

from

markproof fell over

— which is exactly the ambiguity probe_failure_finding() exists to prevent one
layer down. The engine is careful to make "could not be checked" a distinct,
loud outcome; the CLI undid that guarantee one frame up.

The reachable case is not exotic: the README's own example config on a base
install, where MPF-T-001 needs the [synthid] extra.

Why it survived

cli.py was at 21% coverage while the library around it was near 90%. The
aggregate read as healthy; the distribution was the problem, and the untested part
was the one every adopter's pipeline depends on.

What is now tested

tests/test_cli.py, through the real Typer runner rather than by calling
internals:

Area Notable case
exit codes an unreachable endpoint exits 1, never 0 — the worst outcome this tool can have is a green run that checked nothing
failure paths malformed YAML, non-mapping config, unknown rulepack (the error names one that exists), missing pattern file — each asserting no traceback
reports none written without --report-dir; with it, the report names the endpoint and carries a 64-char rulepack digest
signing a full keygen → sign → verify-report round trip, and the guard nothing had ever exercised: when signing fails, no file is written at all
verify-report a tampered verdict rejected; non-JSON and missing files answered with a sentence
keygen the private key is not group- or world-readable

The signing guard is the one worth calling out. Writing an unsigned report under a
name the caller asked to have signed would produce something that looks like
evidence and is not; the code was right and unprotected.

One test needed correcting while writing it: the tamper case originally rewrote a
PASS to PASS, which would have passed while proving nothing. It now changes a
count and the target name, and asserts the signature breaks.

Numbers

cli.py 21% → 88%. Suite overall 82% → 89%. 429 tests, ruff + mypy strict
clean.

🤖 Generated with Claude Code

Closes #22.

`evaluate()` was called outside the try/except that turns configuration problems
into a sentence and exit 2. So a rulepack asking for a check this build cannot
perform — a missing pattern file, an unimplemented check type, the SynthID extra
absent — reached the user as a Python traceback and exit 1.

Exit 1 is this tool's word for "a rule failed". A pipeline could not tell "your
endpoint is not compliant" from "markproof fell over", which is exactly the
ambiguity probe_failure_finding() exists to prevent one layer down: the CLI undid
the guarantee the engine was careful to give. Evaluation now sits inside the
guarded block, and the check-layer exceptions map to exit 2 with a readable
sentence — KeyError included, whose default stringification is its quoted
argument and therefore a filename with no verb.

None of this was caught because cli.py sat at 21% coverage while the library
around it was near 90%. The aggregate looked healthy; the distribution was the
problem, and what was unprotected is precisely what an adopter depends on.

tests/test_cli.py covers, through the real CLI runner:

  exit codes    0 for clean, 1 for a failed rule, 2 for a run that could not
                happen, and — the one that matters most — an unreachable endpoint
                exiting 1 rather than passing quietly.
  failure paths malformed YAML, a config that is not a mapping, an unknown
                rulepack (whose error names one that exists), a missing pattern
                file. Each asserts no traceback reaches the user.
  reports       that none is written without --report-dir, that both files appear
                with it, and that the report names the endpoint and pins the
                rulepack digest.
  signing       a valid key round-tripping through verify-report, and the guard
                that had never been exercised: when signing fails, nothing is
                written at all. An unsigned file under a name the caller asked to
                have signed would look like evidence.
  verify-report a tampered verdict rejected, a non-JSON file and a missing file
                answered with a sentence.
  keygen        that the private key is not group- or world-readable.

cli.py 21% -> 88%; the suite overall 82% -> 89%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Tippel-AI
Tippel-AI merged commit f6391ed into main Aug 31, 2026
8 checks passed
@Tippel-AI
Tippel-AI deleted the fix/cli-failure-paths branch August 31, 2026 19:15
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.

A missing optional extra ends in a traceback and exit 1, which means "a check failed"

1 participant