You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
bug(indexer): Updating one file destroys globally-shared Module nodes and other files' edges #1392
tools/indexing/persistence/writer.py:1330-1338 (delete) vs :406 (global MERGE)
Module is one of three labels merged without a path, yet File -[:CONTAINS]-> Module is still created (item_mappings, :291). The per-file delete then runs:
MATCH (f:File{path: $path})
OPTIONALMATCH (f)-[:CONTAINS]->(element)
DETACHDELETEf, element, p
which deletes the shared Module node — severing every other file's IMPORTS/INCLUDES edge to it. Re-adding the changed file recreates the Module node, but the other files are not reprocessed (they are neither CALLS callers nor INHERITS neighbours), so those edges are never restored.
Reproduced. Ruby repo where mod.rb defines module Shared and a.rb/b.rbinclude Shared:
[before] Module 'Shared' nodes = ['Shared']
INCLUDES edges = [[email protected], [email protected]]
[after delete_file_from_graph('mod.rb')] ← exactly what an edit triggers
Module 'Shared' nodes = []
INCLUDES edges = []
Fix. Exclude pathless shared labels from the cascade (WHERE element.path = $path), deleting only the CONTAINS relationship and letting _purge_dangling_pathless_nodes reclaim orphans.
Audit environment
Version
codegraphcontext 0.5.2 (PyPI)
Commit
df3af04
Backend
FalkorDB Lite (falkordblite 0.9.0), Python 3.12.3, Linux
Corpus
the CodeGraphContext repo itself — 784 files, 23 languages, 5,501 Function nodes
Audit ID I-H04 · severity high · from an end-to-end audit of CGC 0.5.2 (63 findings). Reported with the reproduction as run.
I tried to fix this and backed the fix out. Reporting what I found, because the issue is real but the suggested fix does not work.
Confirmed, with a sharper reproduction
The failure needs update_file_in_graph (what the watcher actually calls on an edit), not just delete_file_from_graph:
repo: mod.rb defines `module Shared`; a.rb and b.rb both `include Shared`
after index : INCLUDES edges -> 2
after edit mod.rb : INCLUDES edges -> 0 ← never restored
The Module node is recreated when mod.rb is re-added, but a.rb/b.rb are not reprocessed — they are neither CALLS callers nor INHERITS neighbours — so their INCLUDES edges are gone for good.
Why the proposed fix does not work
The report suggests filtering the cascade with WHERE element.path = $path. I implemented that and it changes nothing here, because the Ruby Module node does carry a path, equal to mod.rb:
MATCH (f:File {path: $mod_rb})
OPTIONAL MATCH (f)-[:CONTAINS]->(element)
WHERE element.path = $mod_rb
RETURN element.name, label(element)
-> helper (Function)
-> Shared (Module) ← still matched, still deleted
Also: the pathless case does not reproduce
I tested the other half of the premise — a genuinely pathless shared Module — and it is not affected:
a.py and b.py both `import os` -> Module 'os', 2 importers
delete_file_from_graph(a.py) -> Module 'os' survives, 1 importer ← correct
That is because imported modules are reached by IMPORTS, not CONTAINS, and the cascade only follows CONTAINS. Both baseline main and the patched version behave identically here.
What a real fix needs
Exclude Module from the DETACH DELETE and drop only the CONTAINS relationship, so the node and its inbound INCLUDES/IMPORTS edges survive re-indexing of the defining file. The obstacle is that the label predicate is not portable — Kùzu uses label(n), Neo4j uses labels(n) — so it needs either a backend branch or a second query. The alternative is to re-link dependents after re-adding a file that defines shared symbols.
I did not want to guess at that in a PR I could not verify on both backends. #1425 contains only the encoding fix; this one is untouched.
Activity
github-actions commentedon Jul 26, 2026
Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ
Shashankss1205 commentedon Jul 26, 2026
I tried to fix this and backed the fix out. Reporting what I found, because the issue is real but the suggested fix does not work.
Confirmed, with a sharper reproduction
The failure needs
update_file_in_graph(what the watcher actually calls on an edit), not justdelete_file_from_graph:The Module node is recreated when
mod.rbis re-added, buta.rb/b.rbare not reprocessed — they are neither CALLS callers nor INHERITS neighbours — so theirINCLUDESedges are gone for good.Why the proposed fix does not work
The report suggests filtering the cascade with
WHERE element.path = $path. I implemented that and it changes nothing here, because the RubyModulenode does carry a path, equal tomod.rb:Also: the pathless case does not reproduce
I tested the other half of the premise — a genuinely pathless shared Module — and it is not affected:
That is because imported modules are reached by
IMPORTS, notCONTAINS, and the cascade only followsCONTAINS. Both baselinemainand the patched version behave identically here.What a real fix needs
Exclude
Modulefrom theDETACH DELETEand drop only theCONTAINSrelationship, so the node and its inboundINCLUDES/IMPORTSedges survive re-indexing of the defining file. The obstacle is that the label predicate is not portable — Kùzu useslabel(n), Neo4j useslabels(n)— so it needs either a backend branch or a second query. The alternative is to re-link dependents after re-adding a file that defines shared symbols.I did not want to guess at that in a PR I could not verify on both backends. #1425 contains only the encoding fix; this one is untouched.