How Blast Radius Analysis Works in code-review-graph: Knowledge Graph Traversal Explained

code-review-graph determines blast radius by performing a weighted breadth-first search (BFS) through the knowledge graph, starting from modified files and traversing relationships until the impact score falls below a configurable floor or the maximum hop depth is reached.

The blast radius analysis feature in tirth8205/code-review-graph quantifies how far-reaching a code change might be by tracing dependencies through a SQLite-backed knowledge graph. Unlike simple file-based impact detection, this system accounts for semantic relationships—such as function calls, imports, and inheritance—assigning decaying weights as the distance from the change increases.

Core Components of the Blast Radius Engine

The implementation splits responsibilities between a high-level Python API and a optimized SQL-based graph traversal.

The Public Tool Interface (get_impact_radius)

Located in code_review_graph/tools/query.py (lines 105–122), the get_impact_radius function serves as the primary entry point. It prepares the analysis by resolving file paths—either from an explicit list or via git diff—and delegates the computational work to the graph store. After retrieval, it formats the raw results into a human-readable summary containing counts of changed nodes, impacted nodes, affected files, and a computed risk rating.

The SQLite Graph Traversal (GraphStore.get_impact_radius_sql)

The heavy lifting occurs in code_review_graph/graph.py within the get_impact_radius_sql method (starting around line 742). This implementation uses temporary tables and iterative SQL queries to perform the traversal directly inside SQLite, ensuring linear performance even on cyclic graphs. It creates five temporary tables during execution: _impact_seeds, _impact_weights, _impact_best, _impact_frontier, and _impact_next.

Step-by-Step Blast Radius Algorithm

The algorithm proceeds through six distinct phases, combining seed initialization with iterative frontier expansion.

1. Seed Preparation

For every modified file, the system retrieves all contained nodes using self.get_nodes_by_file and inserts their qualified names into the _impact_seeds temporary table (lines 996–1000 in graph.py). These seeds represent the ground truth of what changed, receiving an initial impact score of 1.0.

2. Edge Weight Configuration

The algorithm loads relationship weights from the IMPACT_EDGE_WEIGHTS mapping defined in code_review_graph/constants.py (lines 421–429) into the _impact_weights table (lines 383–390). This allows different edge types—such as CALLS or IMPORTS_FROM—to contribute varying levels of impact to the traversal.

3. Frontier Initialization

Two additional temporary tables are initialized (lines 511–518):

  • _impact_best tracks the highest score achieved for any visited node
  • _impact_frontier contains the nodes to expand during the current iteration

Both tables are seeded with the initial nodes from _impact_seeds at score 1.0.

4. Iterative Expansion and Scoring

For each depth iteration up to max_depth, the algorithm performs the following operations (lines 585–618 and the control loop at 886–904):

  • Joins the current frontier with the edges table in both directions to discover neighbors
  • Calculates candidate scores using the formula: score = parent_score × edge_weight × depth_decay
  • Applies the IMPACT_DEPTH_DECAY factor to reduce influence as distance increases
  • Filters candidates against IMPACT_SCORE_FLOOR to eliminate negligible impacts
  • Updates _impact_best only when a candidate exceeds the current best score for that node, preventing cycles from inflating scores

5. Result Extraction

Once the BFS terminates, the query selects the top max_nodes + 1 rows from _impact_best (lines 909–923). The extra row serves solely to detect truncation. The algorithm joins these results back to the nodes table to retrieve full metadata, filtering out seed nodes and internal "verilog-kind" nodes.

6. Risk Assessment and Formatting

The get_impact_radius wrapper in query.py (lines 124–202) converts the raw GraphNode and GraphEdge objects into dictionaries. When detail_level="minimal", it assigns a risk rating—low, medium, or high—based on the total count of impacted nodes, providing immediate triage guidance for code reviewers.

Practical Usage Examples

Analyzing Changes via CLI

The command-line interface exposes blast radius analysis through the impact sub-command defined in code_review_graph/main.py:


# Analyze the blast radius of uncommitted changes

crg impact

# Analyze a specific commit range

crg impact --base HEAD~3 --head HEAD

Python API Integration

For programmatic access, import the query tool directly:

from code_review_graph.tools.query import get_impact_radius

report = get_impact_radius(
    changed_files=None,      # Auto-detect via git diff

    max_depth=2,
    max_results=500,
    detail_level="standard"
)

print(report["summary"])

# Output: Blast radius for 3 changed file(s):

#   - 5 nodes directly changed

#   - 27 nodes impacted (within 2 hops)

#   - 4 additional files affected

Direct Graph Store Access

Advanced users can bypass the wrapper to interact with the database layer:

from code_review_graph.graph import GraphStore

store = GraphStore("/tmp/crg.db")
changed = ["src/foo/bar.py", "src/baz/qux.ts"]

impact = store.get_impact_radius_sql(
    changed, 
    max_depth=3, 
    max_nodes=200
)

print(f"Total impacted nodes: {len(impact['impacted_nodes'])}")

Configuration and Tuning Constants

The behavior of the blast radius analysis is governed by constants in code_review_graph/constants.py:

  • IMPACT_EDGE_WEIGHTS: Dictionary mapping relationship types (e.g., CALLS, INHERITS_FROM) to numerical weights
  • IMPACT_DEPTH_DECAY: Float value (typically < 1.0) multiplied at each hop to attenuate distant impacts
  • IMPACT_SCORE_FLOOR: Minimum score threshold; nodes scoring below this value are excluded from results

Modifying these constants allows customization for different codebase densities and review strictness requirements.

Summary

  • Blast radius analysis in code-review-graph performs a weighted BFS through a SQLite knowledge graph to measure change impact.
  • The get_impact_radius function in code_review_graph/tools/query.py provides the public API, handling input resolution and output formatting.
  • GraphStore.get_impact_radius_sql in code_review_graph/graph.py implements the core algorithm using temporary tables and iterative SQL to achieve linear time complexity.
  • Impact scores decay multiplicatively based on edge weights and depth decay, ensuring distant relationships contribute less than immediate dependencies.
  • The system supports both CLI usage (crg impact) and programmatic access, with configurable thresholds for depth, score floors, and result limits.

Frequently Asked Questions

How does code-review-graph prevent infinite loops in cyclic graphs?

The algorithm stores the best-known score for each node in the _impact_best temporary table. During each iteration, a candidate node only updates the frontier if its newly calculated score exceeds the existing best score for that node. As scores monotonically decrease due to IMPACT_DEPTH_DECAY, cycles cannot propagate indefinitely, ensuring the query terminates in linear time relative to the number of edges traversed.

What determines the risk rating in the blast radius report?

When detail_level="minimal" is specified, the get_impact_radius function assigns a risk rating based on the cardinality of impacted nodes. Low risk indicates minimal downstream effects, medium risk suggests moderate dependency chains, and high risk signals extensive potential breakage. The specific thresholds are calculated dynamically based on the total nodes discovered within the configured max_depth and max_results constraints.

Can I customize which relationship types affect the blast radius calculation?

Yes. The IMPACT_EDGE_WEIGHTS dictionary in code_review_graph/constants.py (around lines 421–429) defines how much each edge type contributes to the impact score. You can modify these weights before database initialization or extend the mapping to include custom relationship types defined in your knowledge graph schema. Setting a weight to 0.0 effectively excludes that relationship from the traversal.

Why does the algorithm use temporary SQLite tables instead of Python data structures?

Temporary tables leverage SQLite's query optimizer and index structures to handle large graphs efficiently. By performing the frontier expansion, score comparison, and cycle detection within the database engine, the system avoids loading the entire graph into memory. This approach maintains consistent performance even when analyzing repositories with millions of nodes and edges, as the working set remains bounded by the current frontier rather than the full dataset.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →