Summary
ARCHITECTURE.md documents exit codes as 0 clean, 1 vulnerabilities found, 2 error, and the tool behaves correctly. But because a successful scan that finds vulnerabilities exits 1, generic process supervisors and CI steps classify a healthy run as a failure.
Evidence
Our completed run exited 1 and was reported as failed by the supervising process, while its own final JSON envelope showed "errors": [], "degraded": false, and every step success. Distinguishing the two required reading the envelope.
This is intended behaviour and useful for gating, so it is a documentation and ergonomics issue rather than a bug.
Suggested fix
Surface the exit-code contract prominently in the README next to any CI guidance, since that is where users will hit it. Optionally add a --exit-zero-on-findings flag for pipelines that need to distinguish "the tool worked" from "the tool found something" without parsing stdout.
Observed on one full-pipeline run at production scale: a private TypeScript monorepo (Angular front end, NestJS back end), 1,351 analysis units from 866 source files, run with --verify and no --limit, ~6.5 hours wall clock. The engine was invoked directly (python -m openant scan …, Python 3.13); the Go CLI was not built, so none of this involves the Go↔Python envelope. Stage 1 detection and Stage 2 verification ran on Claude Opus 5; application context, enhancement and reporting on Claude Sonnet 5 — all seven phases routed through OpenRouter (anthropic/claude-opus-5, anthropic/claude-sonnet-5) via a locally-added openrouter provider adapter, on a build based on upstream 2ed78f6. Cost figures are expressed as proportions of the run total; absolute amounts are omitted deliberately, as is the identity of the scanned repository.
Summary
ARCHITECTURE.mddocuments exit codes as 0 clean, 1 vulnerabilities found, 2 error, and the tool behaves correctly. But because a successful scan that finds vulnerabilities exits 1, generic process supervisors and CI steps classify a healthy run as a failure.Evidence
Our completed run exited 1 and was reported as failed by the supervising process, while its own final JSON envelope showed
"errors": [],"degraded": false, and every stepsuccess. Distinguishing the two required reading the envelope.This is intended behaviour and useful for gating, so it is a documentation and ergonomics issue rather than a bug.
Suggested fix
Surface the exit-code contract prominently in the README next to any CI guidance, since that is where users will hit it. Optionally add a
--exit-zero-on-findingsflag for pipelines that need to distinguish "the tool worked" from "the tool found something" without parsing stdout.Observed on one full-pipeline run at production scale: a private TypeScript monorepo (Angular front end, NestJS back end), 1,351 analysis units from 866 source files, run with
--verifyand no--limit, ~6.5 hours wall clock. The engine was invoked directly (python -m openant scan …, Python 3.13); the Go CLI was not built, so none of this involves the Go↔Python envelope. Stage 1 detection and Stage 2 verification ran on Claude Opus 5; application context, enhancement and reporting on Claude Sonnet 5 — all seven phases routed through OpenRouter (anthropic/claude-opus-5,anthropic/claude-sonnet-5) via a locally-addedopenrouterprovider adapter, on a build based on upstream2ed78f6. Cost figures are expressed as proportions of the run total; absolute amounts are omitted deliberately, as is the identity of the scanned repository.