gitnexus 1.6.8-rc.9 → 1.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) hide show
  1. package/README.md +3 -3
  2. package/dist/_shared/graph/types.d.ts +31 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.d.ts.map +1 -1
  5. package/dist/_shared/language-detection.js +11 -1
  6. package/dist/_shared/language-detection.js.map +1 -1
  7. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  8. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  9. package/dist/_shared/lbug/schema-constants.js +6 -0
  10. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  11. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  12. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  13. package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
  14. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  15. package/dist/cli/ai-context.d.ts +31 -1
  16. package/dist/cli/ai-context.js +18 -16
  17. package/dist/cli/analyze-config.js +11 -0
  18. package/dist/cli/analyze.d.ts +14 -0
  19. package/dist/cli/analyze.js +98 -0
  20. package/dist/cli/embedding-dims.d.ts +33 -0
  21. package/dist/cli/embedding-dims.js +39 -0
  22. package/dist/cli/eval-server.d.ts +8 -0
  23. package/dist/cli/eval-server.js +242 -2
  24. package/dist/cli/help-i18n.js +19 -0
  25. package/dist/cli/i18n/en.d.ts +15 -1
  26. package/dist/cli/i18n/en.js +15 -1
  27. package/dist/cli/i18n/resources.d.ts +29 -1
  28. package/dist/cli/i18n/zh-CN.d.ts +14 -0
  29. package/dist/cli/i18n/zh-CN.js +15 -1
  30. package/dist/cli/index.js +89 -1
  31. package/dist/cli/mcp.d.ts +6 -1
  32. package/dist/cli/mcp.js +26 -1
  33. package/dist/cli/optional-grammars.d.ts +6 -8
  34. package/dist/cli/optional-grammars.js +8 -11
  35. package/dist/cli/setup.d.ts +3 -1
  36. package/dist/cli/setup.js +67 -17
  37. package/dist/cli/skill-gen.js +2 -1
  38. package/dist/cli/tool.d.ts +19 -1
  39. package/dist/cli/tool.js +96 -3
  40. package/dist/core/embeddings/hf-env.d.ts +0 -66
  41. package/dist/core/embeddings/http-client.d.ts +7 -0
  42. package/dist/core/embeddings/http-client.js +5 -3
  43. package/dist/core/embeddings/server-mapping.d.ts +1 -1
  44. package/dist/core/embeddings/server-mapping.js +7 -3
  45. package/dist/core/graph/import-cycles.d.ts +10 -0
  46. package/dist/core/graph/import-cycles.js +103 -0
  47. package/dist/core/group/cross-impact.d.ts +2 -1
  48. package/dist/core/group/cross-impact.js +5 -1
  49. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  50. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  51. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  52. package/dist/core/group/extractors/http-route-extractor.js +22 -4
  53. package/dist/core/group/extractors/include-extractor.js +22 -13
  54. package/dist/core/group/sync.d.ts +27 -1
  55. package/dist/core/group/sync.js +0 -0
  56. package/dist/core/incremental/subgraph-extract.js +18 -1
  57. package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
  58. package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
  59. package/dist/core/ingestion/cfg/collect.d.ts +51 -0
  60. package/dist/core/ingestion/cfg/collect.js +77 -0
  61. package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
  62. package/dist/core/ingestion/cfg/control-dependence.js +182 -0
  63. package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
  64. package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
  65. package/dist/core/ingestion/cfg/emit.d.ts +293 -0
  66. package/dist/core/ingestion/cfg/emit.js +646 -0
  67. package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
  68. package/dist/core/ingestion/cfg/post-dominators.js +176 -0
  69. package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
  70. package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
  71. package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
  72. package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
  73. package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
  74. package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
  75. package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
  76. package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
  77. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  78. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  79. package/dist/core/ingestion/cfg/types.d.ts +274 -0
  80. package/dist/core/ingestion/cfg/types.js +13 -0
  81. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
  82. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
  83. package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
  84. package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
  85. package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
  86. package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
  87. package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
  88. package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
  89. package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
  90. package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
  91. package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
  92. package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
  93. package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
  94. package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
  95. package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
  96. package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
  97. package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
  98. package/dist/core/ingestion/cfg/visitors/go.js +638 -0
  99. package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
  100. package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
  101. package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
  102. package/dist/core/ingestion/cfg/visitors/java.js +812 -0
  103. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
  104. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
  105. package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
  106. package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
  107. package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
  108. package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
  109. package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
  110. package/dist/core/ingestion/cfg/visitors/php.js +721 -0
  111. package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
  112. package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
  113. package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
  114. package/dist/core/ingestion/cfg/visitors/python.js +558 -0
  115. package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
  116. package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
  117. package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
  118. package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
  119. package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
  120. package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
  121. package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
  122. package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
  123. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
  124. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
  125. package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
  126. package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
  127. package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
  128. package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
  129. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
  130. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
  131. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  132. package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
  133. package/dist/core/ingestion/import-resolvers/utils.js +2 -0
  134. package/dist/core/ingestion/language-provider.d.ts +11 -0
  135. package/dist/core/ingestion/languages/c-cpp.js +6 -1
  136. package/dist/core/ingestion/languages/cobol.js +4 -0
  137. package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
  138. package/dist/core/ingestion/languages/cpp/captures.js +50 -1
  139. package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
  140. package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
  141. package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
  142. package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
  143. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
  144. package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
  145. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  146. package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
  147. package/dist/core/ingestion/languages/csharp.js +2 -0
  148. package/dist/core/ingestion/languages/dart.js +2 -0
  149. package/dist/core/ingestion/languages/go.js +2 -0
  150. package/dist/core/ingestion/languages/java.js +3 -0
  151. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  152. package/dist/core/ingestion/languages/kotlin.js +3 -0
  153. package/dist/core/ingestion/languages/php.js +2 -0
  154. package/dist/core/ingestion/languages/python.js +2 -0
  155. package/dist/core/ingestion/languages/ruby.js +2 -0
  156. package/dist/core/ingestion/languages/rust.js +2 -0
  157. package/dist/core/ingestion/languages/swift.js +2 -0
  158. package/dist/core/ingestion/languages/typescript.js +5 -0
  159. package/dist/core/ingestion/languages/vue.js +6 -0
  160. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  161. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  162. package/dist/core/ingestion/method-types.d.ts +2 -0
  163. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  164. package/dist/core/ingestion/model/symbol-table.js +1 -0
  165. package/dist/core/ingestion/mro-processor.js +109 -0
  166. package/dist/core/ingestion/parsing-processor.js +23 -0
  167. package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
  168. package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
  169. package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
  170. package/dist/core/ingestion/pipeline-phases/index.js +2 -0
  171. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
  172. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  173. package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
  174. package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
  175. package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
  176. package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
  177. package/dist/core/ingestion/pipeline.d.ts +92 -10
  178. package/dist/core/ingestion/pipeline.js +11 -2
  179. package/dist/core/ingestion/scope-extractor.js +12 -2
  180. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
  181. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
  182. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
  183. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
  184. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
  185. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
  186. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
  187. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
  188. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
  189. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
  190. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
  191. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
  192. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
  193. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
  194. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
  195. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  196. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
  197. package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
  198. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  199. package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
  200. package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
  201. package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
  202. package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
  203. package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
  204. package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
  205. package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
  206. package/dist/core/ingestion/taint/call-summary-model.js +25 -0
  207. package/dist/core/ingestion/taint/emit.d.ts +125 -0
  208. package/dist/core/ingestion/taint/emit.js +208 -0
  209. package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
  210. package/dist/core/ingestion/taint/interproc-emit.js +88 -0
  211. package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
  212. package/dist/core/ingestion/taint/interproc-solver.js +312 -0
  213. package/dist/core/ingestion/taint/match.d.ts +153 -0
  214. package/dist/core/ingestion/taint/match.js +278 -0
  215. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  216. package/dist/core/ingestion/taint/path-codec.js +190 -0
  217. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  218. package/dist/core/ingestion/taint/propagate.js +659 -0
  219. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  220. package/dist/core/ingestion/taint/site-safety.js +98 -0
  221. package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
  222. package/dist/core/ingestion/taint/source-sink-config.js +35 -12
  223. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  224. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  225. package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
  226. package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
  227. package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
  228. package/dist/core/ingestion/taint/summary-harvest.js +537 -0
  229. package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
  230. package/dist/core/ingestion/taint/summary-model.js +86 -0
  231. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  232. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  233. package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
  234. package/dist/core/ingestion/utils/env.d.ts +7 -0
  235. package/dist/core/ingestion/utils/env.js +12 -0
  236. package/dist/core/ingestion/utils/method-props.js +1 -0
  237. package/dist/core/ingestion/workers/parse-worker.d.ts +13 -0
  238. package/dist/core/ingestion/workers/parse-worker.js +76 -17
  239. package/dist/core/ingestion/workers/worker-pool.d.ts +9 -40
  240. package/dist/core/ingestion/workers/worker-pool.js +15 -2
  241. package/dist/core/lbug/csv-generator.d.ts +51 -3
  242. package/dist/core/lbug/csv-generator.js +369 -255
  243. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  244. package/dist/core/lbug/lbug-adapter.js +346 -84
  245. package/dist/core/lbug/lbug-config.d.ts +19 -0
  246. package/dist/core/lbug/lbug-config.js +29 -0
  247. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  248. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  249. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  250. package/dist/core/lbug/pool-adapter.js +90 -1
  251. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  252. package/dist/core/lbug/rel-pair-routing.js +140 -0
  253. package/dist/core/lbug/schema.d.ts +2 -2
  254. package/dist/core/lbug/schema.js +3 -0
  255. package/dist/core/logger.d.ts +0 -5
  256. package/dist/core/run-analyze.d.ts +96 -0
  257. package/dist/core/run-analyze.js +207 -3
  258. package/dist/core/tree-sitter/parser-loader.js +5 -4
  259. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  260. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  261. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  262. package/dist/core/wiki/generator.js +3 -1
  263. package/dist/core/wiki/graph-queries.d.ts +6 -0
  264. package/dist/core/wiki/graph-queries.js +9 -1
  265. package/dist/mcp/http-transport.d.ts +134 -0
  266. package/dist/mcp/http-transport.js +503 -0
  267. package/dist/mcp/local/local-backend.d.ts +110 -24
  268. package/dist/mcp/local/local-backend.js +1225 -19
  269. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  270. package/dist/mcp/local/pdg-impact.js +1972 -0
  271. package/dist/mcp/resources.js +13 -1
  272. package/dist/mcp/tools.d.ts +12 -0
  273. package/dist/mcp/tools.js +214 -7
  274. package/dist/server/analyze-launch.d.ts +29 -0
  275. package/dist/server/analyze-launch.js +137 -0
  276. package/dist/server/analyze-upload.d.ts +33 -0
  277. package/dist/server/analyze-upload.js +123 -0
  278. package/dist/server/analyze-worker.d.ts +17 -1
  279. package/dist/server/api.d.ts +17 -0
  280. package/dist/server/api.js +132 -168
  281. package/dist/server/git-clone.d.ts +43 -2
  282. package/dist/server/git-clone.js +211 -16
  283. package/dist/server/mcp-http.d.ts +5 -6
  284. package/dist/server/mcp-http.js +9 -82
  285. package/dist/server/middleware.d.ts +40 -0
  286. package/dist/server/middleware.js +92 -0
  287. package/dist/server/private-ip.d.ts +1 -0
  288. package/dist/server/private-ip.js +15 -0
  289. package/dist/server/upload-ingest.d.ts +56 -0
  290. package/dist/server/upload-ingest.js +276 -0
  291. package/dist/server/upload-paths.d.ts +37 -0
  292. package/dist/server/upload-paths.js +57 -0
  293. package/dist/server/upload-sweep.d.ts +20 -0
  294. package/dist/server/upload-sweep.js +57 -0
  295. package/dist/storage/git.js +5 -8
  296. package/dist/storage/parse-cache.d.ts +22 -1
  297. package/dist/storage/parse-cache.js +41 -10
  298. package/dist/storage/repo-manager.d.ts +121 -10
  299. package/dist/storage/repo-manager.js +43 -5
  300. package/dist/types/pipeline.d.ts +9 -0
  301. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  302. package/hooks/claude/gitnexus-hook.cjs +110 -13
  303. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  304. package/package.json +8 -2
  305. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  306. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  307. package/scripts/cross-platform-tests.ts +1 -0
  308. package/skills/gitnexus-debugging.md +16 -4
  309. package/skills/gitnexus-exploring.md +3 -3
  310. package/skills/gitnexus-guide.md +33 -0
  311. package/skills/gitnexus-pdg-query.md +89 -0
  312. package/skills/gitnexus-refactoring.md +1 -1
  313. package/skills/gitnexus-taint-analysis.md +178 -0
  314. package/vendor/tree-sitter-c/package.json +1 -1
  315. package/vendor/tree-sitter-dart/package.json +1 -1
  316. package/vendor/tree-sitter-kotlin/package.json +1 -1
  317. package/vendor/tree-sitter-proto/package.json +1 -1
  318. package/vendor/tree-sitter-swift/package.json +1 -1
  319. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  320. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  321. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  322. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  323. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  324. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  325. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  326. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  327. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  328. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  329. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  330. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  331. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  332. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  333. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  334. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  335. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  336. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  337. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  338. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  339. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  340. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  341. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  342. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  343. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  344. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  345. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  346. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  347. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  348. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  349. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  350. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  351. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  352. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  353. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  354. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  355. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  356. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  357. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  358. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  359. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  360. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  361. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  362. package/web/assets/index-BvYev1UJ.js +635 -0
  363. package/web/assets/index-_lgn7hs5.css +2 -0
  364. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  365. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  366. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  367. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  368. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  369. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  370. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  371. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  372. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  373. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  374. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  375. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  376. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  377. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  378. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  379. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  380. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  381. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  382. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  383. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  384. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  385. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  386. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  387. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  388. package/web/index.html +13 -15
  389. package/scripts/materialize-vendor-grammars.cjs +0 -97
  390. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  391. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  392. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  393. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  394. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  395. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  396. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  397. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  398. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  399. package/web/assets/chunk-CilyBKbf.js +0 -1
  400. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  401. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  402. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  403. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  404. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  405. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  406. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  407. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  408. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  409. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  410. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  411. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  412. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  413. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  414. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  415. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  416. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  417. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  418. package/web/assets/index-BKWA-m7o.css +0 -2
  419. package/web/assets/index-CG6q8eTs.js +0 -626
  420. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  421. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  422. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  423. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  424. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  425. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  426. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  427. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  428. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  429. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  430. /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
@@ -4,20 +4,28 @@ import { createInterface } from 'readline';
4
4
  import { once } from 'events';
5
5
  import { finished } from 'stream/promises';
6
6
  import path from 'path';
7
- import os from 'os';
8
- import crypto from 'crypto';
9
7
  import lbug from '@ladybugdb/core';
10
8
  import { closeQueryResults } from './query-result-utils.js';
11
9
  import { NODE_TABLES, REL_TABLE_NAME, SCHEMA_QUERIES, EMBEDDING_TABLE_NAME, CREATE_VECTOR_INDEX_QUERY, STALE_HASH_SENTINEL, } from './schema.js';
12
10
  import { streamAllCSVsToDisk } from './csv-generator.js';
11
+ import { getNodeLabel as deriveNodeLabel } from './rel-pair-routing.js';
13
12
  import { extensionManager } from './extension-loader.js';
14
- import { closeLbugConnection, isDbBusyError, isOpenRetryExhausted, isWalCorruptionError, openLbugConnection, toNativeSafePath, WAL_RECOVERY_SUGGESTION, waitForWindowsHandleRelease, } from './lbug-config.js';
13
+ import { closeLbugConnection, isDbBusyError, isOpenRetryExhausted, isWalCorruptionError, openLbugConnection, toNativeSafePath, resolveNativeSafeStorageDir, WAL_RECOVERY_SUGGESTION, waitForWindowsHandleRelease, } from './lbug-config.js';
15
14
  import { finalizeLbugSidecarsAfterClose, inspectLbugSidecars, isMissingShadowSidecarError, isReadOnlyShadowReplayError, preflightLbugSidecars, quarantineWalForMissingShadow, renameFailureMessage, shadowSidecarRecoveryMessage, } from './sidecar-recovery.js';
16
15
  import { isVectorExtensionSupportedByPlatform } from '../platform/capabilities.js';
17
16
  import { logger } from '../logger.js';
18
17
  /**
19
18
  * Split a relationship CSV into per-label-pair files on disk.
20
19
  *
20
+ * @internal RETAINED AS A DIFFERENTIAL ORACLE. As of #2203 U2, production emit
21
+ * routes relationships to per-pair files directly during the single pass (see
22
+ * RelPairRouter in `rel-pair-routing.ts`), so this function has NO production
23
+ * callers — it is kept ONLY so the byte-identity test in
24
+ * `test/integration/csv-pipeline.test.ts` ("direct per-pair emit matches the
25
+ * split oracle") can diff the direct-emit output against this proven path. Do
26
+ * NOT delete it as dead code without also removing that test and accepting the
27
+ * loss of the byte-identity guard (and likewise `test/unit/rel-csv-split.test.ts`).
28
+ *
21
29
  * Streams the CSV line-by-line, routing each relationship to a file named
22
30
  * `rel_{fromLabel}_{toLabel}.csv`. Handles backpressure correctly: only one
23
31
  * drain listener per stream at a time, and readline resumes only when ALL
@@ -732,91 +740,212 @@ const doInitLbug = async (dbPath, readOnly = false) => {
732
740
  currentDbPath = dbPath;
733
741
  return { db, conn };
734
742
  };
735
- export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress) => {
736
- if (!conn) {
737
- throw new Error('LadybugDB not initialized. Call initLbug first.');
738
- }
739
- const log = onProgress || (() => { });
740
- let csvDir;
741
- if (process.platform === 'win32' && /[^\x00-\x7F]/.test(storagePath)) {
742
- const hash = crypto.createHash('sha256').update(storagePath).digest('hex').slice(0, 16);
743
- csvDir = toNativeSafePath(path.join(os.tmpdir(), `gitnexus-csv-${hash}`));
743
+ /**
744
+ * Run a COPY, retrying once with IGNORE_ERRORS=true (which skips row-level
745
+ * errors) on first failure. On a second failure, hand the RAW retry error to
746
+ * `onError` — each call site formats + slices its own message (#2226 F5: node
747
+ * COPY slices to 200 chars and throws; relationship COPY slices to 80 and warns,
748
+ * so the helper must not pre-format and lose that distinction). `onError` may
749
+ * throw to propagate the failure.
750
+ */
751
+ const copyCsvWithRetry = async (targetConn, copyQuery, onError) => {
752
+ try {
753
+ await queryAndDrain(targetConn, copyQuery);
744
754
  }
745
- else {
746
- csvDir = path.join(storagePath, 'csv');
755
+ catch {
756
+ try {
757
+ const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
758
+ await queryAndDrain(targetConn, retryQuery);
759
+ }
760
+ catch (retryErr) {
761
+ onError(retryErr);
762
+ }
747
763
  }
748
- log('Streaming CSVs to disk...');
749
- const csvResult = await streamAllCSVsToDisk(graph, repoPath, csvDir);
750
- const validTables = new Set(NODE_TABLES);
751
- const getNodeLabel = (nodeId) => {
752
- if (nodeId.startsWith('comm_'))
753
- return 'Community';
754
- if (nodeId.startsWith('proc_'))
755
- return 'Process';
756
- return nodeId.split(':')[0];
757
- };
758
- // Bulk COPY all node CSVs (sequential — LadybugDB allows only one write txn at a time)
759
- const nodeFiles = [...csvResult.nodeFiles.entries()];
760
- const totalSteps = nodeFiles.length + 1; // +1 for relationships
764
+ };
765
+ /**
766
+ * Bulk-COPY every node CSV sequentially on the single writable connection
767
+ * (LadybugDB allows one write txn at a time). Extracted from loadGraphToLbug so
768
+ * it can run either at the node-phase boundary — overlapping the relationship
769
+ * emit pass (#2203) — or after emit in the serial escape-hatch path. Each COPY
770
+ * keeps the IGNORE_ERRORS=true retry; a hard failure throws (no node rows ⇒ the
771
+ * relationship COPY would dangle on missing endpoints).
772
+ */
773
+ const copyNodeCSVs = async (targetConn, nodeFileEntries, log, totalSteps) => {
761
774
  let stepsDone = 0;
762
- for (const [table, { csvPath, rows }] of nodeFiles) {
775
+ for (const [table, { csvPath, rows }] of nodeFileEntries) {
763
776
  stepsDone++;
764
777
  log(`Loading nodes ${stepsDone}/${totalSteps}: ${table} (${rows.toLocaleString()} rows)`);
765
- const normalizedPath = normalizeCopyPath(csvPath);
766
- const copyQuery = getCopyQuery(table, normalizedPath);
767
- try {
768
- await queryAndDrain(conn, copyQuery);
769
- }
770
- catch (err) {
771
- try {
772
- const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
773
- await queryAndDrain(conn, retryQuery);
774
- }
775
- catch (retryErr) {
776
- const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
777
- throw new Error(`COPY failed for ${table}: ${retryMsg.slice(0, 200)}`);
778
+ const copyQuery = getCopyQuery(table, normalizeCopyPath(csvPath));
779
+ await copyCsvWithRetry(targetConn, copyQuery, (retryErr) => {
780
+ const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
781
+ throw new Error(`COPY failed for ${table}: ${retryMsg.slice(0, 200)}`);
782
+ });
783
+ }
784
+ };
785
+ /**
786
+ * Persist a KnowledgeGraph: stream CSVs, then bulk-COPY nodes (overlapped with
787
+ * relationship emit — see the body) and relationships.
788
+ *
789
+ * NOT TRANSACTIONAL (#2226). Each `COPY` commits independently and there is no
790
+ * surrounding transaction, so a failure partway through — a node `COPY` that
791
+ * throws at the FK barrier, a relationship `COPY` failure, or a `pdgEmitManifest`
792
+ * collision raised after node rows have already committed in the overlap path —
793
+ * leaves a partially-loaded DB. The caller surfaces the error; recovery is a
794
+ * `--force` re-analyze (a full rebuild), not a partial retry. Callers must not
795
+ * assume the DB is either fully loaded or untouched after a rejection.
796
+ */
797
+ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress,
798
+ /**
799
+ * Streamed PDG-emit manifest (#2202). When present (streaming was on, full
800
+ * rebuild), the BasicBlock node CSV + per-pair PDG-edge CSVs it points at
801
+ * were already flushed to disk during the emit loop; they are merged into the
802
+ * COPY plan below so they load alongside the structural CSVs. When streaming
803
+ * was on the in-memory `graph` holds zero BasicBlocks, so `streamAllCSVsToDisk`
804
+ * emits none — the manifest is the sole source and there is no double-COPY.
805
+ */
806
+ pdgEmitManifest) => {
807
+ if (!conn) {
808
+ throw new Error('LadybugDB not initialized. Call initLbug first.');
809
+ }
810
+ const log = onProgress || (() => { });
811
+ // ── #2203 persistence-path profiling ──────────────────────────────────
812
+ // Mirrors the PROF_SCOPE_RESOLUTION pattern (scope-resolution/pipeline/
813
+ // run.ts): zero-cost when off — process.hrtime.bigint() is only read under
814
+ // PROF_LBUG_LOAD=1, and the summary is logged behind the same gate. Fills
815
+ // the gap that the DB-persistence path is un-timed today (the analyze
816
+ // "emit" number is the scope-resolution emit bucket, not this COPY path).
817
+ const PROF = process.env.PROF_LBUG_LOAD === '1';
818
+ // Escape hatch / differential oracle (#2203): force the legacy strictly-serial
819
+ // load order (emit everything, THEN COPY nodes, THEN COPY rels) instead of the
820
+ // default node-COPY ‖ rel-emit overlap. Lets an operator revert the behavior at
821
+ // runtime, and lets a test load the same graph both ways and assert identical
822
+ // persisted content.
823
+ const SERIAL = process.env.GITNEXUS_SERIAL_LBUG_LOAD === '1';
824
+ const mark = () => (PROF ? process.hrtime.bigint() : 0n);
825
+ const span = (a, b) => (Number(b - a) / 1e6).toFixed(1);
826
+ const tStart = mark();
827
+ const csvDir = resolveNativeSafeStorageDir(storagePath, 'csv');
828
+ // The single writable connection (LadybugDB is single-writer). Captured as a
829
+ // const so the node-COPY closure has a non-null reference — TS cannot narrow
830
+ // the reassignable module-level `conn` across the callback boundary.
831
+ const writeConn = conn;
832
+ const validTables = new Set(NODE_TABLES);
833
+ // Merge the streamed PDG-emit node CSVs (#2202) into a node-file map. Collision
834
+ // guard: a BasicBlock in the in-memory graph during a streamed run is an
835
+ // invariant violation (streamAllCSVsToDisk would also emit basicblock.csv), so
836
+ // fail loudly rather than drop rows (#2202 review #3). Runs at the node-phase
837
+ // boundary so the manifest BasicBlock table COPYs with the structural CSVs.
838
+ const mergeManifestNodeFiles = (nodeFilesMap) => {
839
+ if (!pdgEmitManifest)
840
+ return;
841
+ for (const [table, meta] of pdgEmitManifest.nodeFiles) {
842
+ if (nodeFilesMap.has(table)) {
843
+ throw new Error(`Streaming PDG manifest collides with a structural node CSV for "${table}" — ` +
844
+ `the in-memory graph should hold zero ${table} nodes when streaming. ` +
845
+ `A ${table} node leaked into the graph during a streamed emit.`);
778
846
  }
847
+ nodeFilesMap.set(table, meta);
779
848
  }
780
- }
781
- // Bulk COPY relationships split by FROM→TO label pair (LadybugDB requires it)
782
- const { relHeader, relsByPairMeta, pairWriteStreams, skippedRels, totalValidRels } = await splitRelCsvByLabelPair(csvResult.relCsvPath, csvDir, validTables, getNodeLabel);
783
- // Close all per-pair write streams before COPY. `stream/promises.finished`
784
- // resolves on the stream's 'finish' event and rejects on 'error' replaces
785
- // a hand-rolled promisification with the stdlib primitive.
786
- await Promise.all(Array.from(pairWriteStreams.values()).map(async (ws) => {
787
- ws.end();
788
- await finished(ws);
789
- }));
849
+ };
850
+ // Node COPY is the only DB write that can overlap relationship CSV emit: the
851
+ // rel pass writes new rel_*.csv files and never touches `conn`, while node COPY
852
+ // uses `conn` and never touches the rel files. We start node COPY at the
853
+ // node-phase boundary and let the rel pass run concurrentlythe only
854
+ // single-writer-safe parallelism (#2203). The rel COPY still waits for node
855
+ // COPY (FK precondition), so the DB load order is unchanged.
856
+ let nodeCopyPromise;
857
+ let nodeCopyError;
858
+ const beginNodeCopy = (nodeFilesMap) => {
859
+ mergeManifestNodeFiles(nodeFilesMap);
860
+ const entries = [...nodeFilesMap.entries()];
861
+ // copyNodeCSVs logs node progress as step/total; it processes only node
862
+ // tables (the rel COPY has its own "Loading edges" progress line), so the
863
+ // denominator is the node-table count — not +1 reserving a rel step.
864
+ // .catch captures the failure so an overlapped (mid-emit) rejection cannot
865
+ // surface as an unhandled rejection; it is rethrown at the FK barrier below.
866
+ nodeCopyPromise = copyNodeCSVs(writeConn, entries, log, entries.length).catch((e) => {
867
+ nodeCopyError = e;
868
+ });
869
+ };
870
+ log('Streaming CSVs to disk...');
871
+ let csvResult;
872
+ try {
873
+ csvResult = SERIAL
874
+ ? await streamAllCSVsToDisk(graph, repoPath, csvDir)
875
+ : await streamAllCSVsToDisk(graph, repoPath, csvDir, beginNodeCopy);
876
+ }
877
+ catch (emitErr) {
878
+ // Relationship emit failed. In overlap mode a node COPY may be in flight —
879
+ // settle it (the .catch above means this never rejects) before rethrowing so
880
+ // it cannot leak as an unhandled rejection.
881
+ if (nodeCopyPromise)
882
+ await nodeCopyPromise;
883
+ // If node COPY ALSO failed, emitErr wins the throw — log the swallowed node
884
+ // error so a half-loaded DB isn't misattributed to the emit failure alone.
885
+ if (nodeCopyError) {
886
+ logger.warn({ err: nodeCopyError }, '[lbug-load] node COPY also failed while relationship emit was failing');
887
+ }
888
+ throw emitErr;
889
+ }
890
+ const tCsv = mark();
891
+ // Merge the streamed PDG-emit per-pair rel CSVs (#2202) into the COPY plan —
892
+ // collision-guarded. Done BEFORE node COPY so the serial escape hatch detects a
893
+ // manifest/structural pair collision before committing any node rows (legacy
894
+ // parity with the pre-overlap path), and the overlap path detects it as early
895
+ // as csvResult is available. When a manifest is present, streaming was on and
896
+ // the in-memory graph held zero BasicBlocks, so a structural collision means a
897
+ // streaming-invariant violation — fail loudly rather than load corrupt data.
898
+ if (pdgEmitManifest) {
899
+ for (const [pairKey, meta] of pdgEmitManifest.relsByPair) {
900
+ if (csvResult.relsByPair.has(pairKey)) {
901
+ throw new Error(`Streaming PDG manifest collides with a structural relationship CSV for pair ` +
902
+ `"${pairKey}" — a PDG edge leaked into the in-memory graph during a streamed emit.`);
903
+ }
904
+ csvResult.relsByPair.set(pairKey, meta);
905
+ csvResult.totalValidRels += meta.rows;
906
+ }
907
+ }
908
+ // Serial path: all CSVs are on disk and node COPY has not started — start it
909
+ // here so the barrier below blocks on it exactly as the legacy path did.
910
+ if (SERIAL)
911
+ beginNodeCopy(csvResult.nodeFiles);
912
+ // FK barrier: node rows must exist before the relationship COPY resolves their
913
+ // endpoints. In overlap mode most of node COPY was hidden behind rel emit, so
914
+ // this await is the *residual* node-COPY time (≈0 when fully overlapped).
915
+ if (nodeCopyPromise)
916
+ await nodeCopyPromise;
917
+ if (nodeCopyError) {
918
+ throw nodeCopyError instanceof Error ? nodeCopyError : new Error(String(nodeCopyError));
919
+ }
920
+ const tCopyNodes = mark();
921
+ // Bulk COPY relationships. They were already routed to per-FROM→TO-label-pair
922
+ // files during the emit pass (#2203 U2) — there is no monolithic relations.csv
923
+ // to re-read/re-split here; we COPY each pair file directly.
924
+ const { relsByPair, relHeader, skippedRels, totalValidRels } = csvResult;
925
+ let tCopyRels = tCopyNodes;
926
+ let tFallback = tCopyNodes;
790
927
  const insertedRels = totalValidRels;
791
928
  const warnings = [];
792
929
  if (insertedRels > 0) {
793
- log(`Loading edges: ${insertedRels.toLocaleString()} across ${relsByPairMeta.size} types`);
930
+ log(`Loading edges: ${insertedRels.toLocaleString()} across ${relsByPair.size} types`);
794
931
  let pairIdx = 0;
795
932
  let failedPairEdges = 0;
796
933
  const failedPairCsvPaths = new Set();
797
- for (const [pairKey, { csvPath: pairCsvPath, rows }] of relsByPairMeta) {
934
+ for (const [pairKey, { csvPath: pairCsvPath, rows }] of relsByPair) {
798
935
  pairIdx++;
799
936
  const [fromLabel, toLabel] = pairKey.split('|');
800
937
  const normalizedPath = normalizeCopyPath(pairCsvPath);
938
+ // PARALLEL=false is load-bearing here too — see COPY_CSV_OPTS (#2203 / kuzudb/kuzu#5778).
801
939
  const copyQuery = `COPY ${REL_TABLE_NAME} FROM "${normalizedPath}" (from="${fromLabel}", to="${toLabel}", HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`;
802
940
  if (pairIdx % 5 === 0 || rows > 1000) {
803
- log(`Loading edges: ${pairIdx}/${relsByPairMeta.size} types (${fromLabel} -> ${toLabel})`);
804
- }
805
- try {
806
- await queryAndDrain(conn, copyQuery);
807
- }
808
- catch (err) {
809
- try {
810
- const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
811
- await queryAndDrain(conn, retryQuery);
812
- }
813
- catch (retryErr) {
814
- const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
815
- warnings.push(`${fromLabel}->${toLabel} (${rows} edges): ${retryMsg.slice(0, 80)}`);
816
- failedPairEdges += rows;
817
- failedPairCsvPaths.add(pairCsvPath);
818
- }
941
+ log(`Loading edges: ${pairIdx}/${relsByPair.size} types (${fromLabel} -> ${toLabel})`);
819
942
  }
943
+ await copyCsvWithRetry(conn, copyQuery, (retryErr) => {
944
+ const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
945
+ warnings.push(`${fromLabel}->${toLabel} (${rows} edges): ${retryMsg.slice(0, 80)}`);
946
+ failedPairEdges += rows;
947
+ failedPairCsvPaths.add(pairCsvPath);
948
+ });
820
949
  // Only delete if not in failedPairCsvPaths (needed for fallback)
821
950
  if (!failedPairCsvPaths.has(pairCsvPath)) {
822
951
  try {
@@ -825,6 +954,7 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
825
954
  catch { }
826
955
  }
827
956
  }
957
+ tCopyRels = mark();
828
958
  if (failedPairCsvPaths.size > 0) {
829
959
  log(`Inserting ${failedPairEdges} edges individually (missing schema pairs)`);
830
960
  // Read failed pair files and merge for fallback inserts
@@ -846,15 +976,13 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
846
976
  catch { }
847
977
  }
848
978
  if (allLines.length > 1) {
849
- await fallbackRelationshipInserts(allLines, validTables, getNodeLabel);
979
+ await fallbackRelationshipInserts(allLines, validTables, deriveNodeLabel);
850
980
  }
851
981
  }
982
+ tFallback = mark();
852
983
  }
853
- // Cleanup all CSVs
854
- try {
855
- await fs.unlink(csvResult.relCsvPath);
856
- }
857
- catch { }
984
+ // Cleanup all CSVs (per-pair rel files are unlinked in the COPY loop above;
985
+ // the remaining sweep below catches node CSVs + any leftover pair files).
858
986
  for (const [, { csvPath }] of csvResult.nodeFiles) {
859
987
  try {
860
988
  await fs.unlink(csvPath);
@@ -875,13 +1003,39 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
875
1003
  await fs.rmdir(csvDir);
876
1004
  }
877
1005
  catch { }
1006
+ if (PROF) {
1007
+ const tEnd = mark();
1008
+ let totalNodeRows = 0;
1009
+ for (const [, { rows }] of csvResult.nodeFiles)
1010
+ totalNodeRows += rows;
1011
+ // `mode` records which load path ran. In overlap mode `csv-emit` is the wall
1012
+ // to streamAllCSVsToDisk's return (node COPY overlapped part of it) and
1013
+ // `copy-nodes` is the RESIDUAL node-COPY await after emit returned — it
1014
+ // trends to 0 as the overlap hides node COPY behind relationship emit. In
1015
+ // serial mode the buckets carry their legacy, disjoint meaning.
1016
+ logger.warn(`[lbug-load prof] mode=${SERIAL ? 'serial' : 'overlap'} csv-emit=${span(tStart, tCsv)}ms ` +
1017
+ `copy-nodes=${span(tCsv, tCopyNodes)}ms copy-rels=${span(tCopyNodes, tCopyRels)}ms ` +
1018
+ `fallback=${span(tCopyRels, tFallback)}ms total=${span(tStart, tEnd)}ms ` +
1019
+ `(${totalNodeRows} nodes, ${insertedRels} rels)`);
1020
+ }
878
1021
  return { success: true, insertedRels, skippedRels, warnings };
879
1022
  };
880
1023
  // LadybugDB default ESCAPE is '\' (backslash), but our CSV uses RFC 4180 escaping ("" for literal quotes).
881
1024
  // Source code content is full of backslashes which confuse the auto-detection.
882
1025
  // We MUST explicitly set ESCAPE='"' to use RFC 4180 escaping, and disable auto_detect to prevent
883
1026
  // LadybugDB from overriding our settings based on sample rows.
884
- const COPY_CSV_OPTS = `(HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`;
1027
+ //
1028
+ // PARALLEL=false IS LOAD-BEARING FOR CORRECTNESS — DO NOT FLIP IT (#2203).
1029
+ // LadybugDB's parallel CSV reader (Kuzu-derived; default PARALLEL=true) splits the
1030
+ // file into byte ranges parsed concurrently, and CANNOT determine line boundaries
1031
+ // when a quoted field contains an embedded newline — it errors with "Quoted newlines
1032
+ // are not supported in parallel CSV reader. Please specify PARALLEL=FALSE", or worse,
1033
+ // mis-parses silently (upstream kuzudb/kuzu#5778, still open). Our `content`/`text`
1034
+ // columns hold source code, so quoted multiline fields are guaranteed. PARALLEL=false
1035
+ // is therefore required, not conservative. The multiline-quoted round-trip in
1036
+ // test/integration/copy-parallel-invariant.test.ts fails loudly if this is ever flipped.
1037
+ // Exported so that test asserts the invariant statically as well.
1038
+ export const COPY_CSV_OPTS = `(HEADER=true, ESCAPE='"', DELIM=',', QUOTE='"', PARALLEL=false, auto_detect=false)`;
885
1039
  // Multi-language table names that were created with backticks in CODE_ELEMENT_BASE
886
1040
  // and must always be referenced with backticks in queries
887
1041
  const BACKTICK_TABLES = new Set([
@@ -947,7 +1101,7 @@ const TABLES_WITH_EXPORTED = new Set([
947
1101
  'Method',
948
1102
  'CodeElement',
949
1103
  ]);
950
- const getCopyQuery = (table, filePath) => {
1104
+ export const getCopyQuery = (table, filePath) => {
951
1105
  const t = escapeTableName(table);
952
1106
  if (table === 'File') {
953
1107
  return `COPY ${t}(id, name, filePath, content) FROM "${filePath}" ${COPY_CSV_OPTS}`;
@@ -965,14 +1119,16 @@ const getCopyQuery = (table, filePath) => {
965
1119
  return `COPY ${t}(id, name, filePath, startLine, endLine, level, content, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
966
1120
  }
967
1121
  if (table === 'Route') {
968
- return `COPY ${t}(id, name, filePath, responseKeys, errorKeys, middleware) FROM "${filePath}" ${COPY_CSV_OPTS}`;
1122
+ return `COPY ${t}(id, name, filePath, responseKeys, errorKeys, middleware, method) FROM "${filePath}" ${COPY_CSV_OPTS}`;
969
1123
  }
970
1124
  if (table === 'Tool') {
971
1125
  return `COPY ${t}(id, name, filePath, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
972
1126
  }
973
1127
  if (table === 'BasicBlock') {
974
- // Taint/PDG substrate (issue #2080) — no name column.
975
- return `COPY ${t}(id, filePath, startLine, endLine, text) FROM "${filePath}" ${COPY_CSV_OPTS}`;
1128
+ // Taint/PDG substrate (issue #2080) — no name column. `callees` is the
1129
+ // statement-precise inter-procedural reach substrate (space-joined leaf names);
1130
+ // `calleeIds` is its SOUND parallel (space-joined resolved callee ids, #2227).
1131
+ return `COPY ${t}(id, filePath, startLine, endLine, text, callees, calleeIds) FROM "${filePath}" ${COPY_CSV_OPTS}`;
976
1132
  }
977
1133
  if (table === 'Method') {
978
1134
  return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
@@ -1024,8 +1180,9 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
1024
1180
  query = `CREATE (n:Section {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, level: ${properties.level || 1}, content: ${escapeValue(properties.content || '')}${descPart}})`;
1025
1181
  }
1026
1182
  else if (label === 'BasicBlock') {
1027
- // Taint/PDG substrate (issue #2080) — no name column.
1028
- query = `CREATE (n:BasicBlock {id: ${escapeValue(properties.id)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, text: ${escapeValue(properties.text || '')}})`;
1183
+ // Taint/PDG substrate (issue #2080) — no name column. `calleeIds` (#2227)
1184
+ // is the sound resolved-id parallel to the leaf-name `callees` set.
1185
+ query = `CREATE (n:BasicBlock {id: ${escapeValue(properties.id)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, text: ${escapeValue(properties.text || '')}, callees: ${escapeValue(properties.callees || '')}, calleeIds: ${escapeValue(properties.calleeIds || '')}})`;
1029
1186
  }
1030
1187
  else if (TABLES_WITH_EXPORTED.has(label)) {
1031
1188
  const descPart = properties.description
@@ -1111,8 +1268,9 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
1111
1268
  query = `MERGE (n:Section {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.level = ${properties.level || 1}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
1112
1269
  }
1113
1270
  else if (label === 'BasicBlock') {
1114
- // Taint/PDG substrate (issue #2080) — no name column.
1115
- query = `MERGE (n:BasicBlock {id: ${escapeValue(properties.id)}}) SET n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.text = ${escapeValue(properties.text || '')}`;
1271
+ // Taint/PDG substrate (issue #2080) — no name column. `calleeIds`
1272
+ // (#2227) is the sound resolved-id parallel to the `callees` set.
1273
+ query = `MERGE (n:BasicBlock {id: ${escapeValue(properties.id)}}) SET n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.text = ${escapeValue(properties.text || '')}, n.callees = ${escapeValue(properties.callees || '')}, n.calleeIds = ${escapeValue(properties.calleeIds || '')}`;
1116
1274
  }
1117
1275
  else if (TABLES_WITH_EXPORTED.has(label)) {
1118
1276
  const descPart = properties.description
@@ -1629,6 +1787,110 @@ export const deleteAllCommunitiesAndProcesses = async () => {
1629
1787
  }
1630
1788
  return { nodesDeleted };
1631
1789
  };
1790
+ /**
1791
+ * Drop every interprocedural `TAINT_PATH` relationship (#2084 M4 U6). Used at
1792
+ * the start of an incremental `--pdg` writeback so the `taintSummaries` phase
1793
+ * re-materialises them from scratch on the FULL recomputed graph.
1794
+ *
1795
+ * TAINT_PATH validity is a WHOLE-PROGRAM property (a flow A→C can be
1796
+ * invalidated by a change to an INTERMEDIATE function whose file is neither A
1797
+ * nor C). The endpoint-writability extract rule (`extractChangedSubgraph`)
1798
+ * cannot see that — an A→C edge between two unchanged files would be skipped
1799
+ * and a stale finding would survive. So, exactly like Community/Process, the
1800
+ * sound move is delete-all-then-rebuild: cheap because TAINT_PATH is sparse
1801
+ * (per-run capped), and the compute side already rebuilds every summary each
1802
+ * run. Relationship-level (TAINT_PATH is an edge type, not a node label), so a
1803
+ * plain DELETE on the typed CodeRelation rows — endpoints are untouched.
1804
+ */
1805
+ export const deleteAllInterprocTaintPaths = async () => {
1806
+ if (!conn) {
1807
+ throw new Error('LadybugDB not initialized. Call initLbug first.');
1808
+ }
1809
+ let edgesDeleted = 0;
1810
+ let countResult;
1811
+ try {
1812
+ countResult = await conn.query(`MATCH ()-[r:CodeRelation]->() WHERE r.type = 'TAINT_PATH' RETURN count(r) AS cnt`);
1813
+ const result = Array.isArray(countResult) ? countResult[0] : countResult;
1814
+ const rows = await result.getAll();
1815
+ const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
1816
+ if (count > 0) {
1817
+ await conn.query(`MATCH ()-[r:CodeRelation]->() WHERE r.type = 'TAINT_PATH' DELETE r`);
1818
+ edgesDeleted = count;
1819
+ }
1820
+ }
1821
+ catch (err) {
1822
+ // A missing table on a freshly-initialized DB is the benign, expected case
1823
+ // (the count query above is what throws) — stay silent. Any OTHER failure
1824
+ // (lock, disk, native error) would leave stale TAINT_PATH rows that the
1825
+ // subsequent re-extract then DUPLICATES (CodeRelation has no PK), so it
1826
+ // must ABORT the writeback (#2084 review P2-5): re-throw so the caller's
1827
+ // crash-recovery dirty flag forces a clean full rebuild on the next run,
1828
+ // rather than silently writing duplicate cross-function findings.
1829
+ const msg = err instanceof Error ? err.message : String(err);
1830
+ if (/no table|not exist|not found|does not exist|Table .* does not exist/i.test(msg)) {
1831
+ if (countResult)
1832
+ await closeQueryResults(countResult);
1833
+ return { edgesDeleted };
1834
+ }
1835
+ if (countResult)
1836
+ await closeQueryResults(countResult);
1837
+ throw new Error(`[taint-interproc] failed to clear existing TAINT_PATH edges before incremental ` +
1838
+ `re-write (${msg}) — aborting to avoid duplicate cross-function findings; ` +
1839
+ `the next run will full-rebuild`);
1840
+ }
1841
+ if (countResult)
1842
+ await closeQueryResults(countResult);
1843
+ return { edgesDeleted };
1844
+ };
1845
+ /**
1846
+ * Drop every `CALL_SUMMARY` relationship (PDG FU-C, U-C3). Used at the start of
1847
+ * an incremental `--pdg` writeback so the `callSummaries` phase re-materialises
1848
+ * them from scratch on the FULL recomputed graph.
1849
+ *
1850
+ * Mirrors {@link deleteAllInterprocTaintPaths}: CALL_SUMMARY is a self-loop edge
1851
+ * type (not a node label), so a plain DELETE on the typed CodeRelation rows
1852
+ * leaves endpoints untouched. `extractChangedSubgraph` re-includes ALL of them
1853
+ * from the fresh graph (`isGraphWideRelType`), so delete-all-then-rebuild keeps
1854
+ * an unchanged function's summary from being lost.
1855
+ */
1856
+ export const deleteAllCallSummaries = async () => {
1857
+ if (!conn) {
1858
+ throw new Error('LadybugDB not initialized. Call initLbug first.');
1859
+ }
1860
+ let edgesDeleted = 0;
1861
+ let countResult;
1862
+ try {
1863
+ countResult = await conn.query(`MATCH ()-[r:CodeRelation]->() WHERE r.type = 'CALL_SUMMARY' RETURN count(r) AS cnt`);
1864
+ const result = Array.isArray(countResult) ? countResult[0] : countResult;
1865
+ const rows = await result.getAll();
1866
+ const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
1867
+ if (count > 0) {
1868
+ await conn.query(`MATCH ()-[r:CodeRelation]->() WHERE r.type = 'CALL_SUMMARY' DELETE r`);
1869
+ edgesDeleted = count;
1870
+ }
1871
+ }
1872
+ catch (err) {
1873
+ // A missing table on a freshly-initialized DB is the benign, expected case
1874
+ // (the count query is what throws) — stay silent. Any OTHER failure would
1875
+ // leave stale rows that the re-extract then DUPLICATES (CodeRelation has no
1876
+ // PK), so it must ABORT the writeback: re-throw so the caller's crash-
1877
+ // recovery dirty flag forces a clean full rebuild on the next run.
1878
+ const msg = err instanceof Error ? err.message : String(err);
1879
+ if (/no table|not exist|not found|does not exist|Table .* does not exist/i.test(msg)) {
1880
+ if (countResult)
1881
+ await closeQueryResults(countResult);
1882
+ return { edgesDeleted };
1883
+ }
1884
+ if (countResult)
1885
+ await closeQueryResults(countResult);
1886
+ throw new Error(`[call-summary] failed to clear existing CALL_SUMMARY edges before incremental ` +
1887
+ `re-write (${msg}) — aborting to avoid duplicate summaries; ` +
1888
+ `the next run will full-rebuild`);
1889
+ }
1890
+ if (countResult)
1891
+ await closeQueryResults(countResult);
1892
+ return { edgesDeleted };
1893
+ };
1632
1894
  // ============================================================================
1633
1895
  // Full-Text Search (FTS) Functions
1634
1896
  // ============================================================================
@@ -1,6 +1,25 @@
1
1
  import type lbug from '@ladybugdb/core';
2
2
  export declare function cleanupNativePathJunctions(): void;
3
3
  export declare function toNativeSafePath(p: string): string;
4
+ /**
5
+ * Resolve the on-disk CSV staging dir for `<storagePath>/<subdir>`, applying the
6
+ * same ASCII-safe relocation `toNativeSafePath` enables: on Windows with a
7
+ * non-ASCII storage path, LadybugDB's bulk COPY cannot open files under that
8
+ * path, so the dir is relocated under `os.tmpdir()`. Shared by the structural
9
+ * `csv/` dir and the streaming `pdg-csv/` dir (#2202) so the two can never
10
+ * diverge on platform handling; the `gitnexus-<subdir>-` prefix keeps their tmp
11
+ * locations distinct and recognizable.
12
+ *
13
+ * The relocated dir is created with `fs.mkdtempSync` (a unique, mode-0700,
14
+ * guaranteed-not-pre-existing suffix) rather than a deterministic
15
+ * `gitnexus-<subdir>-<hash>` name. A predictable name in the world-readable OS
16
+ * temp dir is information-disclosure-prone and pre-plantable
17
+ * (CWE-377/378 / CodeQL `js/insecure-temporary-file`); mkdtemp's random suffix
18
+ * is the documented mitigation and is what reaches the streaming sink's
19
+ * `fs.openSync`. The non-Windows / ASCII path stays a pure `path.join` (no dir
20
+ * created) and is byte-identical to before.
21
+ */
22
+ export declare function resolveNativeSafeStorageDir(storagePath: string, subdir: string): string;
4
23
  /**
5
24
  * Shared configuration for `@ladybugdb/core` `Database` construction.
6
25
  *
@@ -179,6 +179,35 @@ export function toNativeSafePath(p) {
179
179
  '(fsutil 8dot3name set 0).');
180
180
  return p;
181
181
  }
182
+ /**
183
+ * Resolve the on-disk CSV staging dir for `<storagePath>/<subdir>`, applying the
184
+ * same ASCII-safe relocation `toNativeSafePath` enables: on Windows with a
185
+ * non-ASCII storage path, LadybugDB's bulk COPY cannot open files under that
186
+ * path, so the dir is relocated under `os.tmpdir()`. Shared by the structural
187
+ * `csv/` dir and the streaming `pdg-csv/` dir (#2202) so the two can never
188
+ * diverge on platform handling; the `gitnexus-<subdir>-` prefix keeps their tmp
189
+ * locations distinct and recognizable.
190
+ *
191
+ * The relocated dir is created with `fs.mkdtempSync` (a unique, mode-0700,
192
+ * guaranteed-not-pre-existing suffix) rather than a deterministic
193
+ * `gitnexus-<subdir>-<hash>` name. A predictable name in the world-readable OS
194
+ * temp dir is information-disclosure-prone and pre-plantable
195
+ * (CWE-377/378 / CodeQL `js/insecure-temporary-file`); mkdtemp's random suffix
196
+ * is the documented mitigation and is what reaches the streaming sink's
197
+ * `fs.openSync`. The non-Windows / ASCII path stays a pure `path.join` (no dir
198
+ * created) and is byte-identical to before.
199
+ */
200
+ export function resolveNativeSafeStorageDir(storagePath, subdir) {
201
+ if (process.platform === 'win32' && NON_ASCII_RE.test(storagePath)) {
202
+ // 8.3-shorten the tmpdir base first (a non-ASCII Windows *profile* path can
203
+ // make os.tmpdir() itself non-ASCII), THEN mkdtemp so the returned path —
204
+ // the one that flows into fs.openSync — is provably mkdtemp-sourced (random,
205
+ // exclusive) and clears the insecure-temp-file dataflow.
206
+ const base = toNativeSafePath(os.tmpdir());
207
+ return fsSync.mkdtempSync(path.join(base, `gitnexus-${subdir}-`));
208
+ }
209
+ return path.join(storagePath, subdir);
210
+ }
182
211
  /**
183
212
  * Shared configuration for `@ladybugdb/core` `Database` construction.
184
213
  *