Ninth slice — the audit item that was never audited.
Gap: main() -> Result meant every failure exited 1 — to a script, “server
down” was indistinguishable from “server refused” and from “you typed it
wrong”.
What changed: main returns ExitCode with a documented contract — 0
success, 1 API/application error, 2 usage error, 3 unreachable, 4
unauthorized. The load-bearing distinction lives in send(): no HTTP response
is a different failure class than an HTTP rejection. An empty monitors edit
is a usage error caught client-side (exit 2), not a round-tripped no-op (exit
1). Results go to stdout, errors to stderr — never mixed.
Proof: tests/exit_codes.rs spawns the real monitor binary
(CARGO_BIN_EXE_monitor) against a pure-std TCP stub server — no dev-deps —
covering a bad subcommand, an empty edit, a closed port, a 401, a 409, and the
two success paths. The contract is pinned end-to-end, not asserted from inside.