A structural scan tells you a change is well-formed. Claim Check checks whether it did what the task actually asked, the question a linter can’t answer. It catches a change that claims to do one thing and quietly does another, and it says so plainly when it has no way to check an obligation at all, rather than guessing. This walkthrough takes about five minutes: sign in, write a small acceptance set, run your first claim check, and read both a real READY verdict and a real BLOCKED one.
1. Sign in
shipmoor version
# shipmoor 0.10.0
shipmoor login
Every installation must be authorized. Free includes five Claim Check invocations per month; Pro is unlimited. Check authorization and usage with shipmoor auth status and shipmoor usage.
2. Write and approve an acceptance set
An acceptance set is a short list of atomic, plain-language obligations, “acceptance criteria,” that the change gets checked against. Scaffold a starter one:
shipmoor claim-check init
init writes a starter file to .shipmoor/acceptance.yaml. Open it and replace the placeholder with the obligations for this change:
goal: "Add a Stripe webhook handler for failed payments"
acceptance_criteria:
- "A handler is bound to the Stripe payment_intent.payment_failed event."
- "The webhook signature is verified before the handler runs."
This is author mode, the default: you write the acceptance set by hand, no model involved anywhere. (Skip this step and Claim Check can derive a starter set from your intent instead, by setting claim_check.authoring_mode to auto or hybrid in .shipmoor.yaml. What comes out is a draft at .shipmoor/acceptance.yaml.draft, not an approved set, until you’ve looked at it.)
Approval is nothing more than committing the file:
git add .shipmoor/acceptance.yaml
git commit -m "Add acceptance criteria for the failed-payment webhook"
Until you commit it, an otherwise clean run reads INCONCLUSIVE, not READY. A draft is never an approved pass. Once you do commit it, Claim Check pins it by hash so the intent can’t be swapped later without detection; more on that in Providing intent. See Reading the verdict for what each of the four verdicts means.
3. Run your first claim check
Pass the change’s intent as a one-line goal:
shipmoor claim-check . --intent-prompt "Add a Stripe webhook handler for failed payments (payment_intent.payment_failed)"
(The acceptance file’s own goal: line would also work as a fallback if you left this flag off; see Providing intent for the full precedence order.)
If the change actually wires the handler:
Intent: Add a Stripe webhook handler for failed payments (payment_intent.payment_failed)
Claim Check READY · exit 0 · policy v3
✓ A handler is bound to the Stripe payment_intent.payment_failed event.
✓ The webhook signature is verified before the handler runs.
VSA written: .shipmoor/claim-check.vsa.json
READY means every acceptance criterion held, checked against the diff itself, not against a description of the diff.
4. Watch it catch a miss
Now suppose the handler only pretends to do the job, say it logs the request body somewhere instead of processing the event. Run the exact same command against that change:
Claim Check BLOCKED · exit 1 · policy v3
✗ A handler is bound to the Stripe payment_intent.payment_failed event.
✓ The webhook signature is verified before the handler runs.
VSA written: .shipmoor/claim-check.vsa.json
Shipmoor caught the change that didn’t earn its claim: the badge reads BLOCKED, and the failing line names the exact obligation that wasn’t met, not a generic warning. The exit code follows the verdict directly, 1 here.
Coverage, honestly. A criterion with no probe behind it yet isn’t guessed at or waved through. Shipmoor reports it as “not yet checked” and leaves it open rather than passing it by default. Honest silence, not a pass. See Reading the verdict for how that interacts with the verdict.
5. The fully deterministic path: —floor-only
Every run above already used the deterministic floor: check adapters that read a git ref, a command’s exit code, a test pattern, nothing that calls a model. Pass --floor-only when you want to make that explicit:
shipmoor claim-check . --floor-only --intent-prompt "Add a Stripe webhook handler for failed payments (payment_intent.payment_failed)"
--floor-only and --agent are mutually exclusive: the entire point of floor-only is that no judge runs. What you get back is a reproducible local verdict from a frozen acceptance set and a fixed evidence set. Self-managed CI currently supports Scan only. See Verdicts & exit codes.
6. Optional: bring a judge
Both criteria above happened to have a deterministic probe behind them. Plenty of real obligations don’t; they’re judgment calls a probe can’t decide mechanically, and the floor is honest about that rather than guessing, reporting them as not yet checked instead. For those, you can opt in to a judge built from your own coding agent:
shipmoor claim-check . --agent claude --intent-prompt "Add a Stripe webhook handler for failed payments (payment_intent.payment_failed)"
Your own toolchain is the oracle here, not Shipmoor. Shipmoor binds the obligation to a check, runs it, and records the outcome. The judge can BLOCK by citing a specific divergence, or ABSTAIN and route to a human; it never hands back a passing verdict on its own, so any READY you see always rides the deterministic floor underneath it. Use bare --agent claude, --agent codex, or --agent cursor when that CLI is installed and authenticated, or pass a raw command for a custom invocation. See BYO-Judge.
Next
- Providing intent: where intent comes from, the precedence order, and how confidence is set.
- Reading the verdict: the four verdicts, exit codes, and the VSA in full.
- Turning on the gate: wiring a
BLOCKEDverdict into a merge check. - BYO-Judge: the intent fidelity judge in depth, K-sampling, and the two-phase decomposition then audit.
- CLI reference: the complete
shipmoor claim-checksurface. - What is Claim Check: the three layers underneath the verdict.