CLI¶
ckdn --version prints the version. python -m ckdn is equivalent to the
ckdn entry point.
Per-command flags — they follow the subcommand, not ckdn itself:
--config PATH, --cwd DIR (working directory for subprocesses and relative
runs_dir; else CKDN_CWD). Every command accepts them except
schema, which reads no config. ckdn init accepts them too, and resolves its
target through the same function the reading commands use — so the command that
writes the config and the ones that read it cannot disagree about where it
lives.
| Command | Purpose |
|---|---|
ckdn run <atomic> [--quiet] [-- extra…] |
run one check; compact digest on stdout |
ckdn run <alias> [--fail-fast] [--quiet] |
run its members in config order → aggregate on stdout |
ckdn run --all [--fail-fast] [--quiet] |
run every atomic check in config order → aggregate on stdout |
ckdn run … --gate |
exit reflects the baseline gate, not execution |
ckdn baseline <check> |
record a check's current findings as the accepted baseline |
ckdn show [run-dir] |
pretty-print a stored digest (latest default) |
ckdn list [-n N] [--json] |
recent runs (text, or {"runs": […]} with --json) |
ckdn checks [--json] |
configured checks (text, or {"checks": […]} with --json) |
ckdn gc [--keep N] |
prune old run directories |
ckdn init |
write starter ckdn.toml at the resolved config path |
ckdn schema [id] |
print a packaged JSON Schema, or list schema ids |
ckdn doctor [--strict] |
pre-flight diagnostics (executables on PATH + parser/command fit) |
ckdn annotate [ref] [--format F] |
render a stored digest's findings as github annotations or sarif |
ckdn verify-config [--locked] [--lock-file PATH] |
validate command policy (+ optional ckdn.lock.toml) |
ckdn lock-config [-o path] |
write command SHA-256 lock file for CI |
--gate works on an alias and on --all too: the aggregate gate combines the
members', worst-first — unavailable > fail > pass.
ckdn baseline needs baseline = "…" under [run]; without it there is
nowhere to record, and it exits 2 rather than inventing a path.
-- extra… is for atomic checks only: an alias rejects extra arguments (exit
2), listing its members so you can run the one you meant. --fail-fast is the
mirror image — it stops a sequence, so it applies to an alias or to --all,
overrides an alias's configured fail_fast, and is rejected on a single
atomic check (exit 2: one command is not a sequence). Only the flag's presence
overrides; there is no --no-fail-fast.
Alias stdout is only the aggregate — and stdout is the only copy of it.
Run directories are written per member, so the aggregate is never stored and
latest ends up pointing at the last member that ran — with fail_fast
(the default) that is the member that failed, not the last one listed: after
ckdn run <alias> or ckdn run --all, ckdn show prints that member's
digest, not the aggregate.
Redirect stdout if you need the aggregate afterwards.
list and checks default to human-readable tab-separated text; add --json
for machine consumption (same {"runs": […]} / {"checks": […]} shape the
MCP list_runs / list_checks tools return).
Exit codes¶
ckdn run reports the check: the command's own nonzero code passes through,
and rc == 0 with a non-green digest becomes 1 — ckdn may downgrade green,
never upgrade red. A code outside 1–255 (a signal death, e.g. -9) becomes
1; it is not saturated to the nearest bound. The full contract, including
the synthetic 124 / 126 / 127 / 130, is in the
status model.
ckdn uses 2 for every refusal to start — nothing ran, so there is no
verdict. It is not reserved: ckdn run passes the command's own code through,
so a check whose tool exits 2 also exits 2. What separates them is the
evidence — a refusal writes no run directory and no digest, while a red check
writes both. Exit 2 covers:
- usage errors (unknown flag, missing subcommand);
- config errors — no
ckdn.toml, invalid TOML, a mistyped value; - an unknown check name, or an unknown run id for
show/annotate; -- extrapassed to an alias or to--all;--fail-faston a single atomic check;- a run-lock conflict — that check is already running in this workspace;
ckdn baselinewith nobaseline = "…"under[run];ckdn initwhen the target already exists, or its parent directory does not.
doctor and verify-config exit 1 in the opposite situation: they ran, and
found problems. That is a verdict, not a refusal.
CI annotations¶
ckdn annotate projects a stored digest's findings onto a CI surface without
running anything or changing the run's status:
ckdn run pytest || { rc=$?; ckdn annotate || true; exit $rc; } # ::error, still red
ckdn annotate --format sarif > ckdn.sarif # upload to code scanning
The braces are not decoration. annotate exits 0 whenever it renders a
digest — it is a projection, not a verdict, and only fails (2) when it cannot
find the run — and a CI step takes the code of its last command.
Piping the run into a bare || ckdn annotate therefore ends the step on that
0 and reports a failing run as green: the exact false green ckdn exists to
prevent. Capture the run's code and re-raise it.
--format github(default) emits GitHub Actions workflow commands (::error file=…,line=…::message), one per finding, so failures show inline on the pull request.--format sarifemits a minimal SARIF 2.1.0 document (tool driverckdn, one rule per findingkind) for a code-scanning dashboard.
annotate renders one stored digest. Since an alias stores no aggregate, the
default latest after ckdn run <alias> or ckdn run --all is the last
member that ran — every other member's findings are silently absent. Pass the
run id of each member you want annotated.