Git Diff Impact Mapping: How Changed Symbols Are Classified by Risk Level in codebase-memory-mcp

git diff identifies modified files in codebase-memory-mcp, but risk classification occurs through a separate BFS traversal that maps hop distance from changed symbols to risk levels (CRITICAL, HIGH, MEDIUM, or LOW).

The codebase-memory-mcp server transforms version control changes into actionable security intelligence using a two-stage architecture. First, the detect_changes tool parses git diff output to locate modified files and extract impacted symbols. Then, the trace_path tool performs a breadth-first search of the call graph to classify each symbol's risk based on its proximity to the modified code.

The Two-Step Risk Classification Workflow

The system separates change detection from risk analysis to optimize performance and maintain clean architectural boundaries.

Step 1: Detecting Changes with detect_changes

The detect_changes MCP tool executes a sandboxed git diff command via handle_detect_changes in src/mcp/mcp.c (lines 4241-4253). The implementation uses git -C … diff with output redirected to /dev/null (POSIX) or 2>NUL (Windows) to prevent shell injection while capturing changed file paths.

After retrieving the file list, the function calls detect_add_impacted_symbols (lines 4172-4186) to iterate over the codebase store's nodes for each changed file. This collects every non-project/folder/file node—functions, classes, and methods—into an impacted_symbols array. The resulting JSON contains:

  • changed_files: Array of paths from git diff
  • changed_count: Total number of modified files
  • impacted_symbols: Objects containing name, label, and file for each symbol
  • depth: The requested BFS depth (default 5)

Step 2: Traversing the Call Graph with trace_path

Risk calculation happens only when invoking the trace_path tool (also aliased as trace_call_path) with the parameter risk_labels:true. The traversal starts from each impacted symbol identified in step one and walks the call graph outward.

During BFS traversal, bfs_to_json_array in src/mcp/mcp.c (lines 4229-4232) records the hop count—representing graph distance from the starting symbol—and enriches the output with a risk string by calling cbm_risk_label(cbm_hop_to_risk(hop)).

How Hop Distance Maps to Risk Levels

The risk classification logic resides in the store layer, specifically in src/store/store.c (lines 2787-2802) and declared in src/store/store.h (lines 408-414). The mapping follows a discrete severity scale based on BFS depth:

Hop Distance Risk Level
1 CRITICAL
2 HIGH
3 MEDIUM
≥ 4 LOW

When cbm_hop_to_risk receives a hop count, it returns an integer risk code that cbm_risk_label converts to the string representations above. This means symbols directly called by changed code (hop 1) receive CRITICAL status, while dependencies four or more layers deep are classified as LOW risk.

Security Controls for Git Operations

The git diff execution includes several hardening measures. Before constructing the command, cbm_validate_shell_arg (lines 4217-4222 in src/mcp/mcp.c) validates the base_branch parameter to reject shell metacharacters and prevent command injection. The diff command itself runs within a constrained context using git -C {repo_path} diff {base_branch}...HEAD syntax, ensuring the operation stays scoped to the intended repository.

Practical Implementation Examples

Detecting Changed Symbols


# List changed files and their contained symbols

codebase-memory-mcp cli detect_changes '{"project":"myproj"}'

Sample output:

{
  "changed_files": ["src/foo.c","src/bar.c"],
  "changed_count": 2,
  "impacted_symbols": [
    {"name":"process_data","label":"Function","file":"src/foo.c"},
    {"name":"DataManager","label":"Class","file":"src/bar.c"}
  ],
  "depth": 5
}

Retrieving Risk-Classified Impact


# Trace call paths with risk labels enabled

codebase-memory-mcp cli trace_path '{
  "project":"myproj",
  "function_name":"process_data",
  "direction":"both",
  "depth":5,
  "risk_labels":true
}'

Sample output:

{
  "results": [
    {"name":"process_data","qualified_name":"myproj.src.foo.process_data","hop":0,"risk":"CRITICAL"},
    {"name":"helper","qualified_name":"myproj.src.utils.helper","hop":1,"risk":"CRITICAL"},
    {"name":"log_error","qualified_name":"myproj.src.log.log_error","hop":2,"risk":"HIGH"},
    {"name":"send_metric","qualified_name":"myproj.src.metrics.send_metric","hop":4,"risk":"LOW"}
  ]
}

Automating the Full Workflow

import json
import subprocess
import shlex

def analyze_change_impact(project):
    # Step 1: Detect changes via git diff

    detect_cmd = [
        "codebase-memory-mcp", "cli", "detect_changes",
        f'{{"project":"{project}"}}'
    ]
    detect_out = subprocess.check_output(detect_cmd)
    detect_data = json.loads(detect_out)
    
    symbols = [s["name"] for s in detect_data["impacted_symbols"]]
    
    # Step 2: Classify risk for each impacted symbol

    for sym in symbols:
        trace_cmd = [
            "codebase-memory-mcp", "cli", "trace_path",
            f'{{"project":"{project}","function_name":"{sym}","risk_labels":true}}'
        ]
        result = subprocess.check_output(trace_cmd)
        print(f"Risk analysis for {sym}:")
        print(json.loads(result)["results"])

Summary

  • detect_changes executes git diff and extracts symbols from modified files, but does not assign risk scores.
  • trace_path with risk_labels:true performs BFS traversal from changed symbols and classifies risk based on hop distance.
  • Risk mapping is hardcoded: hop 1 = CRITICAL, hop 2 = HIGH, hop 3 = MEDIUM, hop ≥4 = LOW.
  • Security is enforced via cbm_validate_shell_arg and sandboxed git execution in src/mcp/mcp.c.
  • Implementation spans src/mcp/mcp.c (traversal logic) and src/store/store.c (risk label definitions).

Frequently Asked Questions

What is the relationship between git diff and risk classification?

git diff only provides the list of changed files. The codebase-memory-mcp server uses this list to seed the impacted_symbols array, but actual risk classification requires a subsequent trace_path call that traverses the call graph. According to the source in src/mcp/mcp.c, these steps are decoupled because graph traversal is computationally expensive and only needed when risk analysis is explicitly requested.

How does the hop-to-risk mapping work?

The mapping is implemented in src/store/store.c within cbm_hop_to_risk. This function takes an integer hop count from the BFS traversal and returns a risk level that cbm_risk_label converts to human-readable strings. Hop 1 (direct callers or callees of changed code) maps to CRITICAL, while distances of 4 or greater map to LOW.

Can I customize the risk level thresholds?

The current implementation in src/store/store.c (lines 2787-2802) uses hardcoded thresholds. The cbm_hop_to_risk function implements a switch statement with fixed values for hops 1 through 3 and a default case for hop ≥4. To modify thresholds, you would need to edit the source and recompile the codebase-memory-mcp binary.

Why is risk classification separated from change detection?

The separation follows a separation-of-concerns design pattern. detect_changes performs a fast git diff operation and literal symbol extraction, while trace_path performs expensive graph traversal. This allows agents to quickly identify changed files without incurring the cost of full call-graph analysis unless risk-aware impact mapping is specifically required.

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 →