Summary
The incremental bolt writer's orphan-prune step (codeanalyzer/neo4j/bolt.py, bolt_writer, full-run branch) deletes any :PyModule whose file_key is not in the current emit, across the entire database — it is not scoped to the --app-name being emitted. So emitting application B detaches/deletes application A's modules, leaving an orphaned :PyApplication {name: "A"} with zero PY_HAS_MODULE edges.
Environment
- codeanalyzer-python 0.2.0 (
--emit neo4j --neo4j-uri ..., i.e. live Bolt push)
Root cause
# bolt.py, bolt_writer(), full_run branch:
present = list(by_module.keys()) # only THIS emit's module file_keys
s.run(
"MATCH (m:PyModule) WHERE NOT m.file_key IN $present "
"OPTIONAL MATCH (m)-[...]->(x) DETACH DELETE x, m ...",
present=present,
)
present is global — every :PyModule in the DB is a prune candidate, with no filter tying m to the :PyApplication anchor being written.
Impact
A single Neo4j database cannot hold multiple applications populated via full-run --emit neo4j: each emit wipes the others' module subgraphs. Observed directly — after emitting app B, app A reports count{(a)-[:PY_HAS_MODULE]->()} = 0. This also makes it unsafe to run multiple projects' integration tests against one shared Neo4j instance.
Suggested fix
Scope the prune to the application being emitted, e.g. only prune modules reachable from this run's anchor:
MATCH (:PyApplication {name: $app})-[:PY_HAS_MODULE]->(m:PyModule)
WHERE NOT m.file_key IN $present
OPTIONAL MATCH (m)-[...]->(x)
DETACH DELETE x, m
Summary
The incremental bolt writer's orphan-prune step (
codeanalyzer/neo4j/bolt.py,bolt_writer, full-run branch) deletes any:PyModulewhosefile_keyis not in the current emit, across the entire database — it is not scoped to the--app-namebeing emitted. So emitting application B detaches/deletes application A's modules, leaving an orphaned:PyApplication {name: "A"}with zeroPY_HAS_MODULEedges.Environment
--emit neo4j --neo4j-uri ..., i.e. live Bolt push)Root cause
presentis global — every:PyModulein the DB is a prune candidate, with no filter tyingmto the:PyApplicationanchor being written.Impact
A single Neo4j database cannot hold multiple applications populated via full-run
--emit neo4j: each emit wipes the others' module subgraphs. Observed directly — after emitting app B, app A reportscount{(a)-[:PY_HAS_MODULE]->()} = 0. This also makes it unsafe to run multiple projects' integration tests against one shared Neo4j instance.Suggested fix
Scope the prune to the application being emitted, e.g. only prune modules reachable from this run's anchor: