In CI, Shipmoor is the same binary running the same scan it does on your laptop, installed onto your runner, scoped to the change, exiting with a stable code. CI uses shipmoor scan: one process, one scan, then exit. (The Agent Harness and the IDE’s language server are for the local dev loop, where they run continuously as you edit; CI wants a single deterministic pass instead.) No source is uploaded anywhere; the scan runs in your runner on your infrastructure, and the only thing handed to GitHub is the SARIF evidence you choose to upload.
What CI catches
A CI scan catches the generated-code failure modes a change introduces (phantom imports, hallucinated APIs, stub handlers that return success without doing the work) before they merge. It reports findings against the change, never against legacy code you didn’t touch. On a license, it can also carry Claim Check, which catches changes that did not earn their claim and says so when it has no probe to check a given expectation.
Everything on this page runs locally in your runner and works on any plan, including free.
The recommended pattern
Three decisions make a good Shipmoor CI gate, regardless of CI provider:
- Scope to the change. Scan the merge-base diff (
--diff origin/main...HEADwithfetch-depth: 0), or use--changedfor staged and unstaged work, so PRs are gated on what they introduce. See Gating & policy. - Pick the threshold deliberately.
--fail-on highis the sensible default: hallucinated dependencies block, debug output doesn’t. - Emit evidence either way. Write SARIF (and a markdown summary) on every run, and upload it even when the gate fails the job; that’s what populates code scanning.
shipmoor scan --changed \
--sarif --output shipmoor.sarif \
--markdown-summary "$GITHUB_STEP_SUMMARY" \
--fail-on high
Installation on a runner is the same one-liner as anywhere else (curl -fsSL https://dl.shipmoor.dev/install.sh | bash); the free Community scan needs no account or secret in CI. Verify the version on the runner with shipmoor version (current release: 0.5.1).
The gate: exit codes
The gate is the exit code. The most common CI mistake is treating 1 as a tooling failure; it isn’t. A 1 means the gate worked and a blocking finding got through your threshold.
shipmoor scan --changed --fail-on high
# exit 0 → pass
# exit 1 → a finding met the threshold (or the Claim Check gate blocked, if you opted in)
| Code | Meaning | CI handling |
|---|---|---|
0 | Clean; nothing met the threshold | Pass |
1 | A blocking finding at the threshold (or the Claim Check gate, when opted in) | The gate working. Fail the check; the SARIF/JSON is still complete. Upload it. |
2 | Usage/config error | Fix the invocation |
3 | Scan failed | Report as a tooling error |
See Output formats & exit codes for the full contract.
Output formats
The same scan emits three shapes from the same evidence, so you can read it as a human and feed it to machines in one run:
- Human: the default grouped, blockers-first console output.
scan.v1JSON (--json): deterministic machine output for your own tooling.- SARIF (
--sarif): feeds GitHub code scanning so findings land in the Security tab and annotate the PR diff.
Add --markdown-summary "$GITHUB_STEP_SUMMARY" to write a readable summary straight into the GitHub job summary, so reviewers see the verdict without opening the log. See GitHub Actions for the full workflow and SARIF & code scanning for getting findings into the Security tab.
GitHub Actions
Two ready-made options, a copy-paste workflow and a composite action, are covered in GitHub Actions. Any other CI (GitLab, Buildkite, Jenkins, a bare cron) follows the same pattern: install, scan the diff, act on the exit code, keep the SARIF/JSON as an artifact.
Claim Check in CI
On an IC plan, the same CI scan can carry the claim check: pass the task’s intent (--intent, or a .shipmoor/intent.txt your workflow writes) so Shipmoor checks whether the change did what it was asked to do.
By default Claim Check in CI is advisory: it reports the verdict and never changes your exit code. To let it block a merge, opt in with a verdict policy:
shipmoor scan --changed \
--intent "Add a Stripe webhook handler for failed payments" \
--verdict-policy .shipmoor/verdict-policy.yaml
Roll it out with --would-block first; that computes the gate as if enabled and prints what would have blocked while still exiting 0, so you can watch its footprint before you enforce. The full opt-in path, what can and cannot block, and the policy file live in Turning on the gate.
Not yet available: Team / server-side gates. Shared baselines, managed team policy, and server-side PR gates are planned for a Team tier built on this same local-first core. They are not released today. Everything on this page works now on any plan, including free.
Next
- GitHub Actions: the workflow and the composite action.
- SARIF & code scanning: getting findings into the Security tab.
- Turning on the gate: making Claim Check block in CI.