gitnexus 1.6.8-rc.8 → 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 (439) 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 +23 -2
  135. package/dist/core/ingestion/languages/c-cpp.js +17 -5
  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 +8 -1
  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 +44 -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/clone-safety.d.ts +109 -0
  238. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  239. package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
  240. package/dist/core/ingestion/workers/parse-worker.js +81 -53
  241. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  242. package/dist/core/ingestion/workers/post-result.js +87 -0
  243. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  244. package/dist/core/ingestion/workers/result-merge.js +43 -0
  245. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
  246. package/dist/core/ingestion/workers/worker-pool.js +43 -16
  247. package/dist/core/lbug/csv-generator.d.ts +51 -3
  248. package/dist/core/lbug/csv-generator.js +369 -255
  249. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  250. package/dist/core/lbug/lbug-adapter.js +346 -84
  251. package/dist/core/lbug/lbug-config.d.ts +19 -0
  252. package/dist/core/lbug/lbug-config.js +29 -0
  253. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  254. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  255. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  256. package/dist/core/lbug/pool-adapter.js +90 -1
  257. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  258. package/dist/core/lbug/rel-pair-routing.js +140 -0
  259. package/dist/core/lbug/schema.d.ts +2 -2
  260. package/dist/core/lbug/schema.js +3 -0
  261. package/dist/core/logger.d.ts +0 -5
  262. package/dist/core/run-analyze.d.ts +96 -0
  263. package/dist/core/run-analyze.js +207 -3
  264. package/dist/core/tree-sitter/parser-loader.js +5 -4
  265. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  266. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  267. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  268. package/dist/core/wiki/generator.js +3 -1
  269. package/dist/core/wiki/graph-queries.d.ts +6 -0
  270. package/dist/core/wiki/graph-queries.js +9 -1
  271. package/dist/mcp/http-transport.d.ts +134 -0
  272. package/dist/mcp/http-transport.js +503 -0
  273. package/dist/mcp/local/local-backend.d.ts +110 -24
  274. package/dist/mcp/local/local-backend.js +1225 -19
  275. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  276. package/dist/mcp/local/pdg-impact.js +1972 -0
  277. package/dist/mcp/resources.js +13 -1
  278. package/dist/mcp/tools.d.ts +12 -0
  279. package/dist/mcp/tools.js +214 -7
  280. package/dist/server/analyze-launch.d.ts +29 -0
  281. package/dist/server/analyze-launch.js +137 -0
  282. package/dist/server/analyze-upload.d.ts +33 -0
  283. package/dist/server/analyze-upload.js +123 -0
  284. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  285. package/dist/server/analyze-worker-ipc.js +16 -0
  286. package/dist/server/analyze-worker.d.ts +17 -1
  287. package/dist/server/analyze-worker.js +7 -1
  288. package/dist/server/api.d.ts +17 -0
  289. package/dist/server/api.js +132 -168
  290. package/dist/server/git-clone.d.ts +43 -2
  291. package/dist/server/git-clone.js +211 -16
  292. package/dist/server/mcp-http.d.ts +5 -6
  293. package/dist/server/mcp-http.js +9 -82
  294. package/dist/server/middleware.d.ts +40 -0
  295. package/dist/server/middleware.js +92 -0
  296. package/dist/server/private-ip.d.ts +1 -0
  297. package/dist/server/private-ip.js +15 -0
  298. package/dist/server/upload-ingest.d.ts +56 -0
  299. package/dist/server/upload-ingest.js +276 -0
  300. package/dist/server/upload-paths.d.ts +37 -0
  301. package/dist/server/upload-paths.js +57 -0
  302. package/dist/server/upload-sweep.d.ts +20 -0
  303. package/dist/server/upload-sweep.js +57 -0
  304. package/dist/storage/git.js +5 -8
  305. package/dist/storage/parse-cache.d.ts +22 -1
  306. package/dist/storage/parse-cache.js +44 -10
  307. package/dist/storage/repo-manager.d.ts +121 -10
  308. package/dist/storage/repo-manager.js +43 -5
  309. package/dist/types/pipeline.d.ts +9 -0
  310. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  311. package/hooks/claude/gitnexus-hook.cjs +110 -13
  312. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  313. package/package.json +8 -2
  314. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  315. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  316. package/scripts/cross-platform-tests.ts +1 -0
  317. package/skills/gitnexus-debugging.md +16 -4
  318. package/skills/gitnexus-exploring.md +3 -3
  319. package/skills/gitnexus-guide.md +33 -0
  320. package/skills/gitnexus-pdg-query.md +89 -0
  321. package/skills/gitnexus-refactoring.md +1 -1
  322. package/skills/gitnexus-taint-analysis.md +178 -0
  323. package/vendor/tree-sitter-c/package.json +1 -1
  324. package/vendor/tree-sitter-dart/package.json +1 -1
  325. package/vendor/tree-sitter-kotlin/package.json +1 -1
  326. package/vendor/tree-sitter-proto/package.json +1 -1
  327. package/vendor/tree-sitter-swift/package.json +1 -1
  328. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  329. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  330. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  331. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  332. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  333. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  334. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  335. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  336. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  337. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  338. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  339. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  340. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  341. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  342. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  343. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  344. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  345. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  346. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  347. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  348. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  349. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  350. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  351. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  352. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  353. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  354. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  355. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  356. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  357. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  358. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  359. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  360. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  361. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  362. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  363. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  364. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  365. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  366. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  367. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  368. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  369. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  370. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  371. package/web/assets/index-BvYev1UJ.js +635 -0
  372. package/web/assets/index-_lgn7hs5.css +2 -0
  373. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  374. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  375. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  376. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  377. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  378. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  379. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  380. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  381. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  382. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  383. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  384. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  385. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  386. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  387. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  388. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  389. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  390. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  391. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  392. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  393. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  394. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  395. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  396. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  397. package/web/index.html +13 -15
  398. package/scripts/materialize-vendor-grammars.cjs +0 -97
  399. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  400. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  401. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  402. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  403. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  404. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  405. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  406. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  407. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  408. package/web/assets/chunk-CilyBKbf.js +0 -1
  409. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  410. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  411. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  412. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  413. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  414. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  415. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  416. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  417. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  418. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  419. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  420. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  421. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  422. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  423. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  424. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  425. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  426. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  427. package/web/assets/index-BKWA-m7o.css +0 -2
  428. package/web/assets/index-CG6q8eTs.js +0 -626
  429. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  430. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  431. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  432. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  433. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  434. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  435. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  436. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  437. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  438. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  439. /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Streaming PDG graph-emit sink (issue #2202).
3
+ *
4
+ * The PDG emit loop (`scope-resolution/pipeline/run.ts`, the `--pdg` block)
5
+ * materializes BasicBlock nodes + intra-file PDG edges (CFG / REACHING_DEF /
6
+ * CDG / POST_DOMINATE / TAINTED / SANITIZES) into the in-memory
7
+ * `KnowledgeGraph`. At full-kernel scale that layer dominates peak RSS
8
+ * (~7 GB at 511K BasicBlocks; ~100 GB extrapolated to the full kernel → OOM).
9
+ *
10
+ * `PdgEmitSink` is a write-routing façade over the real graph: the emit
11
+ * functions are write-only and compute every edge endpoint by deterministic id
12
+ * (audited — no read-back), so the sink can route BasicBlock node rows and PDG
13
+ * edge rows straight to bounded CSV-on-disk writers and **never store them**.
14
+ * The graph's resident size stops growing with the PDG layer → peak RSS becomes
15
+ * O(chunk buffer), not O(graph). Everything else (structural nodes/edges, the
16
+ * whole-program M4 TAINT_PATH edges) is delegated to the real graph unchanged.
17
+ *
18
+ * Why synchronous writers? The whole PDG emit (`runScopeResolution` and its
19
+ * per-file loop) is synchronous — there is no `await` point to drain an async
20
+ * stream, so a `BufferedCSVWriter` (Node `WriteStream`) would accumulate
21
+ * unwritten chunks in process memory across millions of rows, defeating the RSS
22
+ * bound. `fs.writeSync` goes straight to the OS; resident memory is bounded to
23
+ * one `chunkRows` buffer. This mirrors the sync-shard pattern in
24
+ * `storage/parsedfile-store.ts`.
25
+ *
26
+ * Byte-identity (issue acceptance): the sink reuses the SAME shared row
27
+ * builders (`buildBasicBlockRow`, `buildRelRow`) and label derivation
28
+ * (`getNodeLabel`) as `streamAllCSVsToDisk`, so the streamed CSV line SET is
29
+ * identical to the whole-graph emit's, and the bulk COPY loads the same rows →
30
+ * the persisted graph is SET-identical and DB-identical. The guarantee is
31
+ * set-level, not byte-level on the CSV file: the sink streams rows in emit
32
+ * order and does NOT re-sort them under `GITNEXUS_SORT_GRAPH_OUTPUT`, so a
33
+ * streamed CSV file is not necessarily byte-for-byte equal to the sorted
34
+ * whole-graph CSV — but the row set, and therefore the DB outcome, is. (The
35
+ * streamed CSVs are deleted right after the COPY, so their on-disk byte order
36
+ * is never observed.) Cross-pass dedup is done upstream, per FILE, in the emit
37
+ * loop (`run.ts` skips a file whose PDG already streamed) rather than in the
38
+ * sink, because a file can be PDG-emitted in more than one language pass (a
39
+ * `.ts` module imported by a `.vue` SFC is emitted in both the TypeScript and
40
+ * Vue context passes over the same worker-built CFG) and a sink-level per-id
41
+ * dedup set would retain every id → O(total ids) memory, defeating the
42
+ * O(chunk) RSS bound (#2202 review #1). The differential fingerprint test
43
+ * (issue #2202 U6) and the Vue+TS cross-pass integration test guard the set.
44
+ */
45
+ import type { GraphNode, GraphRelationship, RelationshipType } from '../../_shared/index.js';
46
+ import type { KnowledgeGraph } from '../graph/types.js';
47
+ import { type NodeTableName } from './schema.js';
48
+ /** Default streamed-write buffer (rows). Matches the whole-graph emit's
49
+ * `FLUSH_EVERY` order of magnitude; overridable via `GITNEXUS_PDG_EMIT_CHUNK_SIZE`. */
50
+ export declare const DEFAULT_PDG_EMIT_CHUNK_ROWS = 500;
51
+ /**
52
+ * COPY manifest produced by {@link PdgEmitSink.finalize}. Shaped to merge
53
+ * directly into `StreamedCSVResult` so `loadGraphToLbug` COPYs the streamed
54
+ * PDG CSVs through the same per-table / per-pair loops as the structural CSVs.
55
+ * Paths are absolute, so persistence needs no dir recomputation.
56
+ */
57
+ export interface PdgEmitManifest {
58
+ /** Node-table CSVs (only `BasicBlock` today). */
59
+ readonly nodeFiles: Map<NodeTableName, {
60
+ csvPath: string;
61
+ rows: number;
62
+ }>;
63
+ /** pairKey (`From|To`) → per-pair edge CSV. */
64
+ readonly relsByPair: Map<string, {
65
+ csvPath: string;
66
+ rows: number;
67
+ }>;
68
+ }
69
+ /**
70
+ * Write-routing graph façade. Construct one per analyze run, thread it into the
71
+ * per-language `runScopeResolution` calls in place of the real graph during the
72
+ * `--pdg` emit, then {@link finalize} once after the last language.
73
+ */
74
+ export declare class PdgEmitSink implements KnowledgeGraph {
75
+ private readonly real;
76
+ private readonly pdgCsvDir;
77
+ private readonly chunkRows;
78
+ private readonly validTables;
79
+ private bbWriter;
80
+ /** pairKey (`From|To`) → writer. PDG edges are all `BasicBlock|BasicBlock`,
81
+ * but the map keeps the sink general and the manifest pair-keyed. */
82
+ private readonly relWriters;
83
+ private finalized;
84
+ /**
85
+ * First writer-construction failure (a `fs.openSync` throwing on e.g. EMFILE
86
+ * — out of file descriptors). The failure happens inside the `SyncCsvWriter`
87
+ * constructor before a writer object exists to carry poison, so it is held
88
+ * here at the sink level and folded into the {@link finalize} error check.
89
+ * Like an in-flight write fault, an open failure mid-emit would otherwise be
90
+ * swallowed by the emit loop's per-file try/catch and silently drop the rest
91
+ * of that file's rows (#2202 review #4/#6).
92
+ */
93
+ private openFailure;
94
+ constructor(real: KnowledgeGraph, pdgCsvDir: string, chunkRows?: number);
95
+ addNode(node: GraphNode): void;
96
+ addRelationship(relationship: GraphRelationship): void;
97
+ /** Flush + close every streamed writer and return the COPY manifest. Every
98
+ * fd is closed even when a writer is poisoned (its `close` never throws); any
99
+ * IO fault — an in-flight write that poisoned a writer, a final-flush failure,
100
+ * or a writer-open failure (EMFILE) — is surfaced loudly here so a disk-full
101
+ * / out-of-fds run never hands a truncated CSV to the bulk COPY (#2202 review
102
+ * #4). The emit loop's per-file try/catch swallows the synchronous throw, so
103
+ * this poison check is the backstop that turns a silent partial manifest into
104
+ * a hard failure. */
105
+ finalize(): PdgEmitManifest;
106
+ /**
107
+ * Best-effort fd release for the error path — when a language pass throws
108
+ * before {@link finalize} runs, the caller's `finally` calls this so the
109
+ * BasicBlock + per-pair fds never leak. Idempotent with finalize via the
110
+ * `finalized` flag; close errors are swallowed because the run is already
111
+ * failing.
112
+ */
113
+ close(): void;
114
+ get nodes(): GraphNode[];
115
+ get relationships(): GraphRelationship[];
116
+ iterNodes(): IterableIterator<GraphNode>;
117
+ iterRelationships(): IterableIterator<GraphRelationship>;
118
+ iterRelationshipsByType(type: RelationshipType): IterableIterator<GraphRelationship>;
119
+ forEachNode(fn: (node: GraphNode) => void): void;
120
+ forEachRelationship(fn: (rel: GraphRelationship) => void): void;
121
+ getNode(id: string): GraphNode | undefined;
122
+ get nodeCount(): number;
123
+ get relationshipCount(): number;
124
+ removeNode(nodeId: string): boolean;
125
+ removeNodesByFile(filePath: string): number;
126
+ removeRelationship(relationshipId: string): boolean;
127
+ }
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Streaming PDG graph-emit sink (issue #2202).
3
+ *
4
+ * The PDG emit loop (`scope-resolution/pipeline/run.ts`, the `--pdg` block)
5
+ * materializes BasicBlock nodes + intra-file PDG edges (CFG / REACHING_DEF /
6
+ * CDG / POST_DOMINATE / TAINTED / SANITIZES) into the in-memory
7
+ * `KnowledgeGraph`. At full-kernel scale that layer dominates peak RSS
8
+ * (~7 GB at 511K BasicBlocks; ~100 GB extrapolated to the full kernel → OOM).
9
+ *
10
+ * `PdgEmitSink` is a write-routing façade over the real graph: the emit
11
+ * functions are write-only and compute every edge endpoint by deterministic id
12
+ * (audited — no read-back), so the sink can route BasicBlock node rows and PDG
13
+ * edge rows straight to bounded CSV-on-disk writers and **never store them**.
14
+ * The graph's resident size stops growing with the PDG layer → peak RSS becomes
15
+ * O(chunk buffer), not O(graph). Everything else (structural nodes/edges, the
16
+ * whole-program M4 TAINT_PATH edges) is delegated to the real graph unchanged.
17
+ *
18
+ * Why synchronous writers? The whole PDG emit (`runScopeResolution` and its
19
+ * per-file loop) is synchronous — there is no `await` point to drain an async
20
+ * stream, so a `BufferedCSVWriter` (Node `WriteStream`) would accumulate
21
+ * unwritten chunks in process memory across millions of rows, defeating the RSS
22
+ * bound. `fs.writeSync` goes straight to the OS; resident memory is bounded to
23
+ * one `chunkRows` buffer. This mirrors the sync-shard pattern in
24
+ * `storage/parsedfile-store.ts`.
25
+ *
26
+ * Byte-identity (issue acceptance): the sink reuses the SAME shared row
27
+ * builders (`buildBasicBlockRow`, `buildRelRow`) and label derivation
28
+ * (`getNodeLabel`) as `streamAllCSVsToDisk`, so the streamed CSV line SET is
29
+ * identical to the whole-graph emit's, and the bulk COPY loads the same rows →
30
+ * the persisted graph is SET-identical and DB-identical. The guarantee is
31
+ * set-level, not byte-level on the CSV file: the sink streams rows in emit
32
+ * order and does NOT re-sort them under `GITNEXUS_SORT_GRAPH_OUTPUT`, so a
33
+ * streamed CSV file is not necessarily byte-for-byte equal to the sorted
34
+ * whole-graph CSV — but the row set, and therefore the DB outcome, is. (The
35
+ * streamed CSVs are deleted right after the COPY, so their on-disk byte order
36
+ * is never observed.) Cross-pass dedup is done upstream, per FILE, in the emit
37
+ * loop (`run.ts` skips a file whose PDG already streamed) rather than in the
38
+ * sink, because a file can be PDG-emitted in more than one language pass (a
39
+ * `.ts` module imported by a `.vue` SFC is emitted in both the TypeScript and
40
+ * Vue context passes over the same worker-built CFG) and a sink-level per-id
41
+ * dedup set would retain every id → O(total ids) memory, defeating the
42
+ * O(chunk) RSS bound (#2202 review #1). The differential fingerprint test
43
+ * (issue #2202 U6) and the Vue+TS cross-pass integration test guard the set.
44
+ */
45
+ import fs from 'fs';
46
+ import path from 'path';
47
+ import { BASICBLOCK_CSV_HEADER, REL_CSV_HEADER, buildBasicBlockRow, buildRelRow, } from './csv-generator.js';
48
+ import { getNodeLabel } from './rel-pair-routing.js';
49
+ import { NODE_TABLES } from './schema.js';
50
+ /**
51
+ * PDG edge types streamed per-file (all intra-block BasicBlock→BasicBlock).
52
+ * `TAINT_PATH` is intentionally excluded — it is the whole-program M4 edge
53
+ * (Function→Function), computed in a separate post-resolution phase over the
54
+ * complete CALLS graph, and stays in the in-memory graph (it is small and is
55
+ * persisted by the normal whole-graph emit).
56
+ */
57
+ const PDG_EDGE_TYPES = new Set([
58
+ 'CFG',
59
+ 'REACHING_DEF',
60
+ 'CDG',
61
+ 'POST_DOMINATE',
62
+ 'TAINTED',
63
+ 'SANITIZES',
64
+ ]);
65
+ /** Default streamed-write buffer (rows). Matches the whole-graph emit's
66
+ * `FLUSH_EVERY` order of magnitude; overridable via `GITNEXUS_PDG_EMIT_CHUNK_SIZE`. */
67
+ export const DEFAULT_PDG_EMIT_CHUNK_ROWS = 500;
68
+ /**
69
+ * Synchronous buffered CSV writer. Buffers up to `chunkRows` rows, then issues
70
+ * one `fs.writeSync` straight to the OS (no in-process stream buffer). Header
71
+ * is written into the buffer at construction and is NOT counted in `rows`
72
+ * (matching `BufferedCSVWriter` semantics, so manifest row counts line up).
73
+ */
74
+ class SyncCsvWriter {
75
+ csvPath;
76
+ fd;
77
+ buf = [];
78
+ chunkRows;
79
+ rows = 0;
80
+ /**
81
+ * First IO error this writer hit (a `fs.writeSync` short-write loop throwing
82
+ * on e.g. disk-full). Once poisoned the writer refuses further rows and
83
+ * skips its final flush; the sink surfaces it from {@link PdgEmitSink.finalize}
84
+ * so a truncated CSV is never handed to the bulk COPY (#2202 review #4). A
85
+ * streamed-write failure is an IO fault, not the CFG-logic error that the
86
+ * emit loop's per-file try/catch is built to swallow — poisoning routes it
87
+ * past that catch to a loud failure.
88
+ */
89
+ poison = undefined;
90
+ constructor(csvPath, header, chunkRows) {
91
+ this.csvPath = csvPath;
92
+ // Guard a 0/negative buffer: the flush modulo would never fire and `buf`
93
+ // would grow unbounded, defeating the whole point of streaming.
94
+ this.chunkRows = Math.max(1, chunkRows);
95
+ // Exclusive create (O_EXCL): the streamed-CSV dir is wiped + recreated fresh
96
+ // by the PdgEmitSink constructor before any writer opens a file, so the path
97
+ // never pre-exists — 'wx' both matches that invariant and refuses to follow
98
+ // a pre-planted symlink at the path (CWE-377 / CodeQL js/insecure-temporary-file).
99
+ this.fd = fs.openSync(csvPath, 'wx');
100
+ this.buf.push(header);
101
+ }
102
+ addRow(row) {
103
+ // A poisoned writer is dead — stop buffering so memory can't grow on a
104
+ // writer whose fd is already in a bad state; finalize will report the fault.
105
+ if (this.poison !== undefined)
106
+ return;
107
+ this.buf.push(row);
108
+ this.rows++;
109
+ // Flush on DATA-row count, not buffer length: the header occupies buf[0]
110
+ // until the first flush, so a `buf.length >= chunkRows` test would fire one
111
+ // row early on the first chunk. Counting rows makes every flush exactly
112
+ // `chunkRows` rows.
113
+ if (this.rows % this.chunkRows === 0)
114
+ this.flushOrPoison();
115
+ }
116
+ /** Flush, recording (and re-throwing) any IO error as poison. Re-throwing
117
+ * lets the immediate caller log the per-file failure; the persisted `poison`
118
+ * is the backstop that makes finalize fail loudly even when that throw is
119
+ * swallowed by the emit loop's CFG try/catch. */
120
+ flushOrPoison() {
121
+ try {
122
+ this.flush();
123
+ }
124
+ catch (e) {
125
+ this.poison ??= e;
126
+ throw e;
127
+ }
128
+ }
129
+ flush() {
130
+ if (this.buf.length === 0)
131
+ return;
132
+ const data = Buffer.from(this.buf.join('\n') + '\n', 'utf8');
133
+ // fs.writeSync can return a short byte count; loop until the whole buffer
134
+ // lands so a partial write never truncates a CSV row mid-field.
135
+ let offset = 0;
136
+ while (offset < data.length) {
137
+ offset += fs.writeSync(this.fd, data, offset, data.length - offset);
138
+ }
139
+ this.buf.length = 0;
140
+ }
141
+ /** Flush remaining rows (unless already poisoned) and close the fd. Never
142
+ * throws: a final-flush IO error is recorded as poison and the fd is still
143
+ * closed, so a write error neither leaks an fd nor escapes here — the sink
144
+ * reads {@link poison} after closing every writer and fails loudly then. */
145
+ close() {
146
+ try {
147
+ if (this.poison === undefined)
148
+ this.flush();
149
+ }
150
+ catch (e) {
151
+ this.poison ??= e;
152
+ }
153
+ finally {
154
+ try {
155
+ fs.closeSync(this.fd);
156
+ }
157
+ catch {
158
+ /* fd may already be invalid after an IO fault — nothing to recover */
159
+ }
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * Write-routing graph façade. Construct one per analyze run, thread it into the
165
+ * per-language `runScopeResolution` calls in place of the real graph during the
166
+ * `--pdg` emit, then {@link finalize} once after the last language.
167
+ */
168
+ export class PdgEmitSink {
169
+ real;
170
+ pdgCsvDir;
171
+ chunkRows;
172
+ validTables;
173
+ bbWriter;
174
+ /** pairKey (`From|To`) → writer. PDG edges are all `BasicBlock|BasicBlock`,
175
+ * but the map keeps the sink general and the manifest pair-keyed. */
176
+ relWriters = new Map();
177
+ finalized = false;
178
+ /**
179
+ * First writer-construction failure (a `fs.openSync` throwing on e.g. EMFILE
180
+ * — out of file descriptors). The failure happens inside the `SyncCsvWriter`
181
+ * constructor before a writer object exists to carry poison, so it is held
182
+ * here at the sink level and folded into the {@link finalize} error check.
183
+ * Like an in-flight write fault, an open failure mid-emit would otherwise be
184
+ * swallowed by the emit loop's per-file try/catch and silently drop the rest
185
+ * of that file's rows (#2202 review #4/#6).
186
+ */
187
+ openFailure = undefined;
188
+ // NOTE on dedup: the same file can be PDG-emitted in more than one language
189
+ // pass (e.g. a `.ts` module imported by a `.vue` SFC is emitted in both the
190
+ // TypeScript pass and the Vue context pass over the same worker-built
191
+ // `cfgSideChannel`). The in-memory graph dedups that by id (first-writer-wins);
192
+ // this sink does NOT — to keep peak memory O(write buffer) rather than
193
+ // O(total ids), cross-pass dedup is done upstream, per FILE, in the emit loop
194
+ // (`run.ts` skips a file whose PDG already streamed via `pdgEmittedFiles`).
195
+ // The sink therefore receives each id exactly once and is a faithful
196
+ // pass-through; it must not be fed duplicate ids.
197
+ constructor(real, pdgCsvDir, chunkRows = DEFAULT_PDG_EMIT_CHUNK_ROWS) {
198
+ this.real = real;
199
+ this.pdgCsvDir = pdgCsvDir;
200
+ this.chunkRows = chunkRows;
201
+ this.validTables = new Set(NODE_TABLES);
202
+ // Clear any streamed CSVs left by a previous (possibly crashed) run so a
203
+ // later COPY never picks up stale rows.
204
+ fs.rmSync(pdgCsvDir, { recursive: true, force: true });
205
+ fs.mkdirSync(pdgCsvDir, { recursive: true });
206
+ }
207
+ // ── routed writes ──────────────────────────────────────────────────────────
208
+ addNode(node) {
209
+ if (node.label === 'BasicBlock') {
210
+ if (this.bbWriter === undefined) {
211
+ try {
212
+ this.bbWriter = new SyncCsvWriter(path.join(this.pdgCsvDir, 'basicblock.csv'), BASICBLOCK_CSV_HEADER, this.chunkRows);
213
+ }
214
+ catch (e) {
215
+ this.openFailure ??= e;
216
+ throw e;
217
+ }
218
+ }
219
+ this.bbWriter.addRow(buildBasicBlockRow(node));
220
+ return;
221
+ }
222
+ this.real.addNode(node);
223
+ }
224
+ addRelationship(relationship) {
225
+ if (PDG_EDGE_TYPES.has(relationship.type)) {
226
+ const fromLabel = getNodeLabel(relationship.sourceId);
227
+ const toLabel = getNodeLabel(relationship.targetId);
228
+ // Skip edges whose endpoint labels are not valid node tables — mirrors
229
+ // `RelPairRouter` exactly so the streamed set matches the whole-graph set.
230
+ if (!this.validTables.has(fromLabel) || !this.validTables.has(toLabel))
231
+ return;
232
+ const pairKey = `${fromLabel}|${toLabel}`;
233
+ let writer = this.relWriters.get(pairKey);
234
+ if (writer === undefined) {
235
+ try {
236
+ writer = new SyncCsvWriter(path.join(this.pdgCsvDir, `rel_${fromLabel}_${toLabel}.csv`), REL_CSV_HEADER, this.chunkRows);
237
+ }
238
+ catch (e) {
239
+ this.openFailure ??= e;
240
+ throw e;
241
+ }
242
+ this.relWriters.set(pairKey, writer);
243
+ }
244
+ writer.addRow(buildRelRow(relationship));
245
+ return;
246
+ }
247
+ this.real.addRelationship(relationship);
248
+ }
249
+ /** Flush + close every streamed writer and return the COPY manifest. Every
250
+ * fd is closed even when a writer is poisoned (its `close` never throws); any
251
+ * IO fault — an in-flight write that poisoned a writer, a final-flush failure,
252
+ * or a writer-open failure (EMFILE) — is surfaced loudly here so a disk-full
253
+ * / out-of-fds run never hands a truncated CSV to the bulk COPY (#2202 review
254
+ * #4). The emit loop's per-file try/catch swallows the synchronous throw, so
255
+ * this poison check is the backstop that turns a silent partial manifest into
256
+ * a hard failure. */
257
+ finalize() {
258
+ if (this.finalized)
259
+ throw new Error('PdgEmitSink.finalize() called twice');
260
+ this.finalized = true;
261
+ const errors = [];
262
+ if (this.openFailure !== undefined)
263
+ errors.push(this.openFailure);
264
+ const nodeFiles = new Map();
265
+ if (this.bbWriter !== undefined) {
266
+ this.bbWriter.close();
267
+ if (this.bbWriter.poison !== undefined)
268
+ errors.push(this.bbWriter.poison);
269
+ nodeFiles.set('BasicBlock', {
270
+ csvPath: this.bbWriter.csvPath,
271
+ rows: this.bbWriter.rows,
272
+ });
273
+ }
274
+ const relsByPair = new Map();
275
+ for (const [pairKey, writer] of this.relWriters) {
276
+ writer.close();
277
+ if (writer.poison !== undefined)
278
+ errors.push(writer.poison);
279
+ relsByPair.set(pairKey, { csvPath: writer.csvPath, rows: writer.rows });
280
+ }
281
+ if (errors.length > 0) {
282
+ const first = errors[0];
283
+ throw new Error(`PdgEmitSink: ${errors.length} streamed CSV writer(s) hit an IO error ` +
284
+ `(disk-full / out-of-fds) during the emit — the persisted graph would ` +
285
+ `be truncated, so the run is failed rather than COPYing a partial CSV: ${first instanceof Error ? first.message : String(first)}`);
286
+ }
287
+ return { nodeFiles, relsByPair };
288
+ }
289
+ /**
290
+ * Best-effort fd release for the error path — when a language pass throws
291
+ * before {@link finalize} runs, the caller's `finally` calls this so the
292
+ * BasicBlock + per-pair fds never leak. Idempotent with finalize via the
293
+ * `finalized` flag; close errors are swallowed because the run is already
294
+ * failing.
295
+ */
296
+ close() {
297
+ if (this.finalized)
298
+ return;
299
+ this.finalized = true;
300
+ try {
301
+ this.bbWriter?.close();
302
+ }
303
+ catch {
304
+ /* best-effort */
305
+ }
306
+ for (const writer of this.relWriters.values()) {
307
+ try {
308
+ writer.close();
309
+ }
310
+ catch {
311
+ /* best-effort */
312
+ }
313
+ }
314
+ }
315
+ // ── delegated reads / non-PDG mutations ─────────────────────────────────────
316
+ // The PDG emit functions never call these on the routed graph, but the
317
+ // façade implements the full KnowledgeGraph surface so it is a drop-in for
318
+ // the emit target and any non-PDG write transparently reaches the real graph.
319
+ get nodes() {
320
+ return this.real.nodes;
321
+ }
322
+ get relationships() {
323
+ return this.real.relationships;
324
+ }
325
+ iterNodes() {
326
+ return this.real.iterNodes();
327
+ }
328
+ iterRelationships() {
329
+ return this.real.iterRelationships();
330
+ }
331
+ iterRelationshipsByType(type) {
332
+ return this.real.iterRelationshipsByType(type);
333
+ }
334
+ forEachNode(fn) {
335
+ this.real.forEachNode(fn);
336
+ }
337
+ forEachRelationship(fn) {
338
+ this.real.forEachRelationship(fn);
339
+ }
340
+ getNode(id) {
341
+ return this.real.getNode(id);
342
+ }
343
+ get nodeCount() {
344
+ return this.real.nodeCount;
345
+ }
346
+ get relationshipCount() {
347
+ return this.real.relationshipCount;
348
+ }
349
+ removeNode(nodeId) {
350
+ return this.real.removeNode(nodeId);
351
+ }
352
+ removeNodesByFile(filePath) {
353
+ return this.real.removeNodesByFile(filePath);
354
+ }
355
+ removeRelationship(relationshipId) {
356
+ return this.real.removeRelationship(relationshipId);
357
+ }
358
+ }
@@ -37,6 +37,41 @@ export { realStdoutWrite, realStderrWrite, setActiveStdoutWrite } from '../../mc
37
37
  * Call this during long-running operations to prevent the connection from being closed.
38
38
  */
39
39
  export declare const touchRepo: (repoId: string) => void;
40
+ /**
41
+ * Acquire one eviction-exemption lease on a repo (LRU + idle timeout) by
42
+ * incrementing its reference count. The repoId must match the key passed to
43
+ * initLbug (e.g. group sync leases by handle.id — the same id it inits with).
44
+ * Leasing a repoId before it enters the pool is allowed and protects the entry
45
+ * once it is created, but the lease does NOT survive a teardown: closeOne
46
+ * force-clears the count, so a later re-init of the same repoId starts
47
+ * unpinned. Each pinRepo MUST be balanced by exactly one release (the repo
48
+ * stays exempt until the last lease is released). See the pinnedRepos docstring
49
+ * for the full contract.
50
+ *
51
+ * Returns a `release` disposer (mirroring addPoolCloseListener) that releases
52
+ * THIS lease exactly once — calling it twice is a no-op, so it can never
53
+ * over-decrement a sibling holder's count. Prefer the disposer
54
+ * (`const release = pinRepo(id); try { … } finally { release(); }`) so the
55
+ * pin/release pair is leak-proof; unpinRepo remains available for callers that
56
+ * pair explicitly.
57
+ */
58
+ export declare const pinRepo: (repoId: string) => (() => void);
59
+ /**
60
+ * Release one eviction-exemption lease on a repo. The repo becomes eligible for
61
+ * automatic eviction again only once its count reaches 0 (the key is deleted).
62
+ * Idempotent at the floor: releasing a repo with no active lease is a no-op (no
63
+ * negative counts). Does NOT close the repo's pool.
64
+ */
65
+ export declare const unpinRepo: (repoId: string) => void;
66
+ /**
67
+ * Maximum number of repos a bounded multi-repo operation (e.g. group sync's
68
+ * windowed manifest resolution) should hold resident at once. Equals
69
+ * MAX_POOL_SIZE today, but exposed under an intent-named accessor so callers
70
+ * size their working set against "max repos a bounded op should hold" rather
71
+ * than coupling to the LRU eviction-cap constant, which may be tuned
72
+ * independently.
73
+ */
74
+ export declare const getMaxResidentRepos: () => number;
40
75
  /**
41
76
  * Silence stdout by replacing process.stdout.write with a no-op.
42
77
  * Uses a reference counter so nested silence/restore pairs are safe.
@@ -39,6 +39,30 @@ const MAX_POOL_SIZE = 5;
39
39
  const IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
40
40
  /** Max connections per repo (caps concurrent queries per repo) */
41
41
  const MAX_CONNS_PER_REPO = 8;
42
+ /**
43
+ * Repos exempt from AUTOMATIC eviction (LRU + idle timeout) until explicitly
44
+ * unpinned. Used by bounded multi-repo operations like `group sync`, which
45
+ * initializes one pool per repo and then resolves cross-repo manifest/workspace
46
+ * links against ALL of those pools after the init loop. Without pinning, a
47
+ * group larger than MAX_POOL_SIZE would LRU-evict the earliest repos before
48
+ * resolution runs, leaving the deferred executor closures pointing at dead pool
49
+ * entries (issue #2189).
50
+ *
51
+ * Pins are REFERENCE-COUNTED: the map holds repoId → active lease count. This
52
+ * lets overlapping holders (two windows of one sync, or two concurrent
53
+ * `group sync` calls sharing a repo) coexist safely — the repo stays exempt
54
+ * until the LAST holder releases. A boolean Set could not represent "two
55
+ * holders," so the first release would wrongly clear a pin another holder still
56
+ * needs (PR #2191 review, Finding 1).
57
+ *
58
+ * Pins block only automatic eviction (LRU + idle). Explicit teardown
59
+ * (closeOne / closeLbug) always closes the entry and force-clears its count —
60
+ * teardown is authoritative. A present key always means count ≥ 1. While every
61
+ * pooled repo is pinned, evictLRU finds no eligible victim and the pool may
62
+ * transiently exceed MAX_POOL_SIZE — the same soft-cap behavior that already
63
+ * occurs when every entry is checked out.
64
+ */
65
+ const pinnedRepos = new Map();
42
66
  // Behavior-neutral RSS tracing for the FTS evict→reload memory repro
43
67
  // (gitnexus/scripts/bench/fts-evict-reload-rss.mjs). Two invariants keep it safe
44
68
  // in the pool init/close hot path: it writes ONLY to stderr (stdout is the MCP
@@ -77,6 +101,8 @@ function ensureIdleTimer() {
77
101
  idleTimer = setInterval(() => {
78
102
  const now = Date.now();
79
103
  for (const [repoId, entry] of pool) {
104
+ if (pinnedRepos.has(repoId))
105
+ continue;
80
106
  if (now - entry.lastUsed > IDLE_TIMEOUT_MS && entry.checkedOut === 0) {
81
107
  closeOne(repoId);
82
108
  }
@@ -97,7 +123,64 @@ export const touchRepo = (repoId) => {
97
123
  }
98
124
  };
99
125
  /**
100
- * Evict the least-recently-used repo if pool is at capacity
126
+ * Acquire one eviction-exemption lease on a repo (LRU + idle timeout) by
127
+ * incrementing its reference count. The repoId must match the key passed to
128
+ * initLbug (e.g. group sync leases by handle.id — the same id it inits with).
129
+ * Leasing a repoId before it enters the pool is allowed and protects the entry
130
+ * once it is created, but the lease does NOT survive a teardown: closeOne
131
+ * force-clears the count, so a later re-init of the same repoId starts
132
+ * unpinned. Each pinRepo MUST be balanced by exactly one release (the repo
133
+ * stays exempt until the last lease is released). See the pinnedRepos docstring
134
+ * for the full contract.
135
+ *
136
+ * Returns a `release` disposer (mirroring addPoolCloseListener) that releases
137
+ * THIS lease exactly once — calling it twice is a no-op, so it can never
138
+ * over-decrement a sibling holder's count. Prefer the disposer
139
+ * (`const release = pinRepo(id); try { … } finally { release(); }`) so the
140
+ * pin/release pair is leak-proof; unpinRepo remains available for callers that
141
+ * pair explicitly.
142
+ */
143
+ export const pinRepo = (repoId) => {
144
+ pinnedRepos.set(repoId, (pinnedRepos.get(repoId) ?? 0) + 1);
145
+ let released = false;
146
+ return () => {
147
+ if (released)
148
+ return;
149
+ released = true;
150
+ unpinRepo(repoId);
151
+ };
152
+ };
153
+ /**
154
+ * Release one eviction-exemption lease on a repo. The repo becomes eligible for
155
+ * automatic eviction again only once its count reaches 0 (the key is deleted).
156
+ * Idempotent at the floor: releasing a repo with no active lease is a no-op (no
157
+ * negative counts). Does NOT close the repo's pool.
158
+ */
159
+ export const unpinRepo = (repoId) => {
160
+ const count = pinnedRepos.get(repoId);
161
+ if (count === undefined)
162
+ return;
163
+ if (count <= 1) {
164
+ pinnedRepos.delete(repoId);
165
+ }
166
+ else {
167
+ pinnedRepos.set(repoId, count - 1);
168
+ }
169
+ };
170
+ /**
171
+ * Maximum number of repos a bounded multi-repo operation (e.g. group sync's
172
+ * windowed manifest resolution) should hold resident at once. Equals
173
+ * MAX_POOL_SIZE today, but exposed under an intent-named accessor so callers
174
+ * size their working set against "max repos a bounded op should hold" rather
175
+ * than coupling to the LRU eviction-cap constant, which may be tuned
176
+ * independently.
177
+ */
178
+ export const getMaxResidentRepos = () => MAX_POOL_SIZE;
179
+ /**
180
+ * Evict the least-recently-used repo if pool is at capacity.
181
+ * Pinned repos are never chosen as the eviction victim — when every eligible
182
+ * entry is pinned, no eviction occurs and the pool transiently exceeds
183
+ * MAX_POOL_SIZE (see the pinnedRepos docstring).
101
184
  */
102
185
  function evictLRU() {
103
186
  if (pool.size < MAX_POOL_SIZE)
@@ -105,6 +188,8 @@ function evictLRU() {
105
188
  let oldestId = null;
106
189
  let oldestTime = Infinity;
107
190
  for (const [id, entry] of pool) {
191
+ if (pinnedRepos.has(id))
192
+ continue;
108
193
  if (entry.checkedOut === 0 && entry.lastUsed < oldestTime) {
109
194
  oldestTime = entry.lastUsed;
110
195
  oldestId = id;
@@ -167,6 +252,10 @@ function closeOne(repoId) {
167
252
  }
168
253
  }
169
254
  pool.delete(repoId);
255
+ // Clear any eviction pin — the entry is gone, so the pin is meaningless and
256
+ // would otherwise leak across operations in a long-lived process. Teardown
257
+ // is authoritative: an explicit close always wins over a pin.
258
+ pinnedRepos.delete(repoId);
170
259
  // Notify listeners AFTER the pool entry is gone so any cache-invalidation
171
260
  // they perform is consistent with `isLbugReady(repoId) === false`.
172
261
  for (const listener of poolCloseListeners) {