Backfill edge source_file from endpoint nodes (#1279)

Semantic/LLM edges occasionally omit source_file, which build only normalized
when already present, so the field reached graph.json empty and downstream
validation flagged it. Backfill from the source (then target) node in
build_from_json and in the --no-cluster raw-write path, which bypasses it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Safi
2026-06-17 13:49:31 +01:00
co-authored by Claude Opus 4.8
parent 9c8bd4d47c
commit 8437ef465e
3 changed files with 34 additions and 0 deletions
+8
View File
@@ -4453,6 +4453,14 @@ def main() -> None:
merged["nodes"] = _dedupe_nodes(merged["nodes"])
merged["edges"] = _dedupe_edges(merged["edges"])
# Backfill source_file from endpoint nodes — this raw path bypasses
# build_from_json's backfill, and semantic edges sometimes omit it (#1279).
_node_sf = {n.get("id"): n.get("source_file") for n in merged["nodes"]}
for _e in merged["edges"]:
if not _e.get("source_file"):
_e["source_file"] = (
_node_sf.get(_e.get("source")) or _node_sf.get(_e.get("target")) or ""
)
_backup(graphify_out)
graph_json_path.write_text(
json.dumps(merged, indent=2), encoding="utf-8"
+9
View File
@@ -296,6 +296,15 @@ def build_from_json(extraction: dict, *, directed: bool = False, root: str | Pat
if src not in node_set or tgt not in node_set:
continue # skip edges to external/stdlib nodes - expected, not an error
attrs = {k: v for k, v in edge.items() if k not in ("source", "target")}
# Backfill source_file from the endpoint nodes (every node carries one).
# Semantic/LLM edges occasionally omit it, which downstream validation
# flags and leaves query results with no file reference (#1279).
if not attrs.get("source_file"):
attrs["source_file"] = (
G.nodes[src].get("source_file")
or G.nodes[tgt].get("source_file")
or ""
)
if "source_file" in attrs:
attrs["source_file"] = _norm_source_file(attrs["source_file"], _root)
# Drop cross-language INFERRED `calls` edges — same short names (render,