fix: snap cross-repo resolver confidence scores to the INFERRED rubric (#4045)

Snap the off-rubric INFERRED confidence scores emitted by the cross-repo
call/type resolvers and interface dispatch (0.8 and 0.9) to the nearest
canonical rubric tier (0.85), so every INFERRED edge carries one of the
documented discrete scores. Label-only change: no edge flips
EXTRACTED<->INFERRED and no edge is produced or dropped. Replaces the
substring-matching guard test with an AST-based one that scans every
module for off-rubric scores.

Squashed from #4045 (authorship preserved).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Deepanshu PalandClaude Opus 4.8 authored and safishamsi committed 2026-10-04 20:23:44 +01:00
1 parent 7e7de7d08f
commit b8fcd29cb1
10 files changed
+91 -25

No files matched your search

+1 -1
View File
@@ -235,7 +235,7 @@ def link_cross_repo_member_calls(merged: "nx.Graph") -> int:
relation="calls",
context="cross_repo",
confidence="INFERRED",
confidence_score=0.8,
confidence_score=0.85,
source_file=str(caller_data.get("source_file") or ""),
source_location=entry.get("line"),
weight=1.0,
+1 -1
View File
@@ -65,7 +65,7 @@ def link_shared_type_declarations(merged: "nx.Graph") -> int:
relation=SHARED_TYPE_RELATION,
context="cross_repo",
confidence="INFERRED",
confidence_score=0.9,
confidence_score=0.85,
source_file=str(merged.nodes[left].get("source_file") or ""),
weight=1.0,
_src=left,
+6 -6
View File
@@ -3945,7 +3945,7 @@ def _resolve_swift_member_calls(
"relation": relation,
"context": "call",
"confidence": "EXTRACTED" if type_qualified else "INFERRED",
"confidence_score": 1.0 if type_qualified else 0.8,
"confidence_score": 1.0 if type_qualified else 0.85,
"source_file": rc.get("source_file", ""),
"source_location": rc.get("source_location"),
"weight": 1.0,
@@ -4237,7 +4237,7 @@ def _resolve_typescript_member_calls(
"relation": "calls",
"context": "call",
"confidence": "EXTRACTED" if type_qualified else "INFERRED",
"confidence_score": 1.0 if type_qualified else 0.8,
"confidence_score": 1.0 if type_qualified else 0.85,
"source_file": rc.get("source_file", ""),
"source_location": rc.get("source_location"),
"weight": 1.0,
@@ -4403,7 +4403,7 @@ def _resolve_cpp_member_calls(
"relation": relation,
"context": "call",
"confidence": "EXTRACTED" if type_qualified else "INFERRED",
"confidence_score": 1.0 if type_qualified else 0.8,
"confidence_score": 1.0 if type_qualified else 0.85,
"source_file": src_file,
"source_location": rc.get("source_location"),
"weight": 1.0,
@@ -4625,7 +4625,7 @@ def _resolve_csharp_member_calls(
"relation": "calls",
"context": "call",
"confidence": "EXTRACTED" if type_qualified else "INFERRED",
"confidence_score": 1.0 if type_qualified else 0.8,
"confidence_score": 1.0 if type_qualified else 0.85,
"source_file": src_file,
"source_location": rc.get("source_location"),
"weight": 1.0,
@@ -4835,7 +4835,7 @@ def _resolve_java_member_calls(
"relation": "calls",
"context": "call",
"confidence": "EXTRACTED" if exact else "INFERRED",
"confidence_score": 1.0 if exact else 0.8,
"confidence_score": 1.0 if exact else 0.85,
"source_file": raw_call.get("source_file", ""),
"source_location": raw_call.get("source_location"),
"weight": 1.0,
@@ -5026,7 +5026,7 @@ def _resolve_objc_member_calls(
"relation": relation,
"context": "call",
"confidence": "EXTRACTED" if type_qualified else "INFERRED",
"confidence_score": 1.0 if type_qualified else 0.8,
"confidence_score": 1.0 if type_qualified else 0.85,
"source_file": src_file,
"source_location": rc.get("source_location"),
"weight": 1.0,
+1 -1
View File
@@ -150,7 +150,7 @@ def resolve_interface_dispatch(
"relation": DISPATCH_RELATION,
"context": "call",
"confidence": "INFERRED",
"confidence_score": 0.9,
"confidence_score": 0.85,
"source_file": str(impl_node.get("source_file", "")),
"source_location": impl_node.get("source_location"),
"weight": 1.0,
+1
View File
@@ -98,6 +98,7 @@ def test_a_parked_call_binds_to_the_one_declaration_in_another_repo():
data = G.edges["a::app_run", "b::greeter_greet"]
assert data["relation"] == "calls"
assert data["confidence"] == "INFERRED"
assert data["confidence_score"] == 0.85
assert data["context"] == "cross_repo"
assert data["source_location"] == "L10"
+1
View File
@@ -67,6 +67,7 @@ def test_same_namespace_and_name_across_repos_are_linked(tmp_path):
endpoints = {links[0]["source"], links[0]["target"]}
assert endpoints == {"svc_a::evt", "svc_b::evt"}
assert links[0]["confidence"] == "INFERRED"
assert links[0]["confidence_score"] == 0.85
def test_same_name_in_different_namespaces_is_not_linked(tmp_path):
+4
View File
@@ -76,6 +76,10 @@ _INJECTED = {
def test_single_implementer_links_the_interface_method(tmp_path):
dispatch, r = _extract(tmp_path, _INJECTED)
assert (_find(r, ".Build()", "ireport"), _find(r, ".Build()", "report_report")) in dispatch
for edge in r["edges"]:
if edge["relation"] == "dispatches_to":
assert edge["confidence"] == "INFERRED"
assert edge["confidence_score"] == 0.85
def test_chain_through_an_injected_dependency_becomes_reachable(tmp_path):
+72 -12
View File
@@ -18,6 +18,7 @@ promote AST `uses` edges to EXTRACTED/1.0, which is a semantic judgement about
what the extractor knows; snapping the scores onto the documented scale fixes
the stated violation without making that call.
"""
import ast
from pathlib import Path
import pytest
@@ -63,19 +64,78 @@ def test_extracted_and_ambiguous_defaults_are_unchanged():
# No emission site ships an off-rubric literal
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("rel_path", [
"extract.py",
"symbol_resolution.py",
"extractors/engine.py",
"extractors/resolution.py",
def _literal_scores(expr):
"""Read only emitted literals, including both branches of a ternary.
Calls and lookups are consumers of scores, not literal emission sites.
Parsing Python also avoids matching examples in strings or comments.
"""
if isinstance(expr, ast.Constant) and type(expr.value) in (int, float):
yield expr.value
elif isinstance(expr, ast.IfExp):
yield from _literal_scores(expr.body)
yield from _literal_scores(expr.orelse)
def _emitted_score_exprs(tree):
for node in ast.walk(tree):
if isinstance(node, ast.Dict):
for key, value in zip(node.keys, node.values):
if isinstance(key, ast.Constant) and key.value == "confidence_score":
yield value
elif isinstance(node, ast.keyword) and node.arg == "confidence_score":
yield node.value
elif isinstance(node, ast.Assign) and any(
isinstance(target, ast.Name) and target.id == "confidence_score"
for target in node.targets
):
yield node.value
elif isinstance(node, ast.AnnAssign) and (
isinstance(node.target, ast.Name) and node.target.id == "confidence_score"
) and node.value is not None:
yield node.value
def test_no_module_hardcodes_an_off_rubric_score():
"""Cover every Python module and spelling, not a fixed list of resolvers.
EXTRACTED/1.0 and AMBIGUOUS/0.2 are valid too. Keep the separate
runtime checks below to verify that INFERRED edges use the INFERRED set.
"""
allowed = RUBRIC | {1.0, 0.2}
violations = []
for path in sorted(SRC.rglob("*.py")):
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
for expr in _emitted_score_exprs(tree):
for score in _literal_scores(expr):
if score not in allowed:
violations.append(f"{path.relative_to(SRC)}:{expr.lineno}: {score}")
assert not violations, "Off-rubric emitted scores:\n" + "\n".join(violations)
@pytest.mark.parametrize("source", [
'{"confidence_score": 0.8}',
'emit(confidence_score=0.8)',
'confidence_score = 0.8',
'confidence_score: float = 0.8',
'{"confidence_score": 1.0 if exact else 0.8}',
])
def test_no_module_hardcodes_an_off_rubric_inferred_score(rel_path):
"""0.8 was the value in the tree and is not on the scale. Catch it and the
forbidden 0.5 as literals, so a future edit cannot reintroduce either."""
text = (SRC / rel_path).read_text(encoding="utf-8")
for forbidden in ('"confidence_score": 0.8,', '"confidence_score": 0.5,',
"confidence_score = 0.8\n", "confidence_score = 0.5\n"):
assert forbidden not in text, f"{rel_path} still emits {forbidden.strip()}"
def test_emission_guard_recognizes_all_literal_spellings(source):
scores = [
score
for expr in _emitted_score_exprs(ast.parse(source))
for score in _literal_scores(expr)
]
assert 0.8 in scores
def test_emission_guard_ignores_comments_strings_and_score_consumers():
tree = ast.parse(
'# "confidence_score": 0.8\n'
'example = \'{"confidence_score": 0.8}\'\n'
'score = edge.get("confidence_score", 0.5)\n'
)
assert list(_emitted_score_exprs(tree)) == []
# ---------------------------------------------------------------------------
+3 -3
View File
@@ -91,7 +91,7 @@ def test_swift_cross_file_member_calls_have_correct_confidence_and_resolve(tmp_p
if e.get("relation") != "calls":
continue
if tgt_label in inferred_targets:
assert e["confidence"] == "INFERRED" and e["confidence_score"] == 0.8
assert e["confidence"] == "INFERRED" and e["confidence_score"] == 0.85
assert e["target"] in node_ids and src_by_id.get(e["target"])
seen_inferred.add(tgt_label)
elif tgt_label in extracted_targets:
@@ -295,7 +295,7 @@ def test_environment_attribute_typed_receiver_resolves(tmp_path: Path):
and _label(result, e["target"]) == ".reset()"), None)
assert edge is not None, "store.reset() must resolve to Store.reset"
assert _label(result, edge["source"]) == ".go()"
assert edge["confidence"] == "INFERRED" and edge["confidence_score"] == 0.8
assert edge["confidence"] == "INFERRED" and edge["confidence_score"] == 0.85
def test_environment_keypath_and_dotted_forms_are_skipped(tmp_path: Path):
@@ -368,7 +368,7 @@ def test_factory_returned_receiver_resolves(tmp_path: Path):
assert (".local()", "calls", ".go()") in calls # method-local receiver
for e in result["edges"]:
if e.get("relation") == "calls" and _label(result, e["target"]) == ".go()":
assert e["confidence"] == "INFERRED" and e["confidence_score"] == 0.8
assert e["confidence"] == "INFERRED" and e["confidence_score"] == 0.85
def test_factory_receiver_resolves_through_cross_file_extension(tmp_path: Path):
+1 -1
View File
@@ -105,7 +105,7 @@ def test_dispatch_edge_shape(tmp_path):
_, r = _extract(tmp_path, _INJECTED)
edge = next(e for e in r["edges"] if e["relation"] == "dispatches_to")
assert edge["confidence"] == "INFERRED"
assert edge["confidence_score"] == 0.9
assert edge["confidence_score"] == 0.85
assert edge["context"] == "call"
assert edge["source_file"].endswith("RemoteStore.swift")