# How detect_changes_tool Implements Risk Scoring in code-review-graph

> Learn how detect_changes_tool in code-review-graph calculates risk. Discover how it aggregates flow participation, community crossing, test coverage, and security sensitivity into a 0.0-1.0 score for effective PR risk assessment.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: internals
- Published: 2026-08-07

---

**The `detect_changes_tool` calculates risk by aggregating six weighted factors—including flow participation, community crossing, test coverage, and security sensitivity—into a 0.0–1.0 score for each changed node, then reports the maximum as the overall PR risk.**

The `detect_changes_tool` in the `tirth8205/code-review-graph` repository is an MCP (Model Context Protocol) tool that analyzes recent code changes to generate risk-scored review reports. Understanding how this tool implements **detect_changes_tool risk scoring** helps developers prioritize review efforts on high-impact modifications. The implementation combines graph analysis metrics with static code attributes to produce granular risk assessments for every changed function and class.

## Risk Scoring Architecture

The risk scoring pipeline operates in two phases within [`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py). First, `compute_risk_score` evaluates individual risk for each changed node using six weighted heuristics. Second, `analyze_changes` aggregates these into repository-level metrics. The public `detect_changes_tool` defined in [`code_review_graph/main.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/main.py) (lines 633–667) orchestrates this workflow and returns a structured JSON payload containing the final assessment.

## The Six Risk Factors in compute_risk_score

For every changed function or class, the tool builds a numeric risk score between 0.0 and 1.0 by summing weighted contributions from six distinct factors defined in `compute_risk_score`.

### Flow Participation

Nodes belonging to runtime execution flows receive additional risk weight. Each flow membership contributes 0.05, capped at 0.25. When flow criticality values are available, their sum is used instead (also capped at 0.25).

```python
flow_criticalities = store.get_flow_criticalities_for_node(node.id)
if flow_criticalities:
    score += min(sum(flow_criticalities), 0.25)
else:
    flow_count = store.count_flow_memberships(node.id)
    score += min(flow_count * 0.05, 0.25)

```

This logic appears in lines 31–38 of [`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py).

### Community Crossing

Calls from different graph communities indicate cross-boundary dependencies. Each unique caller from a different community adds 0.05, with a maximum of 0.15.

```python
cross_community = 0
node_cid = store.get_node_community_id(node.id)

# ... iterate through caller community IDs

for cid in cid_map.values():
    if cid is not None and cid != node_cid:
        cross_community += 1
score += min(cross_community * 0.05, 0.15)

```

See lines 39–53 in [`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py).

### Test Coverage

The tool evaluates transitive test coverage. Completely untested nodes receive 0.30, while nodes with five or more tests receive 0.05, with linear interpolation between these bounds.

```python
test_count = len(transitive_tests)
score += 0.30 - (min(test_count / 5.0, 1.0) * 0.25)

```

This calculation occurs in lines 54–58.

### Security Sensitivity

Nodes containing security-related keywords in their name or qualified name receive a flat 0.20 boost. The tool checks against an internal `_SECURITY_KEYWORDS` list.

```python
if any(kw in name_lower or kw in qn_lower for kw in _SECURITY_KEYWORDS):
    score += 0.20

```

Found in lines 60–64.

### Caller Count

High fan-in increases risk due to broader impact potential. The score adds `callers / 20.0`, capped at 0.10.

```python
score += min(caller_count / 20.0, 0.10)

```

Located at lines 65–67.

### Change Frequency (Optional)

When `include_churn` is enabled, recent commit frequency adds temporal risk. The contribution uses `_CHURN_SATURATION` (10 commits) and `_CHURN_WEIGHT` (0.15) constants.

```python
if churn_counts and node.file_path:
    commit_count = churn_counts.get(node.file_path, 0)
    score += min(commit_count / _CHURN_SATURATION, 1.0) * _CHURN_WEIGHT

```

See lines 69–73.

After summing all factors, the final score is clamped to `[0.0, 1.0]` and rounded to four decimal places (line 74):

```python
return round(min(max(score, 0.0), 1.0), 4)

```

## Aggregating Risk with analyze_changes

Once individual scores are computed, `analyze_changes` (lines 82–90) derives the overall PR risk by taking the **maximum** value across all changed nodes. It also identifies the top-10 highest-risk functions as review priorities.

The function returns a dictionary containing:

- `summary`: Human-readable text describing the changes
- `risk_score`: The overall PR risk (maximum individual score)
- `changed_functions`: Detailed list with per-node risk scores
- `review_priorities`: Ranked list of the ten riskiest functions

## Using detect_changes_tool

The public tool interface accepts parameters to customize the analysis:

```python

# Example: invoking the tool with churn analysis enabled

result = await crg.detect_changes_tool(
    repo_root="/path/to/repo",
    include_churn=True,  # Opt-in temporal risk factor

)

print("Overall PR risk:", result["risk_score"])
for fn in result["review_priorities"]:
    print(f"{fn['qualified_name']} – risk {fn['risk_score']}")

```

Alternatively, via CLI:

```bash
crg detect-changes --repo-root . --include-churn

```

## Summary

- **`compute_risk_score`** in [`code_review_graph/changes.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/changes.py) implements six weighted risk factors: flow participation (cap 0.25), community crossing (cap 0.15), test coverage (0.05–0.30), security sensitivity (+0.20), caller count (cap 0.10), and optional change frequency (cap 0.15).
- Individual scores are clamped to 0.0–1.0 and rounded to four decimal places.
- **`analyze_changes`** aggregates individual scores using the **maximum** value to determine overall PR risk.
- **`detect_changes_tool`** in [`code_review_graph/main.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/main.py) exposes this functionality via MCP, returning JSON with risk scores and prioritized review lists.

## Frequently Asked Questions

### What is the valid range for risk scores in detect_changes_tool?

Risk scores are floating-point values between **0.0** (lowest risk) and **1.0** (highest risk). The `compute_risk_score` function explicitly clamps results to this range using `min(max(score, 0.0), 1.0)` and rounds to four decimal places for consistency.

### How does test coverage influence the risk calculation?

Test coverage inversely affects risk: nodes with zero tests receive 0.30, while nodes with five or more tests receive 0.05. The formula `0.30 - (min(test_count / 5.0, 1.0) * 0.25)` creates a linear reduction in risk as test coverage increases, encouraging comprehensive test suites for high-risk code.

### What does the overall PR risk score represent?

The overall PR risk score represents the **maximum** individual risk score among all changed functions and classes. This conservative approach ensures that even a single high-risk change elevates the entire review priority, preventing dangerous modifications from being overlooked due to averaging effects.

### How do I enable change frequency (churn) analysis?

Pass `include_churn=True` when calling `detect_changes_tool`, or use the `--include-churn` flag in the CLI. This opt-in feature analyzes recent commit counts per file (default saturation at 10 commits) and adds up to 0.15 to the risk score for frequently modified files, capturing temporal decay risks.