From 37efe877cf42c2b69aabd8d260adbe9e7d9c6add Mon Sep 17 00:00:00 2001 From: nothariharan Date: Wed, 21 Oct 2026 17:00:00 +0530 Subject: [PATCH] fix(markdown): keep owner evidence when the edge already exists The review bot flagged a real hazard in the owner-evidence bookkeeping: a mention whose `references` edge is already in the graph returned early, so the document never recorded that it cites the target. A later bare method mention in the same document then lost its owner and was dropped. `_add` now records the cited target before it decides whether the edge has to be written, so the evidence survives an edge that is already there. The pipeline cannot reach that state today (the resolver is handed a fresh edge list, and the incremental context only carries contains/method/ inherits/imports edges), so this is hardening rather than a fix for a reachable failure. The regression test drives the resolver directly with a pre-existing class edge. --- graphify/markdown_resolution.py | 11 ++++++++-- tests/test_markdown_code_spans.py | 36 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/graphify/markdown_resolution.py b/graphify/markdown_resolution.py index 1c174adb8..a131f5ed2 100644 --- a/graphify/markdown_resolution.py +++ b/graphify/markdown_resolution.py @@ -229,7 +229,14 @@ def resolve_markdown_mentions( def _add(rc: dict, target: str | None, confidence: str, score: float) -> None: caller = str(rc.get("caller_nid", "")) - if target is None or target == caller or (caller, target) in existing: + if target is None or target == caller: + return + doc = _posix(str(rc.get("source_file", ""))) + if (caller, target) in existing: + # The edge is already in the graph, but this document still CITES that + # target, so it stays on record as owner evidence for a later method + # mention in the same document (#4609 review finding). + cited_targets.setdefault(doc, set()).add(target) return existing.add((caller, target)) all_edges.append({ @@ -243,7 +250,7 @@ def resolve_markdown_mentions( "source_location": rc.get("source_location"), "weight": 1.0, }) - cited_targets.setdefault(_posix(str(rc.get("source_file", ""))), set()).add(target) + cited_targets.setdefault(doc, set()).add(target) # Pass 1: explicit evidence resolves on its own — a cited path, a dotted # qualifier chain, or a trailing (). Record those targets per document. diff --git a/tests/test_markdown_code_spans.py b/tests/test_markdown_code_spans.py index 7cde0df9e..603c21eca 100644 --- a/tests/test_markdown_code_spans.py +++ b/tests/test_markdown_code_spans.py @@ -346,3 +346,39 @@ def test_owner_cited_in_another_document_does_not_count(tmp_path): b = _node(r, "B") assert {t for (s, t) in _references(r) if s == b["id"]} == set() + + +def test_owner_evidence_survives_an_edge_that_already_exists(): + """A mention whose edge is already in the graph still counts as citing its target. + + The resolver skips an edge that is already there, but the document has still + cited that symbol, so the evidence must stay on record for a later method + mention in the same document (#4609, review finding on the PR). + """ + from graphify.markdown_resolution import resolve_markdown_mentions + + per_file = [{"raw_calls": [ + {"caller_nid": "doc_notes", "callee": "HandlerResult", "language": "markdown", + "context": "HandlerResult", "source_file": "notes.md", "source_location": "L3"}, + {"caller_nid": "doc_notes", "callee": "successWithWarnings", "language": "markdown", + "context": "successWithWarnings", "source_file": "notes.md", "source_location": "L3"}, + ]}] + nodes = [ + {"id": "doc_notes", "label": "Notes", "file_type": "document", + "source_file": "notes.md"}, + {"id": "handler_handlerresult", "label": "HandlerResult", "file_type": "code", + "_callable": True, "_callable_class": True, "source_file": "handler.py"}, + {"id": "handler_handlerresult_successwithwarnings", "label": ".successWithWarnings()", + "file_type": "code", "_callable": True, "source_file": "handler.py"}, + ] + edges = [ + {"source": "handler_handlerresult", + "target": "handler_handlerresult_successwithwarnings", "relation": "method"}, + # The class reference edge is already in the graph. + {"source": "doc_notes", "target": "handler_handlerresult", "relation": "references"}, + ] + + resolve_markdown_mentions(per_file, nodes, edges) + + assert ("doc_notes", "handler_handlerresult_successwithwarnings") in { + (e["source"], e["target"]) for e in edges if e.get("relation") == "references"}