How Execution Flow Detection Traces Call Chains in code-review-graph
The execution flow detection subsystem discovers entry points using multiple heuristics and then follows outgoing CALLS edges with a breadth-first search to construct complete call chains.
The code-review-graph repository implements a sophisticated static analysis pipeline that maps execution flows through Python codebases. This article examines how the system located in tirth8205/code-review-graph identifies program entry points and traces call chains using graph traversal algorithms.
How Entry Points Are Identified
The detect_entry_points function (lines 64-88 of code_review_graph/flows.py) serves as the foundation of the execution flow detection system. It scans every Function and Test node in the graph, applying three distinct heuristics to identify where code execution begins.
Root Functions Without Incoming Calls
The system first identifies true root functions—those with no incoming CALLS edges. This check relies on store.get_all_call_targets (line 78) to build a comprehensive set of all qualified names that serve as call targets. Any function not present in this set qualifies as an entry point since no other code invokes it.
Framework Decorator Detection
Modern Python frameworks use decorators to mark entry points. The _has_framework_decorator function (lines 29-40) checks nodes against regex patterns stored in _FRAMEWORK_DECORATOR_PATTERNS. When a function carries a decorator matching patterns for Flask, Django, FastAPI, or similar frameworks, the system flags it as an entry point regardless of its call graph position.
Conventional Naming Patterns
The _matches_entry_name function (lines 44-50) implements pattern matching against _ENTRY_NAME_PATTERNS. Functions named main, cli, or following common CLI framework conventions trigger automatic entry point classification.
Test File Filtering
By default, the system excludes test files from entry point detection unless include_tests=True is specified. The _is_test_file helper (lines 54-61) filters out nodes residing in test directories or files matching test naming conventions.
Tracing Call Chains with BFS
Once entry points are established, the system employs breadth-first search to follow execution paths through the codebase.
Flow Adjacency Structure
The FlowAdjacency object provides the calls_out mapping—a dictionary connecting each qualified name to its set of callee qualified names. This adjacency structure is populated by store.load_flow_adjacency and enables efficient forward traversal without repeated database queries.
The BFS Traversal Algorithm
The _trace_single_flow function (lines 22-64) implements the core tracing logic. Starting from a single entry point, it initializes a queue containing the root node's qualified name. The algorithm then enters a loop (lines 41-58) that:
- Dequeues the current node
- Retrieves its outgoing CALLS edges from
calls_out - Enqueues any unvisited callees
- Records traversal order in
path_idsandpath_qnames
The process tracks actual_depth to respect the max_depth parameter, preventing infinite loops in recursive codebases. When the traversal completes with more than one node, the function assembles a flow dictionary (lines 69-79) containing the complete call chain.
Orchestrating Flow Detection and Criticality Scoring
The trace_flows function (lines 84-100) orchestrates the complete pipeline. It first obtains the entry point list, loads the adjacency structure once for efficiency, then invokes _trace_single_flow for each entry point.
After construction, flows undergo criticality analysis via compute_criticality (lines 24-93). This scoring algorithm evaluates five weighted factors:
- File spread: How many distinct files the flow touches
- External calls: Interactions with libraries or system calls
- Security keywords: Presence of sensitive operations defined in
constants.py - Test coverage gap: Execution paths lacking test coverage
- Depth: The length of the call chain
The resulting 0.0-1.0 score determines flow importance, with trace_flows sorting results by criticality (line 115) before returning them.
Code Example: Tracing Execution Flows
The following example demonstrates how to trace execution flows using the public API:
from code_review_graph.graph import GraphStore
from code_review_graph.flows import trace_flows, get_flows
# Initialize a populated graph store after parsing a repository
store = GraphStore(db_path=":memory:")
# Trace all execution flows excluding test entry points
all_flows = trace_flows(store, max_depth=15, include_tests=False)
# Retrieve the top 5 most critical flows
top_five = get_flows(store, sort_by="criticality", limit=5)
for flow in top_five:
print(f"Flow {flow['name']} – criticality {flow['criticality']:.2f}")
For incremental analysis after code changes, use incremental_trace_flows(store, changed_files) to re-trace only flows intersecting modified files rather than rebuilding the entire graph.
Summary
- Entry point detection in
flows.py(lines 64-88) uses three heuristics: missing incoming CALLS edges, framework decorators, and conventional naming patterns. - Breadth-first search in
_trace_single_flow(lines 22-64) follows outgoing CALLS edges through theFlowAdjacencystructure to build complete call chains. - Flow orchestration via
trace_flows(lines 84-100) processes all entry points and loads adjacency data once for performance. - Criticality scoring weighs file spread, external dependencies, security keywords, test coverage, and call depth to rank flows by importance.
- Incremental tracing supports efficient updates when only specific files change.
Frequently Asked Questions
How does the system identify entry points without manual configuration?
The system automatically discovers entry points through static analysis heuristics implemented in detect_entry_points. It identifies functions with no incoming CALLS edges as roots, detects framework decorators like @app.route using regex patterns in _FRAMEWORK_DECORATOR_PATTERNS, and recognizes conventional names such as main or cli via _ENTRY_NAME_PATTERNS.
What is the maximum depth for call chain tracing?
The max_depth parameter in trace_flows controls traversal limits, with the BFS algorithm tracking actual_depth during execution. While configurable per invocation, the default prevents excessive recursion in deep call graphs while ensuring complete coverage of shallow execution paths.
How is flow criticality calculated?
The compute_criticality function (lines 24-93) calculates a 0.0-1.0 score based on weighted factors including cross-file dependencies, external library usage, security-sensitive keywords defined in constants.py, gaps in test coverage, and the maximum BFS depth reached during tracing.
Can the system trace flows incrementally after code changes?
Yes. The incremental_trace_flows function accepts a list of changed files and re-traces only flows intersecting those modifications. This avoids rebuilding the entire call graph when updating the analysis after small commits or pull request changes.
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 →