A claim-check run returns one of four verdicts, not a score. New in 0.8.0, a run that isn’t fully READY also comes with a fix packet: a structured list of exactly what’s wrong and, where possible, what to write next to close it. This page covers both: how the four verdicts get decided, and how to read a fix packet in a real report.
The floor’s own signal: binary, nothing else
It helps to keep two layers separate. The deterministic floor runs first, on every call, in both modes. It binds each required acceptance criterion to a mechanical check and asks only whether that check passed. The floor’s own internal signal is strictly binary: READY or BLOCKED. It never says “maybe.”
Above the floor, and only present in full mode, sits an approval layer: the intent fidelity judge, adjudicating whatever the floor left open. The judge can BLOCK, citing a divergence, or ABSTAIN, routing the item to a human. It never passes an obligation. INCONCLUSIVE belongs exclusively to this layer. The floor itself can never produce it.
How the four verdicts are decided
The full verdict is a fusion of the floor’s signal and the judge’s opinion (when one runs), applied in this order. Read it top to bottom: a rule only applies if none of the ones above it already fired.
- Any required acceptance criterion fails the deterministic floor: the verdict is
BLOCKED. This wins outright, regardless of anything else. A real, mechanical miss ends the conversation. - Floor is green, and the judge delivers a confirmed block on some criterion: the verdict is
BLOCKED. Same lane, different source: the judge cited a real divergence with a resolving pointer. See BYO-Judge. - Floor is green, nothing blocked, and a required criterion comes back abstained or otherwise undecided: the verdict is
INCONCLUSIVE. This is not a failure. It’s an honest “Shipmoor couldn’t decide this one, you decide,” and it routes straight to a person. - Floor is green, nothing blocked, and nothing left underspecified: the verdict is
READY. - Floor is green, nothing blocked, and something is still underspecified, but only in a non-required or optional criterion: the verdict is
READY WITH GAPS. You ship, and the open item is disclosed rather than hidden.
That ordering is why a floor BLOCKED can never be softened by a judge opinion, and why INCONCLUSIVE can only ever come from the layer above the floor. The floor itself never says “I’m not sure”; it says yes or a hard no. “I’m not sure” belongs exclusively to the judge, and only on a required item, and only once the floor has already said yes.
| Verdict | What it means | Exit code |
|---|---|---|
READY | Floor is green, nothing blocked, nothing required left open. | 0 |
READY WITH GAPS | Floor is green, nothing blocked, only optional or non-required items remain open. | 0 |
BLOCKED | The floor found a required miss, or the judge confirmed one. Either wins outright. | 1 |
INCONCLUSIVE | Floor is green, nothing blocked, but a required item came back undecided. Routed to a human. | 1 |
The remaining exit codes are operational: 2 for a usage error, 3 for an engine crash. See CLI reference.
One more gate, in front of all of this
The fusion above assumes the acceptance set itself is settled. If it isn’t, that’s checked first. An acceptance set that Claim Check auto-derived and wrote as a draft to .shipmoor/acceptance.yaml.draft has not been agreed to by anyone yet. Where the fusion above would otherwise land on READY, an uncommitted draft reads INCONCLUSIVE instead: an honest “this looks clean, but nobody has confirmed this is really the goal, so a person should.” Commit the draft as .shipmoor/acceptance.yaml to clear it. See Providing intent.
Worked example
Say the task was: add a refund endpoint that voids the Stripe PaymentIntent, marks the order refunded, writes an audit log entry, and can’t be run twice on the same order. The acceptance set has four criteria. Here’s what a real report might look like:
Claim Check BLOCKED · exit 1 · policy v3
✓ Order is marked refunded in the database.
✗ The Stripe PaymentIntent is voided.
requirement: stage1.floor.refund_voids_payment_intent · required · result: unsatisfied
reason: no call to PaymentIntent.cancel (or equivalent) found on the refund path
evidence: sha256:7ac1e4… (tests/refund/test_stripe_void.py::test_void_called, failed)
? An audit log entry is written for every refund.
requirement: stage3.byo_judge · required · result: abstained
rationale: no audit-log call is visible in the diff, but the write may happen inside
the shared AuditWriter helper this change doesn't touch — routed to a human
∅ A refund cannot be issued twice for the same order.
requirement: stage1.floor.refund_idempotency · required · result: unbound
tag: AC-REFUND-IDEMPOTENT-4f2a
Only one criterion has a hard, mechanical miss (the PaymentIntent isn’t actually voided), but the overall verdict is BLOCKED. That’s rule 1: a floor deny wins outright, regardless of what’s happening with the other three criteria. Notice the abstained and unbound criteria are still fully reported, they just don’t change a verdict a floor deny has already decided.
Alongside that report, the run writes a fix packet (schema claim_check.fixes.v2):
{
"schema": "claim_check.fixes.v2",
"fixes": [
{
"requirement": "stage1.floor.refund_voids_payment_intent",
"ac": "The Stripe PaymentIntent is voided.",
"reason": "no call to PaymentIntent.cancel found on the refund path",
"evidence": {
"type": "attestation",
"pointer": "sha256:7ac1e4...",
"detail": "tests/refund/test_stripe_void.py::test_void_called (failed)"
}
}
],
"route_to_human": [
{
"ac": "An audit log entry is written for every refund.",
"reason": "judge abstained: no audit-log call visible in the diff"
}
],
"author_checks": [
{
"ac": "A refund cannot be issued twice for the same order.",
"tag": "AC-REFUND-IDEMPOTENT-4f2a",
"instruction": "Write a test whose id or filename contains this exact tag. It must fail at the base commit and pass at the head commit."
}
]
}
fixes
One entry per requirement that actually failed: a floor deny, or a judge block the floor accepted as counted. Each entry names the requirement, which acceptance criterion it’s about, the reason, and a concrete evidence pointer, here an attestation digest plus the specific failing test. There’s always something to go look at; the packet never hands you a bare “unsatisfied.”
route_to_human
The acceptance items that came back abstained or otherwise undecided. This is the INCONCLUSIVE set made actionable: instead of a status you have to go dig for, it’s a short list of exactly which judgment calls need a person, and why the judge couldn’t close it alone.
author_checks
The authoring-loop payload. AC-REFUND-IDEMPOTENT-4f2a is an exact tag string. Write a new test, anywhere in your suite, whose id or filename contains that tag verbatim, for example test_refund_idempotency_AC_REFUND_IDEMPOTENT_4f2a.py. Claim Check finds it by an exact substring join, nothing fuzzy, no model involved in the match. It then confirms the test is real evidence, not a rubber stamp: run against the base commit it must fail, run against the head commit it must pass. A test that was already green before your change proves nothing and won’t be accepted.
Once that check is bound and passing, “a refund cannot be issued twice for the same order” graduates permanently: it moves from something the judge had to guess about into something the deterministic floor decides on its own, every run. Run it again after adding the test, and that line moves out of author_checks for good.
Claim Check confirms the check exists, is tagged correctly, and genuinely flips from failing to passing. It does not read the test’s assertions to judge whether they truly exercise the claimed obligation; the tag match and the fail-to-pass transition are the evidence.
The VSA still gets written
Independent of the fix packet, every run also writes a VSA, a Verification Summary Attestation, to .shipmoor/claim-check.vsa.json (or a path you choose with --vsa-out), with a per-item disposition for every acceptance criterion. The two artifacts describe the same run from different angles: the VSA is the durable, complete record; the fix packet is the actionable to-do list built from it. See Attestation artifacts for the full shape, including how a fix packet’s evidence pointer lines up with a VSA disposition.
Next
- Attestation artifacts: the VSA, the evidence fabric, and replay.
- Turning on the gate: when a
BLOCKEDorINCONCLUSIVEverdict should block a merge. - BYO-Judge: how the judge decomposes and audits a judgment criterion.
- Providing intent: drafts, commits, and confidence.
- CLI reference: the full
shipmoor claim-checkflag surface.