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.
A link relates exactly two endpoints
Every link carries a kind:
| Kind | Relates | Stored? |
|---|---|---|
realizes | an obligation → the code paths that realize it | Yes, in the committed store |
documents | a doc section → the code paths it documents | Yes, in the committed store |
verifies | a check → the obligation it verifies | Never. Derived every run |
A verifies entry appearing in a committed store is a loader error, not a tolerated extension. The reason is in the next section.
Today
documentslinks are compared at whole-file granularity. Section-level anchors arrive with docs verification, and the documented-surface gaps list is disclosed as not-checked rather than rendered as an empty list.
Pins and what identity means
Every stored link is pinned with the content identity of both endpoints. At run time:
- both endpoints match their pins → silent
- exactly one moved → suspect, with the stale side named
- both moved in the same change → in sync, and the run re-pins automatically
Identity is content, not meaning. A comment-only or whitespace-only edit changes the bytes and therefore the identity, so it suspects.
That is not a rough edge to be smoothed out later. It is the direct consequence of the no-lexical-inference law: 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 is a human decision recorded in the committed diff, never a smarter comparator.
An obligation’s identity is its frozen statement hash, taken from .shipmoor/acceptance.json and computed by the same API Claim Check uses. There is one hashing truth, never a reimplementation.
The committed store
The durable store is a committed file, .shipmoor/links.yaml, sitting beside the acceptance set and governed by the same approval mechanic: committed means approved.
The graph is repo state, reviewed in pull requests like everything else. Two consequences follow, and both are deliberate:
- Blast never scaffolds the store. An engine-created store would be an approval nobody made. A repo with no store gets one honest line naming where links come from.
- Blast never writes the acceptance file. The obligation side of every link is read-only to this feature.
links.yaml is the only durable state Blast Radius owns. Store writes are minimal-diff so the change you review is the change that happened, and any change to the links.v1 shape is a declared migration, not drift.
Authoring a realizes link
You write the endpoints; the run writes the pins. Add an entry with no pin fields:
# shipmoor links.v1 store. Committed means approved; edits are reviewed in the PR.
links:
- id: ac-01.realizes.login
kind: realizes
item: AC-01
code:
- app/login.py
provenance: authored
The next run resolves both endpoints and writes the pins back, disclosed as a first pinning rather than a re-pin. Nothing drifted; the endpoints simply had no recorded identity yet, so the entry gains no re-pin history. From then on the link is classified against those pins on every run.
A declaration the run cannot honor is disclosed, never dropped
| Reason | What it means |
|---|---|
unknown item id | The item is not in the frozen acceptance set. |
absent path | A code path is not in the repo. |
missing acceptance file | The repo has no frozen set. Write it with shipmoor claim-check freeze. |
In every case the entry keeps its committed place and lands in the not-checkable bucket carrying that reason. It is never quietly pinned and never silently discarded. Fix the id or the path and the next run pins it. See Troubleshooting.
Minted links: the graph that builds itself
You do not have to author every link by hand.
When a Claim Check run graduates an obligation to the deterministic floor — decided satisfied through a bound check — that round’s change surface is causal evidence for which code realizes the obligation. The run mints or refreshes a realizes pin from it, labeled minted (claim-check graduation).
The rules are narrow on purpose:
- Post-verdict and floor-only. Only obligations the floor decided satisfied through a bound check mint. A judge outcome never mints, and neither does an underspecified obligation.
- Authored outranks minted. A mint never overwrites an authored entry; the collision is a disclosed no-op.
- Minting requires
blast_radiusalongsideclaim_check. For a caller holding onlyclaim_check, its absence is a disclosed skip. For a caller holding neither, the run is silent, because a free run must never advertise a paid feature the caller has shown no sign of owning. - The verdict is unaffected. Minting on, off, or failing yields byte-identical verdicts, exit codes, and outputs, apart from the disclosed notice.
This is why a repo’s graph thickens with every round it runs, without anyone doing traceability work as a separate task.
Verifies edges are derived, never authored
An obligation’s expected check tag comes from the frozen acceptance set — it looks like ac_01_7c9d21aa — and a check claims that obligation by carrying the tag in its test id or file name.
That is an exact string join and nothing else. A check whose name merely reads like the obligation earns no edge. This is the law applied to the check side of the graph.
The reason these edges are never stored is that they are cheap to recompute and expensive to be wrong about. A stored verifies edge could outlive the check it names, or the tag it joined on, and there would be no way to tell from the file. Deriving them every run means the graph cannot carry a stale claim about what verifies what.
Freshness: three states, never invented
Each derived edge carries exactly one freshness state, read from evidence a Claim Check run already recorded. Blast never runs a check and never invents freshness.
| State | What it means |
|---|---|
fresh | Evidence was recorded for this change. |
predates this change | Evidence exists, but against a different change; a re-run is advised. |
no recorded evidence | Nothing has been recorded, stated as such. |
Rerun advised is advisory and is never an exit-code input.
Freshness and staleness are two axes, never merged
When a change edits an obligation’s statement, the whole acceptance set is re-frozen and every expected tag moves with it. Checks tagged against the previous statement are still shown, annotated as pinned against that statement, rather than vanishing from the graph.
Freshness and that annotation are independent and are shown as two clauses. A check can have run for this change and still be tagged against a statement the change superseded, because running a check does not re-tag it. Merging the two would produce a line that is false in one of the two directions. Re-tagging the check resolves the annotation.
One check per obligation, disclosed
Each obligation shows one verifying check. The join returns a single binding per obligation, so when several checks carry the same expected tag the graph names one of them — and the run discloses that, rather than letting one edge imply there is only one check.
Configuration the join depends on
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.
Next
- Classification and re-pinning — the truth table and the two ways a pin moves.
- Gaps — obligations with no
realizesentry, and code no link pins.