PROBLEM
The Neo4j edge writers (src/build/neo4j/bolt.ts MERGE (a)-[r:${type}]->(b) SET r += row.p, and the equivalent in cypher.ts) merge relationships on the endpoint pair alone. The DDG legitimately carries several edges between the same statement pair — one per variable, plus the ssa/points-to prov split — and a conditional can carry a true/false CFG_NEXT pair between one node pair. Under a plain endpoint-pair MERGE these collapse to a single relationship whose var/prov/kind props are whichever row wrote last: the live graph silently materializes fewer dependence edges than the projection produced, and re-push order changes which one survives.
Found in codeanalyzer-python during the stage-5 v2 gates: its opt-in bolt count test (relationships in DB == len(rows.edges)) failed 106 != 109, root-caused to exactly this shape and fixed by giving discriminated relationship families an internal _k merge key — PY_DDG merges per (var, prov), PY_CFG_NEXT per kind (codeanalyzer-python PR for issue #98; see codeanalyzer/neo4j/{rows,bolt,cypher,project}.py). project.ts:129-131 emits DDG with var/prov props and CFG_NEXT with kind through the same plain MERGE, so the TS projection has the same bug class.
SUGGESTED FIX
Mirror the Python fix: an optional per-edge discriminant on the row model, MERGE (a)-[r:TYPE {_k: row.k}]->(b) when present, applied to DDG (key (var, prov)) and CFG_NEXT (key kind). A count-parity assertion in the bolt test (edges in DB == rows.edges.length) catches regressions.
PROBLEM
The Neo4j edge writers (
src/build/neo4j/bolt.tsMERGE (a)-[r:${type}]->(b) SET r += row.p, and the equivalent incypher.ts) merge relationships on the endpoint pair alone. The DDG legitimately carries several edges between the same statement pair — one per variable, plus the ssa/points-toprovsplit — and a conditional can carry a true/falseCFG_NEXTpair between one node pair. Under a plain endpoint-pair MERGE these collapse to a single relationship whosevar/prov/kindprops are whichever row wrote last: the live graph silently materializes fewer dependence edges than the projection produced, and re-push order changes which one survives.Found in codeanalyzer-python during the stage-5 v2 gates: its opt-in bolt count test (
relationships in DB == len(rows.edges)) failed 106 != 109, root-caused to exactly this shape and fixed by giving discriminated relationship families an internal_kmerge key —PY_DDGmerges per(var, prov),PY_CFG_NEXTperkind(codeanalyzer-pythonPR for issue #98; seecodeanalyzer/neo4j/{rows,bolt,cypher,project}.py).project.ts:129-131emitsDDGwithvar/provprops andCFG_NEXTwithkindthrough the same plain MERGE, so the TS projection has the same bug class.SUGGESTED FIX
Mirror the Python fix: an optional per-edge discriminant on the row model,
MERGE (a)-[r:TYPE {_k: row.k}]->(b)when present, applied toDDG(key(var, prov)) andCFG_NEXT(keykind). A count-parity assertion in the bolt test (edges in DB == rows.edges.length) catches regressions.