DEV Community

Daniel Westgaard
Daniel Westgaard

Posted on • Originally published at riftmap.dev

Can AI check the blast radius of a PR before you merge?

Three products shipped the same promise this quarter: see the blast radius of your change before you merge. They mean three different dependency graphs — and each is blind to a kind of breakage the others catch. Picking a tool is really picking which graph gets consulted.


In the past few weeks I have watched three different products promise the same sentence. Overmind's homepage now leads with engineers seeing the blast radius of their changes, before they merge, delivered by simulating the live cloud environment. A GitLab hackathon project shipped an MIT-licensed Blast Radius Reviewer agent to the Duo catalog that runs pre-merge cross-project impact analysis on the Orbit knowledge graph. And Port published an official guide to calculating blast radius with AI before production deploys.

So, can AI check the blast radius of a PR before you merge? Yes, if there is a dependency graph for it to query. The interesting question is which graph, because at least three different graphs are being sold under that sentence right now, and they see different changes break.

I build one of these tools, so I have an obvious interest here. I am going to try to earn your trust the boring way, by being precise about what each graph genuinely sees, where each one structurally stops, and then walking one real pre-merge check end to end on a real public organisation, with the actual API responses.

One phrase, three graphs

"Blast radius before merge" currently describes at least three products that answer three different questions. The symbol layer asks what code calls the code you changed. The live-state layer asks what running cloud resources your plan touches. The artifact layer asks which repositories build on the thing you changed. Each layer is genuinely good at something the other two structurally cannot do, and none of them is lying to you. They are just answering different questions with the same vocabulary.

I have made a version of this argument before, scoped to Terraform: Terraform blast radius is three questions, the in-config graph, the live-cloud graph, and the cross-repo graph. A pull request is a more general object than a Terraform plan. A PR can be application code, which pulls the symbol layer into the picture, and the old in-config visualisers were never pre-merge gates in any serious sense. There is also a fourth kind of graph on the market, the modeled catalog, which I will come to at the end; it is less a layer than a maintenance regime. So for the question in this post's title, the map has three territories. Here is each one, honestly.

The symbol layer: what code calls this code

The symbol layer answers "who calls, imports, or references the code symbols this diff touches", and for application code it is the right layer to ask. The most interesting recent example is that Blast Radius Reviewer agent built on GitLab's Orbit knowledge graph. It extracts the public symbols from the diff, walks direct and transitive references outward up to three hops, weights risk by hop distance, pulls in reachable security findings and ownership, and lands a recommendation on the merge request. When Orbit is unavailable it degrades loudly to single-repo search and labels the result as partial rather than pretending it saw everything. It is well built, it is free, and it is exactly what I would want reviewing a Go or TypeScript change in a GitLab-native org. I have written before about how seriously I take Orbit as a platform, and this agent is a good example of why.

Now look at the assumption stated in its own writeup: changes that touch no public symbols are pruned early, because internal-only changes cannot have a cross-project blast radius. For application code, that is a reasonable heuristic. For infrastructure, it is exactly backwards. A Dockerfile FROM line, a Terraform source block, a Helm Chart.yaml dependency, a GitLab CI include directive: none of these is a public symbol. A diff that bumps them contains nothing for a symbol graph to traverse, so the highest-blast-radius PRs in an infrastructure estate are the ones this layer scores as impactless, by design rather than by defect.

This is not a niche gap. When I counted every cross-repo edge in two real organisations, not one was a code symbol. Symbol graphs and artifact graphs are different categories, and the difference is the whole point of this post.

The live-state layer: what running resources the plan touches

The live-state layer answers "what, in the cloud that is actually running, does this Terraform plan affect", and nothing else on this list can answer that. Overmind is the serious product here. It takes the plan JSON from your PR, queries your AWS, GCP, or Kubernetes environment in real time through read-only access, and maps the affected resources plus everything that depends on them, including resources created outside Terraform entirely, through the console or CloudFormation or a script someone ran in 2021. Those click-ops resources appear in no manifest anywhere. A parser can never find them, because there is nothing to parse. If your worry is "this plan will take down something nobody wrote down", Overmind's layer is the only one that can see it, and I have a lot of respect for it.

Its structural boundary is the same thing that makes it powerful: it reasons from a plan against live state. The repositories that consume your module have not planned anything yet. When you change a shared module, the breakage does not happen in your plan. It happens later, in the plans of a hundred downstream repos, one at a time, as each of them eventually bumps. At the moment your PR is open there is no live signal in the place the damage will actually land. Overmind's graph is plan-scoped and runtime-scoped. Riftmap's is artifact-scoped and source-scoped. A serious platform team at scale plausibly wants both, and I mean that as a description of the architecture rather than as diplomacy. The full comparison is its own post.

The artifact layer: which repos build on what you changed

The artifact layer answers "which repositories declare a build-time dependency on the thing this PR changes", and in today's crop of pre-merge tools it is the question nobody else is answering. Nothing in the current wave walks Terraform source blocks, Dockerfile FROM lines, Helm chart dependencies, or CI include directives across the repositories that were never checked out. So rather than argue it abstractly, here is one real pre-merge check, end to end.

The organisation is Cloud Posse, which maintains one of the largest public Terraform module estates there is. Riftmap scanned the org on 2026-07-02 with one read-only token: 242 repositories in about thirteen and a half minutes, zero errors, no per-repo configuration. The PR we will pretend to open is against terraform-null-label, their naming and tagging convention module. Say it is a release-prep PR for a new tag that renames an output.

Start with the two layers above, because the result is instructive. The diff touches HCL output and locals blocks, so there are no public symbols to extract; a symbol-layer reviewer prunes this change as having no cross-project impact. And the module declares no resource, data, or module blocks at all, only locals, variable, and output blocks. It provisions nothing, so terraform plan shows nothing to add, change, or destroy, and there is no live infrastructure for a plan-simulation layer to map. Both layers report, correctly by their own definitions, that this PR touches nothing.

Now ask the artifact graph:

curl -s "https://api.riftmap.dev/api/v1/repositories/{repo_id}/impact?max_depth=10" \
  -H "Authorization: Bearer $RIFTMAP_TOKEN"
Enter fullscreen mode Exit fullscreen mode
{
  "source_repository": {
    "name": "terraform-null-label",
    "full_path": "cloudposse/terraform-null-label"
  },
  "affected_repositories": [
    { "full_path": "cloudposse/terraform-aws-acm-request-certificate", "depth": 1, "confidence": 1.0 },
    { "full_path": "cloudposse/terraform-aws-alb",                     "depth": 1, "confidence": 1.0 },
    { "full_path": "cloudposse/terraform-aws-alb-ingress",             "depth": 1, "confidence": 1.0 }
  ],
  "total_affected": 147,
  "max_depth_reached": 1
}
Enter fullscreen mode Exit fullscreen mode

That response is real and only trimmed. 147 repositories, 61% of the org's 242 repos, declare terraform-null-label as a direct Terraform-module dependency (deduplicated, confidence ≥ 0.8, and intra-org only, a caveat I will come back to). The module that both other layers scored as impactless is the single highest-blast-radius artifact in the estate. Every one of those 147 edges sits at depth 1. This is not a deep chain amplifying a small number; it is 147 repositories importing one module directly, a module that contains no infrastructure at all, only the convention everything else is named by.

The consumers view is where the pre-merge decision actually gets made, because it carries versions:

{
  "artifact": { "artifact_type": "terraform_module", "name": "cloudposse/terraform-null-label", "consumer_count": 147 },
  "latest_version": "0.25.0",
  "consumers_on_latest": 138,
  "consumers_lagging": 9,
  "state_breakdown": { "pinned": 139, "floating": 0, "branch": 8, "absent": 0 },
  "consumers": [
    { "repository": { "full_path": "cloudposse/terraform-aws-elastic-beanstalk-application" },
      "version_constraint": "0.25.0",     "version_constraint_state": "pinned", "source_file": "context.tf", "source_line": 24 },
    { "repository": { "full_path": "cloudposse/terraform-aws-cloudwatch-flow-logs" },
      "version_constraint": "tags/0.3.1", "version_constraint_state": "branch", "source_file": "kinesis.tf",  "source_line": 2 },
    { "repository": { "full_path": "cloudposse/terraform-aws-multi-az-subnets" },
      "version_constraint": "0.24.1",     "version_constraint_state": "pinned", "source_file": "public.tf",   "source_line": 10 }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Notice the source_file and source_line fields. Each edge points at the exact line of the exact manifest where the dependency is declared. Nothing here is guessed. The resolver collapses three declaration forms onto the same module: Terraform-registry version pins like 0.25.0, git-tag refs like tags/0.3.1, and one raw git URL with no version at all (Cloud Posse's geodesic image, which references the module that way and sits just outside these 147 as a lower-confidence edge).

Two honest wrinkles, because precision is the product here. First, zero of the 147 consumers float a version range. Every single one hard-pins, so publishing a new tag breaks nobody at the moment of release. What the number tells you is something more useful: who has to coordinate the upgrade, and the version distribution tells you how well that coordination has gone historically. 138 consumers sit on the latest release and nine are scattered across six older tags, some dating back to the tags/0.3.x era. Not one repo floats, yet the org is still fragmented across seven distinct versions. That drift is what version constraints look like in real estates generally, and it is the evidence that "everyone pins" and "everyone is current" are very different sentences.

Second, when a consumer does bump, null-label's outputs feed resource names and tags, so the consumer's own plan can light up with renames, and at that moment, one consumer at a time, the live-state layer sees it. The artifact layer answers the question that comes before any of those plans exist: who has to open that PR at all.

There is a fashionable objection to this layer, which is that parsed manifests capture declared dependencies rather than real ones. At the artifact layer the objection dissolves, because the declaration is the mechanism. The build executes the FROM line. terraform init resolves the source block. The pipeline includes the include. There is no separate runtime truth that the manifest is a stale snapshot of; the manifest is the instruction the machines follow. Undeclared runtime calls between services are a real blind spot, and they belong to a different layer of dependency entirely, one that runtime observation is the right tool for. Which repos build on your module is not a runtime question. It is written down, deterministically, at a file and line number, in repos you have never cloned. It just is not written down in the repo the PR is in, which is why neither the agent that opened the PR nor the reviewer reading it can see it, and why inferring it with embeddings or an LLM's confidence score is the wrong tool for a merge gate.

The caveat I promised: all of these counts are intra-org. Cloud Posse's modules are used enormously across the public Terraform ecosystem, and none of that external usage appears in this scan. Within their own 242 repositories, the numbers above are exact. In the world, the true blast radius is far larger and nobody's graph sees all of it.

Where the catalogs sit

Port's blast-radius guide is real and worth taking seriously, and it works on the fourth kind of graph I flagged earlier: a modeled one. You describe your services and their relations as catalog entities, and an AI agent reasons over those relations to produce a risk score and a deployment analysis. The output is a judgement, and the graph it judges from is as accurate as the last person who updated the catalog. I have written about modeled graphs versus parsed graphs and about why hand-maintained catalogs drift toward fiction, so I will keep it to one sentence here: a modeled graph answers with the accuracy of its YAML, and an enumeration beats a judgement anywhere an enumeration is available.

The blast radius the AI reports is the blast radius of the graph

The answer to this post's title was never really in doubt. An agent can query any graph you hand it, and every product I have named will genuinely put something called a blast radius on your PR. What you are choosing when you pick one is not whether AI checks your change. It is which graph gets consulted, and therefore which category of breakage stays invisible. Symbol graphs cannot see the FROM line. Live-state graphs cannot see the repos that have not planned yet. Artifact graphs cannot see the click-ops instance someone made in the console. The teams that get this right will not be the ones that bought the best AI. They will be the ones that knew which question their estate actually needed answered, and made sure a graph existed that could answer it before the merge button did.

If your estate's risk lives where Cloud Posse's does, in the modules, images, charts, and templates that a hundred repos quietly build on, that graph is the artifact graph, and checking it from CI or an agent is one HTTP call.

Common questions

Can AI check the blast radius of a PR before you merge?

Yes, if there is a dependency graph for it to query at PR time. The AI resolves what the PR changes, requests the set of affected consumers from the graph, and reports the result before merge. Without a graph, an AI reviewer only sees the repository the PR is in, so cross-repo blast radius stays invisible until something breaks downstream. The practical question is which graph it queries: symbol graphs see code references, live-state graphs see running cloud resources, and artifact graphs see which repositories build on the changed artifact.

What tools show blast radius on a pull request before merge?

Three kinds, by layer. Symbol-layer tools like Sourcegraph and GitLab Orbit (including the open-source Blast Radius Reviewer agent) trace which code references the symbols in a diff. Live-state tools like Overmind simulate a Terraform plan against real-time AWS, GCP, or Kubernetes state and comment the affected resources on the PR. Artifact-layer tools like Riftmap enumerate the repositories whose manifests (Terraform source blocks, Dockerfile FROM lines, Helm dependencies, CI includes) consume what the PR changes. They answer different questions, and infrastructure-heavy estates usually need the third.

Does Claude Code or Cursor know the blast radius of a change?

Not on their own. Coding agents see the repository they have cloned or indexed, so a change to a shared module, base image, or CI template looks safe because the consumers live in repos the agent never opened. Given a queryable dependency graph, though, an agent can check blast radius at planning time with one API call and get the affected repositories back before it opens the PR. The agent is not the limitation; the missing graph is. More on that in Claude Code, Cursor, and the graph neither sees.

Is blast radius analysis deterministic or AI-generated?

It depends on the layer, and honest tools are clear about which parts are which. Graph traversal is deterministic: "147 repositories declare this module at these file and line numbers" is an enumeration, and it is either right or wrong. Risk narratives, severity scores, and deployment recommendations are judgements, usually LLM-generated, layered on top of whichever graph the tool has. A reasonable rule for merge gates: gate on the enumeration, treat the narrative as advice.

Top comments (0)