From 3bdac481ef306796753af9209b3dbcdcb3f31b8a Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:43:55 +0530 Subject: [PATCH 01/10] fix: snap cross-repo call confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- graphify/cross_repo_calls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphify/cross_repo_calls.py b/graphify/cross_repo_calls.py index 553af7eaf9..32117e1822 100644 --- a/graphify/cross_repo_calls.py +++ b/graphify/cross_repo_calls.py @@ -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, From b8b10e37b5b2bb007fa32f603b410ca356b270e5 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:44:21 +0530 Subject: [PATCH 02/10] Decrease confidence score from 0.9 to 0.85 --- graphify/cross_repo_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphify/cross_repo_types.py b/graphify/cross_repo_types.py index 5bc269fe1d..3cd9ea0124 100644 --- a/graphify/cross_repo_types.py +++ b/graphify/cross_repo_types.py @@ -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, From 40f2882beb6d0c756cfd9b843e07e5eaec0cf6ec Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:44:32 +0530 Subject: [PATCH 03/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- graphify/interface_dispatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphify/interface_dispatch.py b/graphify/interface_dispatch.py index ddb53f815a..0ebab448ff 100644 --- a/graphify/interface_dispatch.py +++ b/graphify/interface_dispatch.py @@ -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, From e956336887fd87f5387e7c4db2acfaeb9d193a04 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:44:42 +0530 Subject: [PATCH 04/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- graphify/extract.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/graphify/extract.py b/graphify/extract.py index 9e87c05f9e..be3aa1bf52 100644 --- a/graphify/extract.py +++ b/graphify/extract.py @@ -3923,7 +3923,7 @@ def _key(label: str) -> str: "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, @@ -4215,7 +4215,7 @@ def _key(label: str) -> str: "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, @@ -4381,7 +4381,7 @@ def _key(label: str) -> str: "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, @@ -4603,7 +4603,7 @@ def _park_if_absent(type_name: str | None, caller_node: dict | None, rc: dict) - "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, @@ -4813,7 +4813,7 @@ def _method_on_type_or_bases(type_nid: str, callee_key: str) -> str | None: "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, @@ -5004,7 +5004,7 @@ def _field_type_up_chain(cls, receiver): "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, From 767edb538f7da1a2e26b0604ed8ef59c0d6219d7 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:45:00 +0530 Subject: [PATCH 05/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- tests/test_cross_repo_member_calls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_cross_repo_member_calls.py b/tests/test_cross_repo_member_calls.py index 3c16724646..fc6bc030ed 100644 --- a/tests/test_cross_repo_member_calls.py +++ b/tests/test_cross_repo_member_calls.py @@ -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" From 92e9e8b5faae084b8250b8f6bbccf521f12a4759 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:45:12 +0530 Subject: [PATCH 06/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- tests/test_cross_repo_shared_types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_cross_repo_shared_types.py b/tests/test_cross_repo_shared_types.py index de32d2d3b7..ace05c8979 100644 --- a/tests/test_cross_repo_shared_types.py +++ b/tests/test_cross_repo_shared_types.py @@ -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): From 776f1bd93836d0cd27e924c2df8b88212e0a7fd4 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:45:22 +0530 Subject: [PATCH 07/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- tests/test_csharp_interface_dispatch.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_csharp_interface_dispatch.py b/tests/test_csharp_interface_dispatch.py index 0735ffebeb..420c0b2d08 100644 --- a/tests/test_csharp_interface_dispatch.py +++ b/tests/test_csharp_interface_dispatch.py @@ -76,6 +76,10 @@ def _reachable(r, start: str) -> set[str]: 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): From 6012e6fd69300aad5fc79ca78896053a17281dcb Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:45:33 +0530 Subject: [PATCH 08/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- tests/test_inferred_confidence_rubric.py | 84 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/tests/test_inferred_confidence_rubric.py b/tests/test_inferred_confidence_rubric.py index b9c47c93eb..3dc1834301 100644 --- a/tests/test_inferred_confidence_rubric.py +++ b/tests/test_inferred_confidence_rubric.py @@ -18,6 +18,7 @@ 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)) == [] # --------------------------------------------------------------------------- From 1ea3b39ed907711838999ed02df834d0ae8de8a4 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:45:44 +0530 Subject: [PATCH 09/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- tests/test_swift_cross_file_calls.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_swift_cross_file_calls.py b/tests/test_swift_cross_file_calls.py index 81e56ae515..fb7cacc001 100644 --- a/tests/test_swift_cross_file_calls.py +++ b/tests/test_swift_cross_file_calls.py @@ -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): From 142027736438c660ce0dd01dc00a9674b344a0ed Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Sun, 4 Oct 2026 07:45:53 +0530 Subject: [PATCH 10/10] fix: snap cross-repo confidence_score to the INFERRED rubric (#4045) Refs #4045. Co-Authored-By: Claude --- tests/test_swift_protocol_dispatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_swift_protocol_dispatch.py b/tests/test_swift_protocol_dispatch.py index 5d77128058..579ffa7608 100644 --- a/tests/test_swift_protocol_dispatch.py +++ b/tests/test_swift_protocol_dispatch.py @@ -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")