Not yet generally available. Blast Radius ships in Shipmoor CLI 0.9.0, which is still in the release process. This page describes shipped behavior on the release candidate; the command is not on the stable channel yet.
Blast Radius discloses rather than guesses, so most surprises arrive as a named reason rather than as silence. This page is that vocabulary, and what to do about each.
Not-checkable reasons
These appear in the not-checkable bucket. The entry keeps its committed place, is never quietly pinned, and never affects the exit code.
missing acceptance file
What it means. The repo has no frozen acceptance set at .shipmoor/acceptance.json, so no obligation has a content identity to compare against. Every realizes link reads this.
Fix it.
shipmoor claim-check freeze .
git add .shipmoor/acceptance.json && git commit -m "freeze the acceptance set"
Authoring .shipmoor/acceptance.yaml is not enough on its own. The YAML is the contract you write; the frozen JSON pin is what gives each obligation a stable identity, and it is what proves the set you run is the set you committed. Commit both.
unknown item id
What it means. A link’s item is not in the frozen acceptance set.
Usual causes. A typo in the id. An obligation that was renamed or removed. A link authored against a set that has since been re-frozen with different ids.
Fix it. Correct the id in .shipmoor/links.yaml, or remove the link if the obligation is genuinely gone. The next run pins it.
absent path
What it means. A code path in the link is not in the repo.
Usual causes. The file moved or was renamed. A typo. A path written relative to the wrong root — paths are repo-relative.
Fix it. Update the path. Note that a moved file is a genuine editorial decision, not a mechanical one: the link is a claim about which code realizes an obligation, and only you know whether the moved file is still that code.
The graph looks empty
A repo with no store
$ shipmoor blast
One honest line naming where links come from, and exit 0. This is correct behavior, not a failure.
Blast never scaffolds the store, because an engine-created store would be an approval nobody made. Author your first link by hand (Quickstart) or run Claim Check and let graduating obligations mint links for you.
A store exists but nothing classifies
Check, in order:
- Is
.shipmoor/acceptance.jsonpresent and committed? Without it, everyrealizeslink readsmissing acceptance file. This is by far the most common cause. - Do the
itemids match the frozen set? Look forunknown item idin the not-checkable bucket. - Do the
codepaths exist? Look forabsent path. - Is the change surface what you think it is? The default is the working-tree diff. If you have committed your change, use
--diff main...HEADor--from/--to, or--allto read the whole tree regardless of the diff.
No verifies edges at all
The verifies join reads claim_check.runners.test, because the rung it calls binds only a check something can run. Without a runner configured, no edges are derived — and the run says so rather than presenting an empty graph as a clean one.
claim_check:
runners:
test: "pytest {ref}"
If a runner is configured and edges still do not appear, the join is an exact string match on the obligation’s expected check tag (ac_01_7c9d21aa) in the test id or file name. A check whose name merely reads like the obligation earns no edge — that is the law, not a bug. Tag the check.
A comment-only edit made it suspect
Working as designed. Identity is content, not meaning. A comment-only or whitespace-only edit changes the bytes and therefore the identity.
The moment a comparator starts deciding which edits “really” changed the promise, it is making a semantic judgment, and there is no honest way to explain to a reader why one edit counted and another did not.
The relief valve is an explicit re-pin, which records your judgment in the committed diff:
shipmoor blast --repin ac-01.realizes.login
A verifies edge says the check is stale, but it just ran
Working as designed, and the line is carrying two clauses, not one.
When a change edits an obligation’s statement, the whole acceptance set is re-frozen and every expected tag moves with it. A check tagged against the previous statement is still shown, annotated as pinned against that statement.
Freshness and that annotation are independent axes. A check can have run for this change (fresh) and still be tagged against a statement the change superseded, because running a check does not re-tag it. Re-tagging the check resolves the annotation.
--repin did not do what I expected
| Behavior | Why |
|---|---|
| Stated no-op | The link is not currently suspect. In sync and silent links have nothing to accept. |
| Refused | The link’s endpoint was deleted. A deleted endpoint has no identity to pin, and recording a hollow success would be a lie in the store. |
| Usage error | The link id does not exist in the store. |
| Nothing written | --no-write was set. The intended change is disclosed; the file on disk is untouched. |
Usage errors (exit 2)
Malformed committed store
An unreadable store is a usage error, never an empty graph. An empty graph looks identical to a clean repo, so a parse failure that degraded to one would report “nothing to worry about” on a repo whose governance file is broken.
Check the YAML, and check for a verifies entry — a stored verifies kind is a loader error, not a tolerated extension, because those edges are derived every run and a stored one could outlive the check it names.
Malformed config
An unparseable config is a usage error, never a silent fall back to the default. So is an unrecognised suspect_threshold value.
If suspect_threshold: fial silently became warn, a team would believe they had a ratchet, CI would pass, and nobody would find out until a drifted obligation shipped.
Note the boolean spellings: off, no, n, and false in any capitalisation are all read as off. The true spellings (on, yes, true) are not thresholds and are rejected. See Configuration.
Conflicting selectors
--staged, --diff, --all, and the --from/--to pair are mutually exclusive. --from and --to are given together.
The command will not run at all
shipmoor blast is gated on the blast_radius entitlement. A caller without an active IC subscription gets upgrade guidance and the command does not run.
shipmoor capabilities
That lists what your license actually grants. If you have signed in and blast_radius is still absent, see Plans & tiers.
The report keeps showing up as a changed file
Ignore it — the report is generated, not authored:
.shipmoor/blast.v1.json
Keep the entry targeted. A broad .shipmoor/ entry would also ignore .shipmoor/links.yaml, and an ignored link store breaks the approval mechanic the whole feature rests on: committing the store is what approves it.
Leaving it untracked is harmless in the meantime. The report is classed as a Shipmoor control artifact and never counts as ungoverned code, and the count settles after one run rather than growing.