How to Interpret Three-Tier Edge Confidence Scoring (EXTRACTED/INFERRED/AMBIGUOUS) in code-review-graph
TLDR: The code-review-graph repository uses three confidence tiers—EXTRACTED for direct parser observations, INFERRED for type-based deductions, and AMBIGUOUS for unresolved matches—to rank the reliability of edges in its SQLite-backed knowledge graph.
The code-review-graph repository constructs a knowledge graph of code relationships stored in SQLite, where each edge represents a dependency or call between entities. Understanding the three-tier edge confidence scoring system is essential for accurately interpreting query results and visualizations, as it distinguishes between directly observed syntax and algorithmically deduced connections.
The Three-Tier Confidence System
Every edge in the graph carries a confidence tier (stored in the confidence_tier column) that indicates how the relationship was derived. This classification operates alongside a numeric confidence score (float in the confidence column) to provide both categorical and probabilistic measures of edge reliability.
EXTRACTED (High Confidence)
EXTRACTED edges originate from concrete syntactic elements that the parser can unambiguously locate. These represent the highest tier of confidence because they map directly to source code constructs. Typical examples include explicit function calls, import statements, class inheritance declarations, and test-to-code links.
According to the source code in code_review_graph/graph.py, edges default to this tier upon insertion. Lines 90-91 set the confidence_tier column to 'EXTRACTED' when a new edge is first created, reflecting that the relationship was directly observed rather than deduced.
INFERRED (Medium Confidence)
INFERRED edges are added by resolver modules that deduce relationships not explicitly present in the source but determinable through type information, method overrides, or runtime-like analysis. For example, when a method call resolves to a specific implementation via an interface or when dispatch is discovered after analyzing class hierarchies, the edge is marked as inferred.
The scoped_resolver.py module handles these rewrites. At lines 33-34, the resolver updates existing edges with confidence_tier = 'INFERRED' after performing type resolution. Similarly, event_resolver.py tags event-driven inferred relationships with this tier. These edges typically carry a reduced confidence float (e.g., 0.85) reflecting the heuristic nature of the deduction.
AMBIGUOUS (Low Confidence)
AMBIGUOUS edges point to targets that could not be uniquely identified, usually because multiple symbols match the name (overloads), language-specific dynamic features obscure the target, or type information is missing. These edges are preserved in the graph to maintain completeness but are considered the least trustworthy.
When a resolver cannot select a single qualified name from multiple candidates, it assigns the AMBIGUOUS tier. These edges carry lower confidence scores (often around 0.4) and signal to downstream consumers that human verification may be required.
Implementation Details
The tier assignment logic spans three critical files:
code_review_graph/graph.pydefines the SQLite schema and establishes the default. Whenadd_edge()is called, the database schema automatically setsconfidence_tierto'EXTRACTED'unless explicitly overridden.code_review_graph/scoped_resolver.pyperforms type-based resolution. When resolving scoped calls (particularly for Rust and PHP), it rewrites edges usingrewrite_edge()with the tier set to'INFERRED'.code_review_graph/event_resolver.pyhandles event-driven inference pathways, also tagging edges asINFERREDwhen relationships are discovered through indirect analysis.
Unit tests in tests/test_rust_scoped_calls.py and tests/test_php_scoped_calls.py assert correct tier transitions, ensuring that extracted edges remain stable while inferred and ambiguous edges are properly classified during resolution passes.
Working with Confidence Tiers in Python
When constructing or querying the graph programmatically, you interact with tiers through the store API:
from code_review_graph import GraphStore
store = GraphStore("knowledge.db")
# Adding a direct observation - defaults to EXTRACTED tier
edge_id = store.add_edge(
kind="CALLS",
source_qualified="my_pkg.moduleA.func_a",
target_qualified="my_pkg.moduleB.func_b",
file_path="moduleA.py",
line=42,
confidence=1.0
)
# Resolver rewriting after type inference
store.rewrite_edge(
edge_id=edge_id,
new_target="my_pkg.moduleB.impl_func_b",
confidence=0.85,
confidence_tier="INFERRED"
)
# Handling ambiguous resolution
store.add_edge(
kind="CALLS",
source_qualified="my_pkg.moduleC.call_dynamic",
target_qualified="my_pkg.moduleD.unknown",
confidence=0.4,
confidence_tier="AMBIGUOUS"
)
Query filters can leverage these tiers to adjust result precision:
# High-precision query (EXTRACTED only)
precise_edges = store.query_edges(
source="my_pkg.moduleA",
confidence_tier="EXTRACTED"
)
# Include inferred relationships with threshold
broad_edges = store.query_edges(
source="my_pkg.moduleA",
min_confidence=0.5
)
Why Confidence Tiers Matter
The three-tier system enables sophisticated graph analysis strategies:
- Impact-radius queries use tier information to weight edges during traversal. High-confidence
EXTRACTEDedges contribute fully to impact calculations, whileINFERREDedges may be down-weighted andAMBIGUOUSedges filtered out entirely, allowing developers to trade completeness for precision. - Visualization components (such as those in
visualization.py) color-code edges by tier, enabling code reviewers to immediately distinguish between directly observed calls and speculative connections. - Incremental updates maintain tier stability as the codebase evolves. When new code is parsed, previously
INFERREDedges may be promoted toEXTRACTEDif the parser now locates a concrete syntactic element, progressively improving graph accuracy without requiring full rebuilds.
Summary
- EXTRACTED edges represent direct parser observations and default to confidence 1.0, defined in
code_review_graph/graph.py. - INFERRED edges are type-resolved deductions created by
scoped_resolver.pyandevent_resolver.py, typically with reduced confidence scores. - AMBIGUOUS edges indicate unresolved multiple matches, retained for completeness but marked with low confidence.
- The
confidence_tiercolumn (TEXT) works alongside theconfidencefloat column to enable both categorical filtering and probabilistic weighting. - Query and visualization tools use these tiers to balance precision against recall when analyzing code dependencies.
Frequently Asked Questions
How does the confidence float relate to the confidence tier?
The confidence float (column confidence) stores a probabilistic value between 0.0 and 1.0 indicating the likelihood that an edge is correct, while the confidence tier (column confidence_tier) provides a categorical classification of how the edge was derived. Typically, EXTRACTED edges carry 1.0 confidence, INFERRED edges range from 0.7-0.9, and AMBIGUOUS edges fall below 0.5. Queries can filter by tier for semantic meaning or by float threshold for probabilistic tolerance.
Can an edge change tiers after initial creation?
Yes. Edges may be promoted or rewritten by resolver modules. For instance, when scoped_resolver.py performs type analysis, it may rewrite an EXTRACTED edge to INFERRED if it discovers the original target was an interface requiring dispatch resolution. Conversely, during incremental updates, an INFERRED edge may be replaced by an EXTRACTED edge if the parser subsequently locates a concrete call site for that relationship.
Why keep AMBIGUOUS edges in the graph instead of discarding them?
AMBIGUOUS edges are retained to prevent false negatives in dependency analysis. While they indicate uncertainty about the specific target, they signal that a relationship likely exists between the source and one of several candidate targets. This allows visualization tools to flag areas requiring manual review and ensures that impact analysis doesn't miss potential dependencies simply because type resolution was inconclusive.
Which resolver modules assign the INFERRED tier?
The scoped_resolver.py module assigns INFERRED when resolving method calls through class hierarchies and interfaces, particularly for Rust and PHP scoped calls. The event_resolver.py module also tags edges as INFERRED when deducing relationships through event-driven or callback-based analysis. Both modules use the rewrite_edge() method to update the tier while preserving the edge's provenance.
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 →