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

> Discover how codebase-memory-mcp uses git diff impact mapping to classify risk on changed symbols. Learn about CRITICAL, HIGH, MEDIUM, and LOW risk levels through hop distance analysis.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: deep-dive
- Published: 2026-07-20

---

**`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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/store.c) (lines 2787-2802) and declared in [`src/store/store.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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

```bash

# List changed files and their contained symbols

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

```

**Sample output:**

```json
{
  "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

```bash

# 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:**

```json
{
  "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

```python
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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/mcp/mcp.c).
- **Implementation** spans [`src/mcp/mcp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/mcp/mcp.c) (traversal logic) and [`src/store/store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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`](https://github.com/DeusData/codebase-memory-mcp/blob/main/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.