DEV Community

Cover image for Your coding agent is about to rename PaymentStatus::Failed
iCe Gaming
iCe Gaming

Posted on

Your coding agent is about to rename PaymentStatus::Failed

Structural, not typed and honest about it

impact does not type-check. It is not rust-analyzer, tsc, or gopls wearing a different hat. It resolves a call by name: exact qualified path first, then a shorter form, then a bare identifier.
When two symbols share a short name, it does not pick one and stay quiet. It reports both. A blast-radius tool should over-report, not under-report. A false positive is visible and easy to dismiss. A false negative is invisible and costs you later.
Every DIRECT / INDIRECT entry carries a confidence tier:

  • Exact every hop from the thing you queried resolved unambiguously.
  • Heuristic some hop only matched a bare name that more than one candidate owns. A chain is only as trustworthy as its weakest hop. Tree-text tags the weak ones inline (caller::maybe_this [heuristic]). --min-confidence exact (CLI) or min_confidence: "exact" (MCP) drops them when you only want what is certain. --explain fills in the via chain for indirect hits, so a heuristic entry is something you can verify instead of something you have to trust. ## File, symbol, or the diff you already have Three ways in. Same engine. A file. Everything declared in it.
impact query src/payment/service.rs
Enter fullscreen mode Exit fullscreen mode

A specific change. Not English. A tiny grammar, so the same description always resolves the same way and an unrecognized one is a hard error, not a best-effort guess:

rename <path>
rename <path> to <path>
remove <path>
remove variant <Enum>::<Variant>
remove field <Type>.<field>
change signature of <path>
Enter fullscreen mode Exit fullscreen mode

Same payments crate, asking what a signature change on save_payment hits:

$ impact change "change signature of repo::save_payment"
DIRECT
  handlers::PaymentHandler::create_payment_route  src/handlers.rs:6
  repo::save_payment_persists  src/repo.rs:7
INDIRECT
  e2e_tests::creates_payment_route_end_to_end  src/e2e_tests.rs:4
API
  POST /payments
EVENTS
  PaymentCreated
DATABASE
  payments
TESTS
  2 affected tests
  e2e_tests::creates_payment_route_end_to_end  src/e2e_tests.rs:4
  repo::save_payment_persists  src/repo.rs:7
Enter fullscreen mode Exit fullscreen mode

The unit test that lives next to the function shows up this time. The file query seeded from declarations in repo.rs as a set; the change query seeded from one symbol. Same graph, different start node.
A unified diff. The combined blast radius of every symbol whose span the touched lines fall inside:

git diff | impact diff
Enter fullscreen mode Exit fullscreen mode

One call, not one impact query per dirty file. The project has to be indexed against the working tree which is what git diff on uncommitted changes already describes.
JSON is there when a machine is reading (--json). Humans get the tree. CLI and MCP share one computation layer, so they cannot drift.

The agent does not get a chatbot. It gets a rule.

Registering an MCP server only gives an agent tools. It still needs telling when to call them.
impact install does both, for Cursor, Codex, Claude Code, and Claude Desktop: MCP server plus a hard rule.

impact install                 # all four clients, user (global) scope
impact install --client cursor --scope project
impact doctor                  # what is configured, and whether the rule is current
Enter fullscreen mode Exit fullscreen mode

The rule is not "consider using impact." It is two triggers:
Before editing and before proposing a concrete rename / remove / signature change index if needed, then impact_file or impact_change. Treat a nonzero report as a checklist. Vague "here's roughly how I'd approach it" talk does not need it. A named target does.
After editing re-index, run the same query, confirm the blast radius you addressed still matches and nothing new appeared.
Five-step loop: INDEX (hash-gated), QUERY (callers + contracts), CHECKLIST (update every hit), EDIT (the actual change), RE-CHECK (index then query again).

That is the product. The agent stops guessing what it will break.
MCP tools, if you wire them by hand:
| Tool | What it answers |
|---|---|
| impact_index | Build or refresh the local SQLite cache |
| impact_file | Blast radius of a file |
| impact_change | Blast radius of one grammar-shaped change |
| impact_diff | Blast radius of a unified diff |

claude mcp add impact -- impact mcp
Enter fullscreen mode Exit fullscreen mode

Seven languages. Contracts where the shape is unambiguous.

Rust, TypeScript / TSX (React), JavaScript / JSX (React Native), Python, Go, Kotlin (Android), Swift.
The core is language-agnostic. Each language is a pluggable tree-sitter adapter. Every adapter after Rust added zero lines to impact-core that boundary is load-bearing, not a slide.
DIRECT / INDIRECT work across all seven. Contract detection is narrower, on purpose. impact only emits an API / event / table identity when the source shape is structurally certain:

  • API axum (Rust), net/http Go 1.22+ method-prefixed routes (HandleFunc("POST /payments", ...)), FastAPI / Flask decorators, Express / Fastify named-handler registrations. Same "{VERB} {path}" identity string, so a Go service and a Rust service registering the same route can match across a workspace.
  • Events / database Rust today (marker-trait or naming-suffix events; sqlx-family macros for tables).
  • Tests whatever convention a language actually agrees on: #[test] / #[tokio::test], pytest's test prefix, TestXxx in _test.go, JUnit @Test, XCTest test* methods, and for JS/TS the one convention Jest / Vitest / Mocha share (.test. / .spec. / __tests__/). It will not pretend it() vs test() is settled. Inline Express handlers, method-less Go patterns, "maybe this identifier inside an arrow function is the route" no contract. Empty is honest. A guessed route is not. affected_tests is a list with file and line, not a count. An agent can run those tests instead of the whole suite. Caveat, because I will not sell you a compiler: a test only appears if it is a caller in the graph. A black-box CLI test that never names the function will not. ## Cross-repo, when the identity is real Sibling services do not share a call graph. They share contracts. A workspace.toml registers the other repos. --workspace extends a report with which of them share an API route, event, or table identity tiered so a coincidence does not dress up as a dependency:
  • Declared a [[links]] entry names that exact contract.
  • Strong the two projects are linked in general.
  • Weak same identity, nothing declared. Always shown, always labeled. Two unrelated apps both exposing POST /health is a real situation. Deterministic given the same workspace and the same indexes. Not a fuzzy score. ## Fast enough that the agent can call it every time Release build, ~50k-line Rust + TypeScript workspace (207 files, 2,249 symbols), desktop CPU (Ryzen 7 7800X3D): | Operation | Time | |---|---| | Cold impact index | ~1.6s | | Re-index, nothing changed (content-hash skip) | ~0.1s | | Warm impact query of one file | ~35–40ms | One machine, one project order of magnitude, not a guarantee. The number that matters in the agent loop is the middle one. The protocol says "index before the edit." You pay the cold parse once. After that, unchanged files are skipped. Cache lives in .impact/cache.sqlite next to the project. Schema-versioned: a stale cache from an older binary gets wiped and rebuilt instead of being silently reused. Analytics, if you want them, are local (impact gain) and opt-out (IMPACT_NO_ANALYTICS=1). No network call, ever. ## Install it, then ask a real file what it breaks macOS / Linux (Homebrew):
brew install ancientice/impact/impact
impact install
Enter fullscreen mode Exit fullscreen mode

macOS / Linux (script):

curl -fsSL https://raw.githubusercontent.com/AncientiCe/impact-rs/master/scripts/install.sh | sh
impact install
Enter fullscreen mode Exit fullscreen mode

Windows (PowerShell):

irm https://raw.githubusercontent.com/AncientiCe/impact-rs/master/scripts/install.ps1 | iex
impact install
Enter fullscreen mode Exit fullscreen mode

Impact loop

From source, if you already have Rust:

cargo install --git https://github.com/AncientiCe/impact-rs --locked impact-cli
Enter fullscreen mode Exit fullscreen mode

Not on crates.io yet. Prebuilt binaries ship for linux x86_64 / aarch64, macOS Intel / Apple Silicon, and Windows x86_64.
Then open a file you were going to let the agent touch, and run:

impact index .
impact query path/to/that/file
Enter fullscreen mode Exit fullscreen mode

If the report is empty, you have a leaf. If it is not, you have a checklist and so does the agent.
Repo, releases, and the rule text impact install writes verbatim:
github.com/AncientiCe/impact-rs

Top comments (0)