How detect_changes_tool Implements Risk Scoring in code-review-graph
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. 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 (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).
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.
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.
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.
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.
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.
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.
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.
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):
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 changesrisk_score: The overall PR risk (maximum individual score)changed_functions: Detailed list with per-node risk scoresreview_priorities: Ranked list of the ten riskiest functions
Using detect_changes_tool
The public tool interface accepts parameters to customize the analysis:
# 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:
crg detect-changes --repo-root . --include-churn
Summary
compute_risk_scoreincode_review_graph/changes.pyimplements 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_changesaggregates individual scores using the maximum value to determine overall PR risk.detect_changes_toolincode_review_graph/main.pyexposes 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →