# How to Merge Dynamic Call Data Into the Knowledge Graph in Code-Graph-RAG

> Learn to merge dynamic call data into your Knowledge Graph with Code-Graph-RAG. Follow our three-step pipeline for efficient runtime event capture and graph integration.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-08-15

---

**Use the three-step pipeline in [`codebase_rag/trace/tracer.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/tracer.py) and [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) to capture runtime call events, normalize them via `Ingestor.process_trace`, and merge them into the graph with `graph.merge_calls(static_edges | dynamic_edges)`.**

Code-Graph-RAG constructs a cross-language knowledge graph that links symbols through **CALLS** edges. While static AST analysis identifies most call relationships, many languages—JavaScript, Python, Rust—hide call targets behind runtime dispatch. These **dynamic calls** require runtime tracing and specialized merging logic. This article explains exactly how Code-Graph-RAG ingests dynamic call data and fuses it with static analysis results.

## The Three-Stage Dynamic Call Pipeline

The codebase implements dynamic call merging through distinct capture, normalization, and merge stages.

### Stage 1: Runtime Tracing with `Tracer`

The [`codebase_rag/trace/tracer.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/tracer.py) module instruments program execution to record function-call events. The `Tracer` class captures caller file paths, callee simple names, and dynamic dispatch details including `this` rebinding, trait objects, and dynamic imports.

```python
from codebase_rag.trace.tracer import Tracer

# Initialize tracer for a Python project

tracer = Tracer(language="python", project_root="my_project")
tracer.start()

# Execute instrumented code...

# tracer captures every call event with full context

events = tracer.stop()  # Raw trace payloads

```

The tracer supports multiple language runtimes and emits structured events that preserve enough context to resolve symbols against the static graph.

### Stage 2: Normalization via `Ingestor.process_trace`

The [`codebase_rag/trace/ingest.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/ingest.py) module transforms raw trace events into the graph's canonical edge representation. The `Ingestor` class deduplicates observations and formats edges as `(caller_file, callee_simple_name)` tuples.

```python
from codebase_rag.trace.ingest import Ingestor

ingestor = Ingestor()
dynamic_edges = ingestor.process_trace(events)

# Returns set of normalized (caller_path, callee_name) edges

```

This normalization ensures that runtime observations use identical identifiers to static analysis results, enabling conflict-free merging.

### Stage 3: Graph Merge in [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py)

The [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) module orchestrates the final merge. The `RealtimeUpdater._process_function_calls` method coordinates static analysis refresh, dynamic edge collection, and atomic graph updates.

```python

# Inside realtime_updater.py

def _process_function_calls(self) -> None:
    # 1️⃣ Refresh static analysis caches

    self.updater.factory.call_processor.reset_resolution_caches()
    self.updater._process_function_calls()
    
    # 2️⃣ Collect normalized dynamic edges

    dynamic_edges = self.updater.ingestor.collect_dynamic_calls()
    
    # 3️⃣ Merge static and dynamic edges with idempotent semantics

    self.graph.merge_calls(static_edges | dynamic_edges)

```

The `graph.merge_calls()` operation uses **MERGE-style semantics** (`INGESTOR._use_merge = True`). Existing `(caller, callee)` pairs are preserved; only missing edges are inserted. This prevents duplicate edges when the same call is observed repeatedly.

## Automated Real-Time Merging

For continuous integration, Code-Graph-RAG provides a file-watching mode that automatically merges dynamic calls as you develop.

```bash
python -m codegraph_rag.realtime_updater \
    --project my_project \
    --language python \
    --watch

```

The watcher triggers on every file change:

- Re-runs static analysis via `call_processor`
- Collects new dynamic trace records
- Executes `merge_calls` to integrate updates

This debounced update cycle (`RealtimeUpdater._process_debounced_change`) balances responsiveness with computational cost.

## Manual Dynamic Call Inspection

Query the graph to verify merged dynamic edges:

```python
from cgr.graph import KnowledgeGraph

graph = KnowledgeGraph()

# Retrieve only dynamically-discovered calls

dynamic_calls = graph.query_calls(dynamic_only=True)

for caller, callee in dynamic_calls:
    print(f"{caller} → {callee}  (dynamic)")

```

The `dynamic_only` filter distinguishes runtime-discovered edges from statically-analyzed ones, useful for coverage analysis and debugging.

## Key Source Files

| File | Purpose |
|------|---------|
| [`codebase_rag/trace/tracer.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/tracer.py) | Captures runtime call events across languages |
| [`codebase_rag/trace/ingest.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/ingest.py) | Normalizes trace events; `Ingestor.process_trace` |
| [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) | Orchestrates merge via `RealtimeUpdater._process_function_calls` |
| [`evals/static_calls.py`](https://github.com/vitali87/code-graph-rag/blob/main/evals/static_calls.py) | Reference oracle for static vs. dynamic coverage comparison |
| [`codebase_rag/tests/test_javascript_dynamic_imports.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tests/test_javascript_dynamic_imports.py) | Validates dynamic import edge recording |
| [`codebase_rag/tests/test_rust_trait_objects.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tests/test_rust_trait_objects.py) | Validates trait object dispatch edge recording |

## Summary

- **Capture** runtime calls with `Tracer` in [`codebase_rag/trace/tracer.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/tracer.py)
- **Normalize** events through `Ingestor.process_trace` in [`codebase_rag/trace/ingest.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/ingest.py)
- **Merge** edges via `graph.merge_calls()` in [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py), using idempotent semantics to prevent duplicates
- **Automate** with the `--watch` CLI mode for continuous integration
- **Verify** dynamic coverage with `graph.query_calls(dynamic_only=True)`

## Frequently Asked Questions

### What file paths are used for dynamic call tracing in Code-Graph-RAG?

The tracing system resides in [`codebase_rag/trace/tracer.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/tracer.py) for capture, [`codebase_rag/trace/ingest.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/ingest.py) for normalization, and [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) for merge orchestration. The `call_processor` symbol in the updater factory coordinates with these modules.

### How does Code-Graph-RAG prevent duplicate edges when merging dynamic calls?

The merge operation uses MERGE-style semantics via `INGESTOR._use_merge = True`. When `graph.merge_calls()` executes, it checks existing `(caller, callee)` pairs and inserts only missing edges. This idempotent design allows repeated trace ingestion without edge explosion.

### Can dynamic calls overwrite or conflict with static analysis results?

No. The merge logic preserves all statically-analyzed edges. Dynamic calls augment the graph rather than replace existing edges. When the same call appears in both static and dynamic sources, the merge operation recognizes the existing edge and skips insertion.

### Which languages support dynamic call tracing in Code-Graph-RAG?

The `Tracer` class supports Python, JavaScript, and Rust runtimes. Test coverage includes JavaScript dynamic imports ([`test_javascript_dynamic_imports.py`](https://github.com/vitali87/code-graph-rag/blob/main/test_javascript_dynamic_imports.py)) and Rust trait object dispatch ([`test_rust_trait_objects.py`](https://github.com/vitali87/code-graph-rag/blob/main/test_rust_trait_objects.py)), validating correct edge recording across these language features.