Blast-radius and provenance analysis for Python - what breaks if I change this symbol, and where a pasted snippet came from, read from the bytes on disk right now.
Project description
blast: A Blast Radius Tool for Coding Agents
- Overview
- Why this over grep?
- Support
- Install
- Reading a Report
- What Outranks What
- Recipes
- Provenance
- Contracts
- Build and Test
- Where This Came From
Overview
An agent about to change a symbol has one question, and it is not "where does this string appear". It is "what breaks". Those are different questions, and a grep answers only the first.
blast SYMBOL answers the second. It reports where the symbol is declared,
which functions lean on it, what it leans on in turn, the comments that
describe it and will be wrong once you edit it, and the files that historically
move alongside it.
Every edge is derived on demand from the corpus as it is right now. There is no project model to configure and no graph to rebuild, so a file another agent saved a second ago is already in the answer, and a symbol in a language nobody taught the tool about still resolves.
That last part is the trade. A compiler front end would be more precise on the one language it was built for; blast is parser-free and reads byte shape, so it covers every language in a polyglot tree at once and pays for it with heuristics it is required to label rather than hide.
Why this over grep?
Use blast when you are about to change code and want the change's footprint before you make it. It is built for an agent editing an unfamiliar tree, and for a human doing the same thing at 2am.
Reach for gist instead when you know the pattern and want the matching lines. gist is a ripgrep-parity indexed search, and a blast report is a much more expensive thing to compute than the list of lines you actually asked for.
Reach for relate instead when the question is about similarity rather than a symbol - what resembles this file, what repeats across the tree, which files jointly explain a task.
Reach for a language server instead when you have one, the tree is one language, and the project builds. blast is what you use when any of those three is false.
Support
File a bug against this repository when a report is wrong: a call site blast
missed, a row it invented, or a definition it failed to find. Paste the symbol
and the --json report, which is the whole of what blast believes.
File it against irregex when the fault is in matching itself - a pattern that should match and does not, or a Unicode boundary that reads wrong. irregex is the engine underneath all three faces, and a matching bug reproduces there in isolation.
File it against relate when the fault is in similarity - a twin that is not a twin, or a provenance phrase attributed to the wrong file. The kinship and attribution kernels live there; blast only composes them.
Report a vulnerability through the process in SECURITY.md, never as a public issue.
Install
Build from source with a Zig toolchain, or take the CLI from a release.
zig build # blast → zig-out/bin/blast
On Windows, the installer builds blast.exe, places it in a per-user directory,
and adds that directory to the user PATH without elevation:
.\install.ps1
The binary is standalone for the blast verb. provenance additionally reads
the codex shelf that relate writes, so install
relate if you want attribution.
Reading a Report
A report has six sections, and they are ordered by how likely you are to have to edit them. Run it on any symbol to see the shape.
blast runBlast
Every section is capped, so a report cannot flood a context window however
popular the symbol is. Pass --budget N to cap it harder in approximate
tokens; what gets trimmed is counted into stats.omitted rather than silently
dropped.
Exact evidence and statistical evidence never mix. A line number and a def/use classification come from matching the bytes; a twin distance comes from compression kinship; they stay in separate fields and are never fused into one relevance score you cannot take apart.
Seed
The seed is where the symbol is declared, plus a guess at what kind of thing it is. A symbol declared in several places lists all of them, strongest declaration first.
Only a source file can declare anything. A definition list in prose and a key in a config file both wear shapes that read like declarations, so they are recorded as mentions and can never pose as the symbol's home.
Dependents
Dependents are the references, and they are the reason you ran the tool. Each row names the file, the line, the enclosing function when there is one, and whether that line uses the symbol or redefines it.
A reference outside a function body counts. Registries, dispatch tables, export lists, route maps, and dependency-injection wiring are exactly the edges that break a build when a name moves, and they live at file scope where a function-shaped search cannot see them.
A reference inside a string literal counts too, marked str. Names get wired
by string in reflection, in SQL, and in route tables, so dropping them loses
real edges - but a string is weaker evidence than a call, and the report says
which it found rather than flattening the two.
Dependencies
Dependencies invert the question: what does the seed itself lean on. This is the list of things whose change would break it, which is what you want when the symbol is misbehaving rather than moving.
Resolution is deliberately conservative. The seed's own parameters and locals
are excluded, a qualified head.member resolves only inside the module its
head names, and a name that a whole package declares is treated as ambient
rather than as a dependency.
Only a function has a body to lean on anything, so a type or a value reports no dependencies at all rather than reporting the neighboring words of its declaration.
Comments
Comments that mention the symbol are the documentation your edit is about to falsify. This is the stale-doc surface, and it is the part of a change that review catches last and users notice first.
A mention inside a comment is never counted as a dependent, and a mention inside a string is never counted as a comment. The same parser-free lexer decides both, so the two sections cannot disagree about where a comment ends.
Twins and Ripple
Twins are files that compress well against the seed's file - near-duplicates, forks, and parallel implementations. They are a co-edit signal rather than a dependency: nothing references anything, but historically these files move together, so a change here usually wants the same change there.
Ripple is the second hop. It names files that call the seed's dependents, so a change that propagates through one of them can reach here, and each row records which dependent bridges the two.
Both are statistical and both are labeled as such. A twin carries its distance so you can see how strong the claim is, and a ripple row carries its bridging name so you can dismiss it in one read.
What Outranks What
Authored code outranks generated code everywhere in the report. A generated file is regenerated from a contract, so it is almost never an agent's edit target - and left unranked it dominates, because codegen repeats a symbol in every stub, descriptor, and client shim it emits.
Generated rows are tagged gen and sorted last rather than deleted. Sometimes
the generated call site is the evidence you wanted, and a tool that silently
hid it would be lying about the radius.
Ranking runs before the caps, which is the part that matters. It means a symbol with six authored call sites and four hundred generated ones reports the six, where a first-come report would fill its entire budget with stubs and never mention the code you have to change.
Codegen is recognized from a generated-by header marker first and a filename convention second. Both are liberal by design: a false demotion only reorders a report, and no signal here can hide a match.
Recipes
Ask what a rename would touch, before you rename anything.
blast WalletService
Narrow to one subtree when you already know the change is local. Scope is optional because a blast radius that stopped at a directory would lie, so narrowing is something you must ask for.
blast Session services/backend clients/web
Take the report as one JSON object when an agent is going to read it rather than a person. The schema is stable, every section is a named key, and nothing is truncated without being counted.
blast Session --json
Cap the report when context is tight. The seed, the stats, and the notes are the spine and are never trimmed; the tail goes first, cheapest evidence first.
blast Session --budget 800
Ask what the tool can do, in a form written for a machine reader with no other documentation.
blast --schema
Provenance
blast provenance TEXT answers a different question with the same discipline:
where did this text come from, and does the tree still contain it. It is the
verb for a snippet you were handed and are about to paste.
Attribution is relate's, verification is blast's. relate attributes each maximal verbatim phrase to one exemplar file on the codex shelf, then blast re-reads that file's current bytes and re-finds the phrase exactly.
A phrase surfaces only if the live file still holds it. That is the whole point of doing this here rather than in relate: an attribution against a shelf built yesterday can name a line that has since been deleted, and blast will not report one.
blast provenance 'const fd = std.posix.openat(std.posix.AT.FDCWD, path'
Raise --min-phrase above its twelve-byte floor to drop trivial quotes, and
-C to widen the context lines around each located phrase. The shelf comes
from relate index --shelf, and provenance says so plainly when it is missing
rather than reporting an empty answer.
Note the two verbs shape their JSON differently, because their answers are different objects. A blast report is one JSON object; provenance emits NDJSON, one row per attributed phrase.
Contracts
Results go to stdout and diagnostics go to stderr, always. A run you piped and a run you watched produce the same bytes on stdout, so a captured report and a read one can never disagree.
Exit codes are ripgrep-shaped. Zero means the verb ran, two means a usage, parse, or missing-shelf error, and a report with no rows is still a zero.
A name that used to be a verb is a diagnostic, never a silent alias. blast context and blast family folded into relate's --matching modifier, and
invoking either prints the invocation that replaced it and exits two - so a
pinned script fails loudly instead of drifting onto semantics that moved.
blast --schema is the machine-readable contract: every verb, every flag, its
type and default, the exit codes, and the notes. Read it rather than parsing
--help, which is written for a person, and note that --version reports this
package's own number rather than the engine's.
Build and Test
Build, test, and typecheck with the Zig toolchain.
zig build # the blast binary → zig-out/bin/blast
zig build test # the unit suite
zig build check # compile-only
Builds are ReleaseFast unless -Dcli-optimize says otherwise. The suite here
is deliberately small: this package is a face over engines that carry their own
much larger suites, so most of blast's behavior is proven underneath it.
The package's import topology is machine-checked by
contract/blast.ward, which allows two hops of reach
and no more. The face lives in
src/surface/face/blast/ and imports everything
else: irregex for engines, corpus, and argv; relate for kinship, the shelf, and
the composition kernels; gist for the CLI chassis.
Where This Came From
blast was extracted from a package path inside a private monorepo, cut at
ce430bbaab. It was briefly named irregex, which is why that name now belongs
to the engine package rather than to a binary.
Development uses sibling checkouts wired by build.zig.zon path dependencies;
releases pin a url and a hash. The license is Apache-2.0, matching every
package underneath it.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file blast_search-1.0.0.tar.gz.
File metadata
- Download URL: blast_search-1.0.0.tar.gz
- Upload date:
- Size: 31.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
254732e48eec4c003d8cb0ac50b5a2ae96ad1b91d4d880215db7cd15000a2520
|
|
| MD5 |
a37cd2fbe640ebd7cd70978319a8e77d
|
|
| BLAKE2b-256 |
cfc4a2cd33924f4411a2c6a3cf25d68e484b217d263088fe0e933ba425d3cd7c
|
Provenance
The following attestation bundles were made for blast_search-1.0.0.tar.gz:
Publisher:
release.yml on The-Billy-Company/blast
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
blast_search-1.0.0.tar.gz -
Subject digest:
254732e48eec4c003d8cb0ac50b5a2ae96ad1b91d4d880215db7cd15000a2520 - Sigstore transparency entry: 2332799864
- Sigstore integration time:
-
Permalink:
The-Billy-Company/blast@640855628cb21a44aad8b7036f335348be91e98f -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/The-Billy-Company
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@640855628cb21a44aad8b7036f335348be91e98f -
Trigger Event:
push
-
Statement type:
File details
Details for the file blast_search-1.0.0-py3-none-any.whl.
File metadata
- Download URL: blast_search-1.0.0-py3-none-any.whl
- Upload date:
- Size: 13.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fe2b9c5145b36d2415ffbceea9cab8606d3862cd11c9d7d0e1f9b715964799d
|
|
| MD5 |
fda48eace0843a3860a61070cc960b55
|
|
| BLAKE2b-256 |
d325b86df9cfc750bb5067217d032f2692b00cdc2b375cce6d27520ea7e06802
|
Provenance
The following attestation bundles were made for blast_search-1.0.0-py3-none-any.whl:
Publisher:
release.yml on The-Billy-Company/blast
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
blast_search-1.0.0-py3-none-any.whl -
Subject digest:
5fe2b9c5145b36d2415ffbceea9cab8606d3862cd11c9d7d0e1f9b715964799d - Sigstore transparency entry: 2332799878
- Sigstore integration time:
-
Permalink:
The-Billy-Company/blast@640855628cb21a44aad8b7036f335348be91e98f -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/The-Billy-Company
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@640855628cb21a44aad8b7036f335348be91e98f -
Trigger Event:
push
-
Statement type: