From 3f8efaebc2bb3eecc3ab12e6a1d38fc12e4b143d Mon Sep 17 00:00:00 2001 From: Safi Date: Tue, 26 May 2026 20:05:31 +0100 Subject: [PATCH] fix graphify update ghost nodes: as_posix() and relativize existing graph before eviction Three issues in _rebuild_code (watch.py): 1. _relativize_source_files was called on result after eviction list was built, so existing nodes with absolute source_file were never normalized before comparison 2. deleted_paths and evict_sources used str() (backslashes on Windows) while graph.json stores forward-slash paths via _norm_source_file 3. _relativize_source_files itself used str() instead of as_posix() Also fix extract.py source_file relativization to use as_posix(). Closes #1007. Co-Authored-By: Claude Sonnet 4.6 --- graphify/extract.py | 2 +- graphify/watch.py | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/graphify/extract.py b/graphify/extract.py index 588211b5..87dde9ae 100644 --- a/graphify/extract.py +++ b/graphify/extract.py @@ -8637,7 +8637,7 @@ def extract( if not sf_path.is_absolute(): continue try: - item["source_file"] = str(sf_path.relative_to(root)) + item["source_file"] = sf_path.relative_to(root).as_posix() except ValueError: pass diff --git a/graphify/watch.py b/graphify/watch.py index 9e74d19b..4a007644 100644 --- a/graphify/watch.py +++ b/graphify/watch.py @@ -138,7 +138,7 @@ def _relativize_source_files(payload: dict, root: Path) -> None: if not source_path.is_absolute(): continue try: - item["source_file"] = str(source_path.resolve().relative_to(root)) + item["source_file"] = source_path.resolve().relative_to(root).as_posix() except ValueError: continue @@ -376,9 +376,9 @@ def _rebuild_code( # (e.g. .gitignore, vendored). Either way, evict any # preserved nodes that still claim this source path. try: - deleted_paths.add(str(cand.relative_to(project_root))) + deleted_paths.add(cand.relative_to(project_root).as_posix()) except ValueError: - deleted_paths.add(str(cand)) + deleted_paths.add(cand.as_posix()) if not wanted and not deleted_paths: print("[graphify watch] No tracked code files in change set - skipping rebuild.") return True @@ -408,13 +408,14 @@ def _rebuild_code( existing = json.loads(existing_graph.read_text(encoding="utf-8")) existing_graph_data = existing new_ast_ids = {n["id"] for n in result["nodes"]} + _relativize_source_files(existing, project_root) evict_sources: set[str] = set(deleted_paths) if changed_paths is not None: for p in extract_targets: try: - evict_sources.add(str(p.relative_to(project_root))) + evict_sources.add(p.relative_to(project_root).as_posix()) except ValueError: - evict_sources.add(str(p)) + evict_sources.add(p.as_posix()) preserved_nodes = [ n for n in existing.get("nodes", []) if n["id"] not in new_ast_ids