fix(ids): legacy-id detector only inspects file-level nodes (no Go false-positive)

The graph_has_legacy_ids nudge flagged fresh v9 graphs that contain Go code,
because the Go extractor scopes a type node by package directory
(_make_id(pkg_dir, name) -> "sub_thing"), which coincides with the OLD one-parent
file-stem form of pkg/sub/thing.go even though it's a current, by-design
package-scoped id (methods on a type across files in a package share one node).

The detector now inspects only file-level nodes (source_location "L1"), whose id is
unambiguously the file stem and which the migration actually re-keys. This still
catches a genuine pre-#1504 graph (its file nodes carry the old stem) while leaving
package/dir-scoped symbol ids alone. Verified end-to-end: a fresh Go graph no longer
triggers the rebuild nudge; a real legacy graph still does.

The migration itself was already correct — file nodes and Python/AST symbols
migrate to full-path ids; Go's package-scoped type ids are intentional and
unaffected. This only fixes the over-eager warning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
safishamsi
2026-06-28 17:52:34 +01:00
co-authored by Claude Opus 4.8
parent 3999dbc67e
commit 73710d33ce
2 changed files with 17 additions and 7 deletions
+8 -3
View File
@@ -201,15 +201,20 @@ def graph_has_legacy_ids(nodes: list, root: str | Path | None = None, sample: in
stem) rather than the full repo-relative path. Read-only consumers (query,
serve) use this to nudge the user to rebuild, since they don't re-extract.
Heuristic and cheap: samples nodes with a relative ``source_file`` and returns
True as soon as one ID matches an OLD stem form but NOT the canonical full-path
form. Absolute/sourceless nodes are skipped (can't be classified)."""
Heuristic and cheap: only **file-level** nodes (source_location ``L1``) are
inspected, because their ID is unambiguously the file stem. Symbol nodes are
skipped — some extractors scope a symbol by package/directory (Go's
``_make_id(pkg_dir, name)`` → ``sub_thing``), which can coincide with an old
file-stem form and would otherwise false-positive. Returns True as soon as one
file node's ID matches an OLD stem form but not the canonical full-path form."""
from graphify.extractors.base import _file_stem
_r = str(root) if root is not None else None
checked = 0
for node in nodes:
if not isinstance(node, dict):
continue
if str(node.get("source_location") or "") != "L1":
continue # only file-level nodes carry an unambiguous file-stem ID
nid = node.get("id")
sf = node.get("source_file")
if not nid or not isinstance(nid, str) or not sf:
+9 -4
View File
@@ -726,13 +726,18 @@ def test_graph_has_legacy_ids_detects_old_scheme():
"""The read-only-consumer nudge (query/serve) flags a pre-#1504 graph and
leaves a canonical one alone."""
from graphify.build import graph_has_legacy_ids
old = [{"id": "api_readme", "source_file": "docs/v1/api/README.md", "type": "document"}]
new = [{"id": "docs_v1_api_readme", "source_file": "docs/v1/api/README.md", "type": "document"}]
old = [{"id": "api_readme", "source_file": "docs/v1/api/README.md", "type": "document", "source_location": "L1"}]
new = [{"id": "docs_v1_api_readme", "source_file": "docs/v1/api/README.md", "type": "document", "source_location": "L1"}]
assert graph_has_legacy_ids(old, root=".") is True
assert graph_has_legacy_ids(new, root=".") is False
# sourceless / top-level nodes don't false-positive
assert graph_has_legacy_ids([{"id": "setup", "source_file": "setup.py"}], root=".") is False
# sourceless / top-level file nodes don't false-positive
assert graph_has_legacy_ids([{"id": "setup", "source_file": "setup.py", "source_location": "L1"}], root=".") is False
assert graph_has_legacy_ids([{"id": "x", "label": "y"}], root=".") is False
# package/dir-scoped SYMBOL ids (Go's _make_id(pkg_dir, name) -> "sub_thing") must
# NOT false-positive: not file-level (no L1), so ignored even though "sub_thing"
# coincides with the old file-stem form of pkg/sub/thing.go.
go_symbol = [{"id": "sub_thing", "source_file": "pkg/sub/thing.go", "type": "code", "source_location": "L3"}]
assert graph_has_legacy_ids(go_symbol, root=".") is False
def test_semantic_rekey_relative_vs_absolute_source_file():