Every scan produces the same evidence; you choose how it’s rendered. Human output is for you, deterministic JSON is for scripts and agents, SARIF is for code scanning — and a stable four-code exit contract makes all three safe to gate a build on.
Human output (default)
shipmoor scan --changed
The default renderer leads with a one-line verdict, groups findings by file with blockers first, and ends by naming the exact command to run next (shipmoor explain <id> for the first blocker, plus the rerun command). Color is suppressed automatically when stdout is not a TTY, when NO_COLOR is set, or with --no-color.
JSON — shipmoor.scan.v1
shipmoor scan --changed --json --output shipmoor.json
The JSON report is a stable, versioned contract (shipmoor.scan.v1) — the same document the Agent Harness, the IDE extension (preview), and your own CI scripts consume. Key fields per finding:
| Field | What it holds |
|---|---|
id | Stable finding ID (SHM-…) — usable with shipmoor explain <id> --from <report> |
rule_id | The rule, e.g. python.phantom_import |
severity | critical / high / medium / low |
path, start_line | Where |
message, recommendation, evidence | The what, the fix, and the proof |
subtype | Finer classification, e.g. hallucinated_package |
change_status | introduced / modified / preexisting / unknown for diff and patch scans |
fingerprint | Stable hash for deduplication across runs |
When --json writes to stdout (no --output), stdout contains only the JSON document; diagnostics go to stderr. That discipline is what lets agents and pipelines parse the output without scraping.
SARIF — for code scanning
shipmoor scan --changed --sarif --output shipmoor.sarif
SARIF 2.1.0, ready for GitHub code scanning upload. The same stdout/stderr discipline applies. See SARIF & code scanning for the workflow.
Markdown summary
For CI job pages, --markdown-summary <path> writes a human-readable digest alongside whichever main format you chose:
shipmoor scan --diff origin/main...HEAD \
--sarif --output shipmoor.sarif \
--markdown-summary "$GITHUB_STEP_SUMMARY"
The exit-code contract
Four codes, stable across versions — this is the contract the Agent Harness and CI integrations are built on:
| Code | Class | Meaning |
|---|---|---|
0 | clean | No gate fired. The report is present. |
1 | threshold | Findings met the gate. Not an error — the JSON/SARIF is still written and complete. |
2 | usage | Bad arguments or config. Error on stderr, no report. |
3 | scan failed | The scan itself crashed. Error on stderr, no report. |
Two rules of thumb for anything consuming these codes:
- Treat
1as a signal, not a failure. It means the gate worked. Parse the report and act on the findings; don’t route it to your “tooling broke” path. - Reserve error handling for
2and3. Those are the only codes where no report exists.
On an IC plan with the Claim Check gate enabled, exit code 1 unifies both gates: the scan exits non-zero if either the finding threshold or the claim-check gate fires. See Turning on the gate.
Note:
shipmoor upgradehas its own, separate exit codes for install plumbing (20network,21checksum, …) — see Installation. The0/1/2/3contract above is specifically thescansurface.
Next
- Gating & policy — choosing what makes exit code
1fire. - GitHub Actions — the copy-paste workflow.
- Output contracts & schemas —
scan.v1,capabilities.v1,identity.v1in full.