gitnexus 1.6.11-rc.8 → 1.6.11

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 (531) hide show
  1. package/README.md +95 -26
  2. package/dist/_shared/graph/types.d.ts +81 -2
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -1
  5. package/dist/_shared/integrations/circuit-breaker.js.map +1 -1
  6. package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -1
  7. package/dist/_shared/integrations/resilient-fetch.js.map +1 -1
  8. package/dist/_shared/language-detection.d.ts.map +1 -1
  9. package/dist/_shared/language-detection.js +2 -0
  10. package/dist/_shared/language-detection.js.map +1 -1
  11. package/dist/_shared/languages.d.ts +2 -1
  12. package/dist/_shared/languages.d.ts.map +1 -1
  13. package/dist/_shared/languages.js +1 -0
  14. package/dist/_shared/languages.js.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.d.ts +2 -2
  16. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  17. package/dist/_shared/lbug/schema-constants.js +4 -0
  18. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  19. package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -1
  20. package/dist/_shared/scope-resolution/finalize-algorithm.js +1 -0
  21. package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
  22. package/dist/_shared/scope-resolution/language-classification.d.ts +3 -0
  23. package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/language-classification.js +4 -0
  25. package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
  26. package/dist/_shared/scope-resolution/reference-site.d.ts +10 -0
  27. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
  28. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -1
  29. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -1
  30. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  31. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  32. package/dist/_shared/scope-resolution/types.d.ts +15 -0
  33. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  34. package/dist/cli/ai-context.d.ts +4 -0
  35. package/dist/cli/ai-context.js +90 -14
  36. package/dist/cli/analyze-config.js +14 -0
  37. package/dist/cli/analyze-options.d.ts +14 -0
  38. package/dist/cli/analyze-watch.d.ts +20 -0
  39. package/dist/cli/analyze-watch.js +434 -0
  40. package/dist/cli/analyze.js +27 -9
  41. package/dist/cli/auto-sync.d.ts +1 -0
  42. package/dist/cli/auto-sync.js +107 -0
  43. package/dist/cli/command-banner.d.ts +15 -0
  44. package/dist/cli/command-banner.js +42 -0
  45. package/dist/cli/detect-changes-format.js +21 -0
  46. package/dist/cli/doctor.d.ts +7 -0
  47. package/dist/cli/doctor.js +14 -0
  48. package/dist/cli/group.js +7 -1
  49. package/dist/cli/help-i18n.js +4 -0
  50. package/dist/cli/i18n/en.d.ts +18 -3
  51. package/dist/cli/i18n/en.js +18 -3
  52. package/dist/cli/i18n/resources.d.ts +33 -3
  53. package/dist/cli/i18n/zh-CN.d.ts +15 -0
  54. package/dist/cli/i18n/zh-CN.js +18 -3
  55. package/dist/cli/index.js +34 -7
  56. package/dist/cli/mcp.d.ts +15 -0
  57. package/dist/cli/mcp.js +44 -0
  58. package/dist/cli/optional-grammars.d.ts +3 -2
  59. package/dist/cli/optional-grammars.js +19 -3
  60. package/dist/cli/setup.js +41 -15
  61. package/dist/cli/update-notice.d.ts +34 -0
  62. package/dist/cli/update-notice.js +91 -0
  63. package/dist/cli/update.d.ts +21 -0
  64. package/dist/cli/update.js +93 -0
  65. package/dist/cli/watch.d.ts +1 -20
  66. package/dist/cli/watch.js +5 -371
  67. package/dist/core/analysis-feature-registry.d.ts +2 -0
  68. package/dist/core/analysis-feature-registry.js +15 -0
  69. package/dist/core/analyzer-identity.js +21 -3
  70. package/dist/core/auto-sync/analysis-worker-launch.d.ts +30 -0
  71. package/dist/core/auto-sync/analysis-worker-launch.js +166 -0
  72. package/dist/core/auto-sync/config.d.ts +33 -0
  73. package/dist/core/auto-sync/config.js +320 -0
  74. package/dist/core/auto-sync/index.d.ts +6 -0
  75. package/dist/core/auto-sync/index.js +6 -0
  76. package/dist/core/auto-sync/path-security.d.ts +9 -0
  77. package/dist/core/auto-sync/path-security.js +251 -0
  78. package/dist/core/auto-sync/repo.d.ts +1 -0
  79. package/dist/core/auto-sync/repo.js +6 -0
  80. package/dist/core/auto-sync/runner.d.ts +44 -0
  81. package/dist/core/auto-sync/runner.js +428 -0
  82. package/dist/core/auto-sync/starter.d.ts +50 -0
  83. package/dist/core/auto-sync/starter.js +497 -0
  84. package/dist/core/auto-sync/state.d.ts +37 -0
  85. package/dist/core/auto-sync/state.js +119 -0
  86. package/dist/core/group/config-parser.js +8 -1
  87. package/dist/core/group/cross-impact.d.ts +3 -0
  88. package/dist/core/group/cross-impact.js +36 -4
  89. package/dist/core/group/extractors/http-patterns/java.js +22 -8
  90. package/dist/core/group/extractors/http-patterns/kotlin.js +86 -31
  91. package/dist/core/group/extractors/http-patterns/node.js +145 -8
  92. package/dist/core/group/extractors/http-patterns/python.js +11 -7
  93. package/dist/core/group/extractors/http-route-extractor.js +63 -17
  94. package/dist/core/group/extractors/java-workspace-extractor.js +244 -31
  95. package/dist/core/group/group-lock.js +5 -6
  96. package/dist/core/group/normalization.d.ts +38 -1
  97. package/dist/core/group/normalization.js +59 -3
  98. package/dist/core/group/service.d.ts +11 -0
  99. package/dist/core/group/service.js +13 -1
  100. package/dist/core/group/storage.js +2 -2
  101. package/dist/core/group/sync.d.ts +24 -1
  102. package/dist/core/group/sync.js +46 -8
  103. package/dist/core/group/types.d.ts +13 -0
  104. package/dist/core/incremental/derived-writeback.d.ts +36 -0
  105. package/dist/core/incremental/derived-writeback.js +68 -0
  106. package/dist/core/incremental/spring-config-drift.d.ts +12 -0
  107. package/dist/core/incremental/spring-config-drift.js +46 -0
  108. package/dist/core/incremental/subgraph-extract.d.ts +7 -4
  109. package/dist/core/incremental/subgraph-extract.js +30 -5
  110. package/dist/core/ingestion/asyncapi/document.d.ts +226 -0
  111. package/dist/core/ingestion/asyncapi/document.js +758 -0
  112. package/dist/core/ingestion/asyncapi/protocol.d.ts +79 -0
  113. package/dist/core/ingestion/asyncapi/protocol.js +203 -0
  114. package/dist/core/ingestion/call-extractors/configs/zig.d.ts +2 -0
  115. package/dist/core/ingestion/call-extractors/configs/zig.js +4 -0
  116. package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +100 -0
  117. package/dist/core/ingestion/call-extractors/zig-static-gating.js +319 -0
  118. package/dist/core/ingestion/call-processor.js +3 -1
  119. package/dist/core/ingestion/class-extractors/configs/zig.d.ts +2 -0
  120. package/dist/core/ingestion/class-extractors/configs/zig.js +32 -0
  121. package/dist/core/ingestion/class-extractors/generic.js +4 -1
  122. package/dist/core/ingestion/class-types.d.ts +6 -2
  123. package/dist/core/ingestion/destination-key.d.ts +59 -0
  124. package/dist/core/ingestion/destination-key.js +61 -0
  125. package/dist/core/ingestion/entry-point-scoring.d.ts +6 -2
  126. package/dist/core/ingestion/entry-point-scoring.js +8 -44
  127. package/dist/core/ingestion/export-detection.d.ts +37 -0
  128. package/dist/core/ingestion/export-detection.js +60 -0
  129. package/dist/core/ingestion/field-extractors/configs/zig.d.ts +8 -0
  130. package/dist/core/ingestion/field-extractors/configs/zig.js +49 -0
  131. package/dist/core/ingestion/field-extractors/generic.d.ts +1 -1
  132. package/dist/core/ingestion/field-extractors/generic.js +12 -1
  133. package/dist/core/ingestion/frameworks/spring/actuator-runtime.d.ts +26 -0
  134. package/dist/core/ingestion/frameworks/spring/actuator-runtime.js +843 -0
  135. package/dist/core/ingestion/frameworks/spring/analysis-features.d.ts +5 -0
  136. package/dist/core/ingestion/frameworks/spring/analysis-features.js +9 -0
  137. package/dist/core/ingestion/frameworks/spring/annotation-arguments.js +8 -1
  138. package/dist/core/ingestion/frameworks/spring/argument-facts.d.ts +80 -0
  139. package/dist/core/ingestion/frameworks/spring/argument-facts.js +85 -0
  140. package/dist/core/ingestion/frameworks/spring/config-bindings.d.ts +1 -0
  141. package/dist/core/ingestion/frameworks/spring/config-bindings.js +2 -1
  142. package/dist/core/ingestion/frameworks/spring/destinations.d.ts +539 -0
  143. package/dist/core/ingestion/frameworks/spring/destinations.js +840 -0
  144. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.d.ts +22 -0
  145. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.js +124 -0
  146. package/dist/core/ingestion/frameworks/spring/message-producers.d.ts +78 -0
  147. package/dist/core/ingestion/frameworks/spring/message-producers.js +90 -0
  148. package/dist/core/ingestion/frameworks/spring/non-http-handlers.d.ts +29 -0
  149. package/dist/core/ingestion/frameworks/spring/vendor-prefixes.d.ts +7 -0
  150. package/dist/core/ingestion/frameworks/spring/vendor-prefixes.js +22 -0
  151. package/dist/core/ingestion/import-resolvers/configs/zig.d.ts +13 -0
  152. package/dist/core/ingestion/import-resolvers/configs/zig.js +26 -0
  153. package/dist/core/ingestion/import-resolvers/zig.d.ts +34 -0
  154. package/dist/core/ingestion/import-resolvers/zig.js +193 -0
  155. package/dist/core/ingestion/language-config.d.ts +177 -0
  156. package/dist/core/ingestion/language-config.js +719 -0
  157. package/dist/core/ingestion/language-provider.d.ts +178 -1
  158. package/dist/core/ingestion/language-provider.js +22 -0
  159. package/dist/core/ingestion/languages/csharp/razor-view-components.d.ts +62 -0
  160. package/dist/core/ingestion/languages/csharp/razor-view-components.js +954 -0
  161. package/dist/core/ingestion/languages/csharp/resolution-config.d.ts +3 -0
  162. package/dist/core/ingestion/languages/csharp/resolution-config.js +6 -1
  163. package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -0
  164. package/dist/core/ingestion/languages/index.d.ts +1 -0
  165. package/dist/core/ingestion/languages/index.js +2 -0
  166. package/dist/core/ingestion/languages/java/analysis-features.d.ts +1 -1
  167. package/dist/core/ingestion/languages/java/analysis-features.js +14 -6
  168. package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +8 -0
  169. package/dist/core/ingestion/languages/java/capture-side-channel.js +34 -0
  170. package/dist/core/ingestion/languages/java/captures.js +22 -1
  171. package/dist/core/ingestion/languages/java/lombok-synthesizer.d.ts +42 -0
  172. package/dist/core/ingestion/languages/java/lombok-synthesizer.js +439 -0
  173. package/dist/core/ingestion/languages/java/scope-resolver.js +2 -0
  174. package/dist/core/ingestion/languages/java/spring-actuator.d.ts +2 -0
  175. package/dist/core/ingestion/languages/java/spring-actuator.js +62 -0
  176. package/dist/core/ingestion/languages/java/spring-di.d.ts +17 -1
  177. package/dist/core/ingestion/languages/java/spring-di.js +44 -2
  178. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.d.ts +8 -0
  179. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.js +62 -0
  180. package/dist/core/ingestion/languages/java/spring-message-producers.d.ts +20 -0
  181. package/dist/core/ingestion/languages/java/spring-message-producers.js +83 -0
  182. package/dist/core/ingestion/languages/java/spring-non-http-handlers.d.ts +11 -1
  183. package/dist/core/ingestion/languages/java/spring-non-http-handlers.js +13 -3
  184. package/dist/core/ingestion/languages/java.js +18 -8
  185. package/dist/core/ingestion/languages/jvm/accessor-synthesis.d.ts +99 -0
  186. package/dist/core/ingestion/languages/jvm/accessor-synthesis.js +173 -0
  187. package/dist/core/ingestion/languages/jvm/beanspec.d.ts +17 -0
  188. package/dist/core/ingestion/languages/jvm/beanspec.js +42 -0
  189. package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +15 -0
  190. package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +50 -0
  191. package/dist/core/ingestion/languages/kotlin/captures.js +24 -1
  192. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.d.ts +26 -0
  193. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.js +417 -0
  194. package/dist/core/ingestion/languages/kotlin/scope-resolver.js +4 -0
  195. package/dist/core/ingestion/languages/kotlin/spring-actuator.d.ts +4 -0
  196. package/dist/core/ingestion/languages/kotlin/spring-actuator.js +172 -0
  197. package/dist/core/ingestion/languages/kotlin/spring-config-bindings.d.ts +18 -0
  198. package/dist/core/ingestion/languages/kotlin/spring-config-bindings.js +411 -0
  199. package/dist/core/ingestion/languages/kotlin/spring-di.d.ts +51 -1
  200. package/dist/core/ingestion/languages/kotlin/spring-di.js +86 -6
  201. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.d.ts +8 -0
  202. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.js +77 -0
  203. package/dist/core/ingestion/languages/kotlin/spring-message-producers.d.ts +17 -0
  204. package/dist/core/ingestion/languages/kotlin/spring-message-producers.js +119 -0
  205. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.d.ts +16 -0
  206. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.js +77 -2
  207. package/dist/core/ingestion/languages/kotlin.js +24 -0
  208. package/dist/core/ingestion/languages/python.js +2 -0
  209. package/dist/core/ingestion/languages/rust/query.js +8 -8
  210. package/dist/core/ingestion/languages/zig/captures.d.ts +260 -0
  211. package/dist/core/ingestion/languages/zig/captures.js +2070 -0
  212. package/dist/core/ingestion/languages/zig/index.d.ts +4 -0
  213. package/dist/core/ingestion/languages/zig/index.js +4 -0
  214. package/dist/core/ingestion/languages/zig/interpret.d.ts +36 -0
  215. package/dist/core/ingestion/languages/zig/interpret.js +176 -0
  216. package/dist/core/ingestion/languages/zig/query.d.ts +3 -0
  217. package/dist/core/ingestion/languages/zig/query.js +426 -0
  218. package/dist/core/ingestion/languages/zig/range-binding.d.ts +37 -0
  219. package/dist/core/ingestion/languages/zig/range-binding.js +373 -0
  220. package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +12 -0
  221. package/dist/core/ingestion/languages/zig/scope-resolver.js +58 -0
  222. package/dist/core/ingestion/languages/zig/simple-hooks.d.ts +25 -0
  223. package/dist/core/ingestion/languages/zig/simple-hooks.js +143 -0
  224. package/dist/core/ingestion/languages/zig.d.ts +32 -0
  225. package/dist/core/ingestion/languages/zig.js +130 -0
  226. package/dist/core/ingestion/method-extractors/configs/zig.d.ts +2 -0
  227. package/dist/core/ingestion/method-extractors/configs/zig.js +120 -0
  228. package/dist/core/ingestion/method-extractors/generic.js +16 -4
  229. package/dist/core/ingestion/method-types.d.ts +10 -4
  230. package/dist/core/ingestion/model/field-registry.d.ts +3 -3
  231. package/dist/core/ingestion/model/field-registry.js +11 -7
  232. package/dist/core/ingestion/model/method-registry.d.ts +3 -3
  233. package/dist/core/ingestion/model/method-registry.js +13 -8
  234. package/dist/core/ingestion/model/registration-table.js +3 -0
  235. package/dist/core/ingestion/model/type-registry.js +8 -4
  236. package/dist/core/ingestion/pipeline-phases/di.js +47 -14
  237. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  238. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  239. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +7 -1
  240. package/dist/core/ingestion/pipeline-phases/parse-impl.js +79 -91
  241. package/dist/core/ingestion/pipeline-phases/parse.d.ts +17 -0
  242. package/dist/core/ingestion/pipeline-phases/processes.d.ts +2 -1
  243. package/dist/core/ingestion/pipeline-phases/processes.js +39 -4
  244. package/dist/core/ingestion/pipeline-phases/routes.js +27 -18
  245. package/dist/core/ingestion/pipeline-phases/runner.d.ts +4 -1
  246. package/dist/core/ingestion/pipeline-phases/runner.js +34 -14
  247. package/dist/core/ingestion/pipeline-phases/scan.js +82 -1
  248. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.d.ts +2 -0
  249. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.js +8 -0
  250. package/dist/core/ingestion/pipeline-phases/spring-destinations.d.ts +123 -0
  251. package/dist/core/ingestion/pipeline-phases/spring-destinations.js +688 -0
  252. package/dist/core/ingestion/pipeline.d.ts +30 -1
  253. package/dist/core/ingestion/pipeline.js +52 -14
  254. package/dist/core/ingestion/resolve-references.js +5 -0
  255. package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +16 -0
  256. package/dist/core/ingestion/route-extractors/constant-resolver.js +10 -0
  257. package/dist/core/ingestion/route-extractors/java-const-resolver.d.ts +27 -1
  258. package/dist/core/ingestion/route-extractors/java-const-resolver.js +161 -13
  259. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.d.ts +12 -18
  260. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.js +214 -33
  261. package/dist/core/ingestion/route-extractors/kotlin-spring.d.ts +13 -0
  262. package/dist/core/ingestion/route-extractors/kotlin-spring.js +259 -0
  263. package/dist/core/ingestion/route-extractors/python-decorator-handler.d.ts +6 -0
  264. package/dist/core/ingestion/route-extractors/python-decorator-handler.js +16 -0
  265. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +30 -1
  266. package/dist/core/ingestion/route-extractors/spring-shared.js +77 -9
  267. package/dist/core/ingestion/route-extractors/spring.js +9 -5
  268. package/dist/core/ingestion/scope-extractor.js +8 -0
  269. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +62 -0
  270. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +4 -0
  271. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +2 -0
  272. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +28 -0
  273. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +11 -2
  274. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +23 -2
  275. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +24 -3
  276. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.d.ts +11 -1
  277. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.js +26 -6
  278. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +11 -0
  279. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +29 -3
  280. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +8 -1
  281. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +35 -9
  282. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +1 -4
  283. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +277 -9
  284. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
  285. package/dist/core/ingestion/scope-resolution/pipeline/run.js +7 -3
  286. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +43 -9
  287. package/dist/core/ingestion/scope-resolution/scope/walkers.js +71 -10
  288. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -0
  289. package/dist/core/ingestion/tree-sitter-queries.js +161 -0
  290. package/dist/core/ingestion/type-extractors/zig.d.ts +2 -0
  291. package/dist/core/ingestion/type-extractors/zig.js +17 -0
  292. package/dist/core/ingestion/utils/ast-helpers.d.ts +40 -1
  293. package/dist/core/ingestion/utils/ast-helpers.js +124 -6
  294. package/dist/core/ingestion/utils/callable-flow-captures.d.ts +9 -0
  295. package/dist/core/ingestion/utils/callable-flow-captures.js +75 -14
  296. package/dist/core/ingestion/utils/test-file-path.d.ts +28 -0
  297. package/dist/core/ingestion/utils/test-file-path.js +87 -0
  298. package/dist/core/ingestion/variable-extractors/configs/zig.d.ts +2 -0
  299. package/dist/core/ingestion/variable-extractors/configs/zig.js +71 -0
  300. package/dist/core/ingestion/workers/parse-worker.js +85 -14
  301. package/dist/core/install-context.d.ts +4 -0
  302. package/dist/core/install-context.js +152 -0
  303. package/dist/core/lbug/csv-generator.d.ts +10 -1
  304. package/dist/core/lbug/csv-generator.js +92 -22
  305. package/dist/core/lbug/graph-emit-sink.d.ts +3 -2
  306. package/dist/core/lbug/graph-emit-sink.js +4 -2
  307. package/dist/core/lbug/lbug-adapter.d.ts +100 -5
  308. package/dist/core/lbug/lbug-adapter.js +236 -24
  309. package/dist/core/lbug/schema.d.ts +6 -5
  310. package/dist/core/lbug/schema.js +84 -22
  311. package/dist/core/net/url-guard.d.ts +6 -0
  312. package/dist/core/net/url-guard.js +147 -0
  313. package/dist/core/package-version.d.ts +5 -0
  314. package/dist/core/package-version.js +10 -0
  315. package/dist/core/run-analyze.d.ts +21 -4
  316. package/dist/core/run-analyze.js +426 -117
  317. package/dist/core/search/fts-indexes.d.ts +19 -1
  318. package/dist/core/search/fts-indexes.js +28 -1
  319. package/dist/core/tree-sitter/parser-loader.js +16 -0
  320. package/dist/core/update-cache.d.ts +39 -0
  321. package/dist/core/update-cache.js +141 -0
  322. package/dist/core/update-check.d.ts +31 -0
  323. package/dist/core/update-check.js +274 -0
  324. package/dist/mcp/local/local-backend.d.ts +27 -13
  325. package/dist/mcp/local/local-backend.js +148 -60
  326. package/dist/mcp/repository-policy.d.ts +5 -1
  327. package/dist/mcp/repository-policy.js +48 -4
  328. package/dist/mcp/resources.js +10 -1
  329. package/dist/mcp/server.js +8 -9
  330. package/dist/mcp/tools.js +32 -22
  331. package/dist/server/analyze-launch.d.ts +2 -0
  332. package/dist/server/analyze-launch.js +2 -0
  333. package/dist/server/analyze-worker-protocol.d.ts +6 -1
  334. package/dist/server/analyze-worker.d.ts +1 -0
  335. package/dist/server/analyze-worker.js +38 -20
  336. package/dist/server/api.d.ts +5 -1
  337. package/dist/server/api.js +105 -88
  338. package/dist/server/git-clone.d.ts +40 -16
  339. package/dist/server/git-clone.js +253 -199
  340. package/dist/server/grep-params.d.ts +18 -0
  341. package/dist/server/grep-params.js +83 -0
  342. package/dist/server/grep-scan.d.ts +23 -0
  343. package/dist/server/grep-scan.js +107 -0
  344. package/dist/server/grep-worker.d.ts +1 -0
  345. package/dist/server/grep-worker.js +12 -0
  346. package/dist/server/mcp-http.d.ts +8 -0
  347. package/dist/server/mcp-http.js +16 -1
  348. package/dist/server/update-controller.d.ts +29 -0
  349. package/dist/server/update-controller.js +82 -0
  350. package/dist/storage/file-lock.d.ts +15 -0
  351. package/dist/storage/file-lock.js +155 -0
  352. package/dist/storage/fs-atomic.d.ts +24 -0
  353. package/dist/storage/fs-atomic.js +86 -2
  354. package/dist/storage/global-dir.d.ts +2 -0
  355. package/dist/storage/global-dir.js +6 -0
  356. package/dist/storage/parse-cache.d.ts +45 -7
  357. package/dist/storage/parse-cache.js +189 -42
  358. package/dist/storage/parsedfile-store.d.ts +41 -60
  359. package/dist/storage/parsedfile-store.js +247 -171
  360. package/dist/storage/repo-manager.d.ts +10 -4
  361. package/dist/storage/repo-manager.js +35 -25
  362. package/dist/storage/repo-meta.d.ts +34 -0
  363. package/dist/storage/v8-sidecar.d.ts +48 -0
  364. package/dist/storage/v8-sidecar.js +347 -0
  365. package/dist/types/pipeline.d.ts +14 -0
  366. package/dist/utils/process-identity.d.ts +2 -0
  367. package/dist/utils/process-identity.js +35 -0
  368. package/package.json +10 -4
  369. package/scripts/assert-web-assets.mjs +35 -0
  370. package/scripts/build-web.js +72 -0
  371. package/scripts/build.js +31 -28
  372. package/scripts/cross-platform-tests.ts +5 -0
  373. package/skills/gitnexus-cli.md +5 -1
  374. package/web/assets/ProcessFlowModal-DAeX2BHW.js +24 -0
  375. package/web/assets/{abnfDiagram-QNN2D4NM-Cq2djWWe.js → abnfDiagram-PAR5IXJI-J3OdeHLl.js} +1 -1
  376. package/web/assets/{agent-BWAXPjC5.js → agent-BMRqXorA.js} +6 -4
  377. package/web/assets/architecture-7GRP2DOG-M7G3SWCO-G3sWyX6M.js +1 -0
  378. package/web/assets/architectureDiagram-2PNWDENW-ysKcRlGT.js +36 -0
  379. package/web/assets/blockDiagram-PUWGUBOA-oLXTa1lY.js +129 -0
  380. package/web/assets/c4Diagram-3NLNHZP5-D3iu4eC0.js +38 -0
  381. package/web/assets/{chunk-2SREHG4O-Dip42xOq.js → chunk-2NMSUPJH-B24w-wng.js} +1 -1
  382. package/web/assets/{chunk-VJFVEZTB-CX66F2ep.js → chunk-32MCUW2P-ByUyt0Sa.js} +3 -3
  383. package/web/assets/{chunk-4RFN2BYJ-DHHWuC-n.js → chunk-3NPNU5GP-DocZ07Sg.js} +1 -1
  384. package/web/assets/{chunk-XNMVGMAZ-Cg_LnPtj.js → chunk-3SMGFRGC-CAu4JRLv.js} +1 -1
  385. package/web/assets/{chunk-W44A43WB-BN8EQc_u.js → chunk-4CAJECRU-DRYMwG9z.js} +1 -1
  386. package/web/assets/chunk-4OADUZ5Q-CxrSvOwZ.js +10 -0
  387. package/web/assets/{chunk-DMV4VAQV-Bn6WXDSX.js → chunk-5IQCK5RY-CCMhl4so.js} +2 -2
  388. package/web/assets/{chunk-RRFMTAIC-CMN0wcYA.js → chunk-5MVFYWFU-Db9KvrYO.js} +1 -1
  389. package/web/assets/chunk-CLS4B6BI-Uvx5QNsu.js +168 -0
  390. package/web/assets/chunk-DZP67EKU-B3Mt380j.js +70 -0
  391. package/web/assets/{chunk-6L755F7B-CMhLBxi2.js → chunk-EICP7K5X-CqwE9hui.js} +1 -1
  392. package/web/assets/{chunk-BI6VK774-D0oaWk8V.js → chunk-GDY2UAB4-CtQQR0a8.js} +1 -1
  393. package/web/assets/chunk-H7VHZCWX-CE1nAmt6.js +1 -0
  394. package/web/assets/chunk-IJBDOHL6-BUKtY6oF.js +1 -0
  395. package/web/assets/{chunk-OIYT25JQ-CQ6KOZGX.js → chunk-JFFVMJIA-0fJenqia.js} +1 -1
  396. package/web/assets/{chunk-HNC4WDU7-DoU3KnA7.js → chunk-JZGDI5JK-BeTMSSkJ.js} +1 -1
  397. package/web/assets/{chunk-EQFTRU2I-1ix7TzW9.js → chunk-KD6SPIPN-Qp1Pflah.js} +1 -1
  398. package/web/assets/chunk-NF366YI6-CpSpya3n.js +231 -0
  399. package/web/assets/chunk-NUDPKBEF-CU1-BQIW.js +34 -0
  400. package/web/assets/{chunk-SATU7PGQ-B1zhgOvc.js → chunk-OJINMHTS-zOCQe4eI.js} +1 -1
  401. package/web/assets/{chunk-E5QJAATJ-dh-5CtIV.js → chunk-Q6NGPIAH-yyBomb63.js} +1 -1
  402. package/web/assets/chunk-QJSWEUOL-D_rf9gPD.js +128 -0
  403. package/web/assets/chunk-R7JDMUKV-CRoqKpD6.js +1 -0
  404. package/web/assets/{chunk-VE5CLXGZ-C1vB7wpY.js → chunk-RCXAM2GK-CUqrWQDV.js} +1 -1
  405. package/web/assets/{chunk-66DQ2XMT-CM0xjFAN.js → chunk-RT4MAWR3-zXy9-Lvt.js} +1 -1
  406. package/web/assets/{chunk-TZUXEDM2-B7p9AcTw.js → chunk-SFMINX4W-J2UrvMpD.js} +1 -1
  407. package/web/assets/{chunk-LORZLZWG-B57uGw2N.js → chunk-SLDQ463B-CHY6e_bQ.js} +2 -2
  408. package/web/assets/{chunk-FWYVLQTC-BgDItRzJ.js → chunk-TCEHX7R3-CXPDEVbm.js} +1 -1
  409. package/web/assets/{chunk-224SPVON-B8gxtXpy.js → chunk-VQC2EELJ-Dc8fIy4R.js} +1 -1
  410. package/web/assets/chunk-VRZKL3EH-DB3KixTQ.js +1 -0
  411. package/web/assets/{chunk-A7VWPJGB-B4O3oQdQ.js → chunk-VTHDQ7BV-b7FsPQZO.js} +1 -1
  412. package/web/assets/chunk-VY2OSTJR-Dkgc3Brg.js +2 -0
  413. package/web/assets/chunk-WUBWJARI-7FwILJ3k.js +2 -0
  414. package/web/assets/{chunk-D6VWDJW2-BxcBdZ9c.js → chunk-YBMQ5JGJ-BSnyWZmd.js} +1 -1
  415. package/web/assets/chunk-YDMLL4PJ-IHhMngTz.js +2 -0
  416. package/web/assets/chunk-Z6V44ARX-BjCEsF0Q.js +62 -0
  417. package/web/assets/chunk-ZJJ7VQJP-Dz089b-K.js +321 -0
  418. package/web/assets/chunk-ZNLK7HRR-B0iFpmW4.js +1 -0
  419. package/web/assets/chunk-ZQN7UHIL-Cy7KAjQt.js +1 -0
  420. package/web/assets/{chunk-L5GCOVLC-CE2w8Nm_.js → chunk-ZS6VBONO-DBjJO0w9.js} +2 -2
  421. package/web/assets/{chunk-NV3KIAZN-5yvcpMiu.js → chunk-ZUNWM646-DNRJyNPe.js} +1 -1
  422. package/web/assets/classDiagram-FGAMPOII-C5Ibyode.js +1 -0
  423. package/web/assets/classDiagram-v2-AI5RJEK4-C5Ibyode.js +1 -0
  424. package/web/assets/{cose-bilkent-RZA4XIPR-BHpK4Crp.js → cose-bilkent-Q72C2OJ3-DMeFXnK8.js} +1 -1
  425. package/web/assets/cynefin-OW5HDTMX-6OCTWOGG-vlqakYcO.js +1 -0
  426. package/web/assets/{cynefinDiagram-TR7LZUCG-C3Iar9lZ.js → cynefinDiagram-2OOWUY4M-BY6DZkTt.js} +1 -1
  427. package/web/assets/dagre-MPVFI544-DWO70fy_.js +4 -0
  428. package/web/assets/diagram-AE4NWXFW-f7JRVRUL.js +24 -0
  429. package/web/assets/{diagram-L7DVE3PN-GIRyVUtt.js → diagram-BUJID5UG-BXZv1rCS.js} +2 -2
  430. package/web/assets/{diagram-JCD7PQ7C-DC5-9FRr.js → diagram-C44CZ5NI-CRNfvjCq.js} +1 -1
  431. package/web/assets/diagram-EYOLEXXS-DZOjKQVE.js +30 -0
  432. package/web/assets/diagram-P7B6PWOJ-D3SQmw3_.js +3 -0
  433. package/web/assets/{ebnfDiagram-DRHRQJWO-D8_CI4ZF.js → ebnfDiagram-FTPZ3PV6-jKWpIDN2.js} +1 -1
  434. package/web/assets/erDiagram-H3ERR26V-BqJntEuJ.js +99 -0
  435. package/web/assets/eventmodeling-NTZA5JFV-WKSWQHFS-Bq4FqtBL.js +1 -0
  436. package/web/assets/flowDiagram-YHGXBVSY-Dnlk2bEu.js +1 -0
  437. package/web/assets/ganttDiagram-RWFTB7ED-Bl6Y1K5T.js +292 -0
  438. package/web/assets/gitGraph-4MIJSDKK-JH2HKYUO-B3yc4GkH.js +1 -0
  439. package/web/assets/gitGraphDiagram-MAR6GKOP-CkNvsrdw.js +106 -0
  440. package/web/assets/{index-CJDpyH-n.css → index-C2_aej58.css} +1 -1
  441. package/web/assets/{index-DDRie6_8.js → index-Dj3vQOHK.js} +51 -51
  442. package/web/assets/info-A6RAGUB7-3TX3OFWW-BM3N5_Dv.js +1 -0
  443. package/web/assets/infoDiagram-LV547AFH-COkFutAf.js +2 -0
  444. package/web/assets/{ishikawaDiagram-4JDSWWDM-DmqWtjkl.js → ishikawaDiagram-PUJQGNDS-Bb8FylSR.js} +2 -2
  445. package/web/assets/{journeyDiagram-QUS3FAKT-XAzVzrQW.js → journeyDiagram-EW5QJT4W-BzoHUBhM.js} +4 -4
  446. package/web/assets/{kanban-definition-D4L7SQKK-YurRnmYn.js → kanban-definition-JSQERAZB-Uy2Pi0yQ.js} +4 -4
  447. package/web/assets/katex-CZ4GXH2S-C0Fcou7U.js +257 -0
  448. package/web/assets/{mindmap-definition-66QP5TD6-ChrWWnil.js → mindmap-definition-VBJCLQLM-B-CnOnt_.js} +3 -3
  449. package/web/assets/packet-AYTQ26CC-MKII6P3Y-Bd52ZE3V.js +1 -0
  450. package/web/assets/{pegDiagram-YKIO5C6U-QGeQLEq2.js → pegDiagram-7WILOQTJ-D9sneKXg.js} +1 -1
  451. package/web/assets/pie-WAS4IAKB-6B4OPMJ5-DW5Xy69Z.js +1 -0
  452. package/web/assets/pieDiagram-EFA7CBSB-Dx2NQofL.js +39 -0
  453. package/web/assets/{quadrantDiagram-VO5QLCXA-B7098wSc.js → quadrantDiagram-WARVYA7P-DPKL9_0A.js} +3 -3
  454. package/web/assets/radar-RG4KPBEZ-UPTR3XUL-CkzibT2h.js +1 -0
  455. package/web/assets/railroad-74A4TZTK-MEFUPFMS-D9zF-mDN.js +1 -0
  456. package/web/assets/railroad-abnf-HS5TGJTU-R5YG2S5D-ruacjUbX.js +1 -0
  457. package/web/assets/railroad-ebnf-LZEXJU2U-G5QEX2L6-S1zc5OiO.js +1 -0
  458. package/web/assets/railroad-peg-WCYAUIDC-H5I2ZRY2-Cbcr0XzY.js +1 -0
  459. package/web/assets/{railroadDiagram-M77LTMSB-AXu4vxk_.js → railroadDiagram-5NIQVNLQ-DsweTo14.js} +1 -1
  460. package/web/assets/{requirementDiagram-RASO5HTO-B-l8InpK.js → requirementDiagram-SXREGOKZ-CXwdNSVR.js} +4 -4
  461. package/web/assets/{sankeyDiagram-F32LOJ6L-Kd7zjJgX.js → sankeyDiagram-VV74M2VP-CA6jtP9P.js} +3 -3
  462. package/web/assets/{sequenceDiagram-URATNSBD-BlcdY9DS.js → sequenceDiagram-52ZFIFDL-k_5rEbzm.js} +15 -15
  463. package/web/assets/src-Df5C1Nz4.js +1 -0
  464. package/web/assets/stateDiagram-3QAKPIVI-BejHTf31.js +1 -0
  465. package/web/assets/stateDiagram-v2-HN2P4RDZ-BrfoRuOc.js +1 -0
  466. package/web/assets/swimlanes-QLVP42QD-CQSVO5D6.js +1 -0
  467. package/web/assets/swimlanesDiagram-JKPDBFCN-DrWB9kcD.js +8 -0
  468. package/web/assets/{timeline-definition-WJXWRH4I-DUfp1R3E.js → timeline-definition-GS45XAXW-C0Ks8rPP.js} +4 -4
  469. package/web/assets/treeView-Q6P3EWNA-ZRJGANNQ-DmKOCo6L.js +1 -0
  470. package/web/assets/treemap-WGGIJYW6-K64VN5AS-DO3xY92U.js +1 -0
  471. package/web/assets/{vennDiagram-K3VCMQLI-Bj-FN-XS.js → vennDiagram-QIQU2ZRJ-rgPiS837.js} +3 -3
  472. package/web/assets/wardley-WFR3VGLG-D67OWCCH-DiDPBNft.js +1 -0
  473. package/web/assets/wardleyDiagram-U6YNM66N-DRVr7SPi.js +78 -0
  474. package/web/assets/{xychartDiagram-5VRUVLCW-CK_rbSWE.js → xychartDiagram-XJAZEDNM-COtlZzYb.js} +3 -3
  475. package/web/index.html +19 -17
  476. package/web/assets/ProcessFlowModal-Badh2Pbg.js +0 -24
  477. package/web/assets/architecture-TIHT7OUA-V6N4J3SY-fTePvLoe.js +0 -1
  478. package/web/assets/architectureDiagram-OT7VKSJ7-mKEXxkeF.js +0 -36
  479. package/web/assets/blockDiagram-54WLDVWM-Dg_hrRWE.js +0 -132
  480. package/web/assets/c4Diagram-X2LASM7V-CnhoT4aV.js +0 -10
  481. package/web/assets/chunk-2AEHWXPW-CV4r6-1I.js +0 -127
  482. package/web/assets/chunk-5VCL7Z4A-CQ-2dYWj.js +0 -1
  483. package/web/assets/chunk-6BELYETK-ChsYtfHW.js +0 -1
  484. package/web/assets/chunk-7CWYLC5S-DRgGkkpF.js +0 -2
  485. package/web/assets/chunk-7FYTHRHK-CsmYIswB.js +0 -32
  486. package/web/assets/chunk-KRXBNO2N-C-UNuKKf.js +0 -1
  487. package/web/assets/chunk-LAHSG3N2-Bz4hcn0N.js +0 -321
  488. package/web/assets/chunk-LIEV3EAG-pyKGssV3.js +0 -1
  489. package/web/assets/chunk-NLANEA3F-XpH_NPA5.js +0 -62
  490. package/web/assets/chunk-PE7DX7ZZ-CCvRVj43.js +0 -10
  491. package/web/assets/chunk-Q67WD55A-BWCH2SVz.js +0 -156
  492. package/web/assets/chunk-SZD42YQK-ijyKKVr1.js +0 -70
  493. package/web/assets/chunk-TFPGFQPA-Dj6a-0pC.js +0 -231
  494. package/web/assets/chunk-TGVD4F4B-BMc31Gj2.js +0 -2
  495. package/web/assets/chunk-UAT7B5JY-DW-A_fpI.js +0 -1
  496. package/web/assets/chunk-VY5UBI4V-yxPQAWxw.js +0 -1
  497. package/web/assets/chunk-WJBAP47W-BckNkuEN.js +0 -1
  498. package/web/assets/classDiagram-VPOSVPXH-BjOiXA_C.js +0 -1
  499. package/web/assets/classDiagram-v2-5VFNUXQA-BjOiXA_C.js +0 -1
  500. package/web/assets/cynefin-VYW2F7L2-2VAHYKR6-DAsF6GZv.js +0 -1
  501. package/web/assets/dagre-K64A6Z3X-CUwi79mA.js +0 -4
  502. package/web/assets/diagram-2TLGJV2N-C2F5Nfym.js +0 -30
  503. package/web/assets/diagram-KFBZAVDW-Dfmf_4ti.js +0 -3
  504. package/web/assets/diagram-YP3FGXHI-CyNFYtkd.js +0 -24
  505. package/web/assets/erDiagram-SZLJLQMB-CohQjznC.js +0 -85
  506. package/web/assets/eventmodeling-45OFAUF4-AOUJWSLX-BvTc6iQi.js +0 -1
  507. package/web/assets/flowDiagram-BWE6NHOH-B6RnEvD9.js +0 -1
  508. package/web/assets/ganttDiagram-2QTCJSNI-Cks_mEcF.js +0 -292
  509. package/web/assets/gitGraph-TEB2WS4Q-U5VWYIBC-Dy7CCSZ5.js +0 -1
  510. package/web/assets/gitGraphDiagram-VVZAVV5G-CG41cIVc.js +0 -106
  511. package/web/assets/info-DKCQHKI2-DX6KST54-BLJg1ikX.js +0 -1
  512. package/web/assets/infoDiagram-FY4PZKRE-BJ3vPVwW.js +0 -2
  513. package/web/assets/katex-C5OPUE3Q-Bnbavtmu.js +0 -257
  514. package/web/assets/packet-7NZHBO7P-5WG45QPS-KUxcuvaA.js +0 -1
  515. package/web/assets/pie-RZYD4A2V-2OCZ2VTD-DPs08kJY.js +0 -1
  516. package/web/assets/pieDiagram-YPY3TKG5-BuT3MYzm.js +0 -39
  517. package/web/assets/radar-I7S5WNFK-EPLSA35B-w5F0K3a7.js +0 -1
  518. package/web/assets/railroad-3IZDKUUU-PWFQV6Y6-Bu668yqY.js +0 -1
  519. package/web/assets/railroad-abnf-AHOZXSZD-JYYJX3BS-B6ghpETS.js +0 -1
  520. package/web/assets/railroad-ebnf-EBAXGLYW-RBZ4ONAP-D-XmHh3M.js +0 -1
  521. package/web/assets/railroad-peg-LSFZ7HO6-KHNPXGPG-mhkrhzEn.js +0 -1
  522. package/web/assets/src-C5sx_gMp.js +0 -1
  523. package/web/assets/stateDiagram-DODFXNG5-DTdHc-fF.js +0 -1
  524. package/web/assets/stateDiagram-v2-JNMM5JRA-DioNLDDo.js +0 -1
  525. package/web/assets/swimlanes-OSWOYLSK-CA0ofdwY.js +0 -2
  526. package/web/assets/swimlanesDiagram-RHGX2RDE-PkwUdckC.js +0 -8
  527. package/web/assets/treeView-QDETBFTQ-EHYTSQNO-C5lpBgPm.js +0 -1
  528. package/web/assets/treemap-6X3UGDF4-CCWCF7EK-BB0ZyO-B.js +0 -1
  529. package/web/assets/wardley-OPB4EBWU-6OWERDVT-CEA3Nq0j.js +0 -1
  530. package/web/assets/wardleyDiagram-XK6FUAWV-CVV0dHFr.js +0 -78
  531. /package/web/assets/{sizeCapture-5GORAGP4-BEqQzsxs.js → sizeCapture-FGA5AGYP-BEqQzsxs.js} +0 -0
@@ -21,11 +21,11 @@ import { logUnresolvedReceiverFiles, summarizeUnresolvedReceivers, } from './ing
21
21
  import { summarizeUndecidedSatisfaction } from './ingestion/scope-resolution/undecided-satisfaction.js';
22
22
  import { summarizeScopeExtractionFailures } from './ingestion/scope-resolution/scope-extraction-failures.js';
23
23
  import { resetDegradedParseCounter } from './tree-sitter/safe-parse.js';
24
- import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, closeLbugBeforeExit, loadCachedEmbeddings, deleteNodesForFiles, ensureEmbeddingRowDmlSafe, ensureFtsRowDmlSafe, readIndexCatalogSnapshot, INDEX_CATALOG_UNREADABLE, deleteAllCommunitiesAndProcesses, deleteAllInterprocTaintPaths, deleteAllCallSummaries, deleteAllInjects, deleteAllAdvisedBy, deleteSpringAopEvidenceNodes, deleteSpringAutoConfigurationDeclarations, deleteSpringAutoConfigurationSyntheticClasses, queryImportersBatch, loadFTSExtension, wipeLbugDbFiles, LbugWipeError, DELETE_FILES_CHUNK_SIZE, } from './lbug/lbug-adapter.js';
24
+ import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, closeLbugBeforeExit, loadCachedEmbeddings, deleteNodesForFiles, nodeTablesWithRowsForFiles, snapshotDerivedRelsForFiles, restoreDerivedRels, ensureEmbeddingRowDmlSafe, ensureFtsRowDmlSafe, readIndexCatalogSnapshot, INDEX_CATALOG_UNREADABLE, deleteAllCommunitiesAndProcesses, deleteAllInterprocTaintPaths, deleteAllCallSummaries, deleteAllInjects, deleteAllAdvisedBy, deleteAllDestinations, deleteSpringAopEvidenceNodes, deleteSpringAutoConfigurationDeclarations, deleteSpringAutoConfigurationSyntheticClasses, queryImportersBatch, loadFTSExtension, wipeLbugDbFiles, LbugWipeError, DELETE_FILES_CHUNK_SIZE, } from './lbug/lbug-adapter.js';
25
25
  import { estimateBufferPool, setBufferPoolSizeHint, resolveNativeSafeStorageDir, } from './lbug/lbug-config.js';
26
26
  import { escapeCypherString } from './lbug/cypher-escape.js';
27
27
  import { chunk } from '../lib/utils.js';
28
- import { buildSearchIndexesOrDegrade, ftsFailureIsFatal, createSearchFTSIndexes, summarizeFtsIndexBuildFailures, dropSearchFTSIndexes, initialiseSearchFTSStemmer, verifySearchFTSIndexes, } from './search/fts-indexes.js';
28
+ import { buildSearchIndexesOrDegrade, ftsFailureIsFatal, createSearchFTSIndexes, summarizeFtsIndexBuildFailures, dropSearchFTSIndexes, missingSearchFTSIndexTables, initialiseSearchFTSStemmer, verifySearchFTSIndexes, } from './search/fts-indexes.js';
29
29
  import { cjkSegmentationModeMismatch, getSearchFTSCjkSegmentation, initialiseSearchFTSCjkSegmentation, } from './search/cjk-segmentation.js';
30
30
  import { getExtensionCapability, getExtensionCapabilities, getFtsCapability, resolveAnalyzeInstallPolicy, } from './lbug/extension-loader.js';
31
31
  import { diagnoseExtensionLoad } from './lbug/extension-load-error.js';
@@ -41,10 +41,13 @@ import { taintModelVersion } from './ingestion/taint/typescript-model.js';
41
41
  import { parseTruthyEnv, parsePositiveIntEnv } from './ingestion/utils/env.js';
42
42
  import { computeFileHashes, diffFileHashes } from '../storage/file-hash.js';
43
43
  import { extractChangedSubgraph, computeEffectiveWriteSet, } from './incremental/subgraph-extract.js';
44
+ import { collectSpringConfigConsumerDriftFiles, } from './incremental/spring-config-drift.js';
44
45
  import { shadowCandidatesFor } from './incremental/shadow-candidates.js';
45
46
  import { shouldEscalateIncrementalWrite } from './incremental/escalation-gate.js';
46
- import { loadParseCache, saveParseCache, pruneCache, PARSE_CACHE_VERSION, } from '../storage/parse-cache.js';
47
- import { getDurableParsedFileDir, pruneAndSaveDurableParsedFileStore, } from '../storage/parsedfile-store.js';
47
+ import { ftsTablesAmong, incrementalFtsTablesFromGraph, nodeTablesForIncrementalDelete, shouldPreservePersistedDerivedGraph, } from './incremental/derived-writeback.js';
48
+ import { NODE_TABLES } from './lbug/schema.js';
49
+ import { loadParseCache, saveParseCache, pruneCache, PARSE_CACHE_VERSION, createColdParseRebuildDir, emptyParseCache, forgetCreatedParseCacheDir, } from '../storage/parse-cache.js';
50
+ import { mergeStagedDurableParsedFileStore } from '../storage/parsedfile-store.js';
48
51
  import { getCurrentCommit, getCurrentBranch, getRemoteUrl, hasGitDir, getInferredRepoName, isWorkingTreeDirty, listWorkingTreeDirtyPaths, resolveRepoIdentityRoot, } from '../storage/git.js';
49
52
  import { isGitNexusManagedPath } from '../storage/gitnexus-managed-paths.js';
50
53
  import { getMaxFileSizeBytes } from './ingestion/utils/max-file-size.js';
@@ -53,9 +56,12 @@ import { sanitizeDetectedBranch } from '../cli/analyze-config.js';
53
56
  import { EMBEDDING_TABLE_NAME, EMBEDDING_DIMS, STALE_HASH_SENTINEL, SCHEMA_FINGERPRINT, schemaFingerprintMismatch, isSchemaFingerprintShaped, embeddingDimsMismatch, } from './lbug/schema.js';
54
57
  import { isSpringBeanCandidateSourceFile } from './ingestion/frameworks/spring/bean-catalog.js';
55
58
  import { isSpringBeanFactoryDeclaration } from './ingestion/frameworks/spring/bean-factories.js';
56
- import { SPRING_AOP_FEATURE, SPRING_BEAN_INVENTORY_FEATURE, SPRING_CONDITIONALS_FEATURE, SPRING_NON_HTTP_HANDLERS_FEATURE, } from './ingestion/frameworks/spring/analysis-features.js';
57
- import { JAVA_ENUM_INTERFACE_HERITAGE_FEATURE, JAVA_RECORD_COMPONENT_ACCESSORS_FEATURE, SPRING_CONFIG_BINDINGS_FEATURE, } from './ingestion/languages/java/analysis-features.js';
58
- import { CLASS_FRAMEWORK_ANNOTATIONS_FEATURE, findAnalysisFeatureMismatches, resolveAnalysisFeatureVersions, } from './analysis-features.js';
59
+ import { SPRING_CONFIG_UNRESOLVED_PREFIX } from './ingestion/frameworks/spring/config-bindings.js';
60
+ import { classifySpringConfigFile } from './ingestion/pipeline-phases/spring-config.js';
61
+ import { SPRING_ROUTE_BINDINGS_FEATURE } from './ingestion/frameworks/spring/analysis-features.js';
62
+ import { springVendorPrefixesKey } from './ingestion/frameworks/spring/vendor-prefixes.js';
63
+ import { findAnalysisFeatureMismatches, resolveAnalysisFeatureVersions, } from './analysis-features.js';
64
+ import { ANALYSIS_FEATURES } from './analysis-feature-registry.js';
59
65
  import { analyzerRunnerIdentitiesEqual, finalizeAnalyzerRunnerIdentity, resolveAnalyzerRunnerIdentity, } from './analyzer-identity.js';
60
66
  // Static, and deliberately so: `embedding-count.ts` and `embedding-checkpoint.ts`
61
67
  // live outside `core/embeddings/` precisely because the lazy-embeddings
@@ -79,16 +85,6 @@ import { EMBEDDING_RESUME_MAX_ATTEMPTS, decideEmbeddingResume, mintInterruptedCh
79
85
  * Newline and tab are preserved — multi-line notices are intentional.
80
86
  */
81
87
  const stripControlCharacters = (msg) => msg.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/g, '');
82
- const ANALYSIS_FEATURES = [
83
- CLASS_FRAMEWORK_ANNOTATIONS_FEATURE,
84
- SPRING_AOP_FEATURE,
85
- SPRING_BEAN_INVENTORY_FEATURE,
86
- SPRING_CONDITIONALS_FEATURE,
87
- SPRING_NON_HTTP_HANDLERS_FEATURE,
88
- SPRING_CONFIG_BINDINGS_FEATURE,
89
- JAVA_ENUM_INTERFACE_HERITAGE_FEATURE,
90
- JAVA_RECORD_COMPONENT_ACCESSORS_FEATURE,
91
- ];
92
88
  function stringList(value) {
93
89
  return Array.isArray(value)
94
90
  ? value.filter((item) => typeof item === 'string')
@@ -557,6 +553,17 @@ async function resolveWriteTarget(repoPath, options) {
557
553
  metaDir: path.dirname(metaPath),
558
554
  };
559
555
  }
556
+ async function removeColdParseRebuildDir(dir, ignoreErrors) {
557
+ if (!dir)
558
+ return;
559
+ try {
560
+ await fs.rm(dir, { recursive: true, force: true });
561
+ }
562
+ catch (err) {
563
+ if (!ignoreErrors)
564
+ throw err;
565
+ }
566
+ }
560
567
  /**
561
568
  * Run the full analysis under an exclusive, index-directory-scoped write lock
562
569
  * (#2658). A second concurrent `analyze` on the same slot waits here for the
@@ -634,6 +641,7 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
634
641
  // selects a `branches/<slug>/` sub-slot only for an explicit `--branch` that
635
642
  // does not own the flat slot. See resolveWriteTarget for the full contract.
636
643
  const { storagePath, repoHasGit, currentCommit, branchLabel, placement, lbugPath, metaDir } = writeTarget;
644
+ let coldParseRebuildDir;
637
645
  // Start each analyze with a clean buffer-pool hint: any pre-pipeline DB open
638
646
  // (e.g. the embeddings-cache open) falls back to the default until the hint is
639
647
  // set from the built graph below, so a prior run's size can't leak in.
@@ -798,6 +806,13 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
798
806
  }
799
807
  progress('fts', 90, 'Search indexes ready');
800
808
  progress('done', 100, 'Done');
809
+ if (options.registryName) {
810
+ await registerRepo(repoPath, existingMeta, {
811
+ name: options.registryName,
812
+ allowDuplicateName: options.allowDuplicateName,
813
+ branch: placement.branch,
814
+ });
815
+ }
801
816
  return {
802
817
  repoName: options.registryName ??
803
818
  getInferredRepoName(repoPath) ??
@@ -1029,6 +1044,15 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1029
1044
  options = { ...options, force: true };
1030
1045
  analysisFeatureMismatchLogged = true;
1031
1046
  }
1047
+ const currentSpringVendorPrefixes = springVendorPrefixesKey();
1048
+ const persistedRouteBindings = existingMeta?.analysisFeatures?.[SPRING_ROUTE_BINDINGS_FEATURE.id];
1049
+ if (existingMeta &&
1050
+ persistedRouteBindings === SPRING_ROUTE_BINDINGS_FEATURE.version &&
1051
+ existingMeta.springVendorPrefixes !== currentSpringVendorPrefixes) {
1052
+ log('Spring vendor mapping prefixes changed; forcing a full rebuild so persisted Route ' +
1053
+ 'evidence matches the configured aliases.');
1054
+ options = { ...options, force: true };
1055
+ }
1032
1056
  // Analyzer provenance is part of freshness, not merely diagnostics. A
1033
1057
  // same-commit fast path must not preserve metadata produced by an older,
1034
1058
  // malformed, or dependency/native-different runner. Force a real rebuild so
@@ -1083,6 +1107,82 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1083
1107
  `new width. Tip: set GITNEXUS_EMBEDDING_DIMS (or --embedding-dims) to pin it across runs.`);
1084
1108
  options = { ...options, force: true };
1085
1109
  }
1110
+ // Actuator snapshots are external runtime inputs and are intentionally not
1111
+ // hashed or persisted. Rebuild on every enabled run so updated snapshots
1112
+ // cannot hit the git freshness fast path; rebuild once when the option is
1113
+ // removed so stale runtime-only evidence is cleared from the index.
1114
+ const springActuatorRequested = options.springActuatorPath !== undefined;
1115
+ const springActuatorPreviouslyEnabled = existingMeta?.springActuator?.enabled === true;
1116
+ const previousActuatorInputs = existingMeta?.springActuator?.repoRelativeInputs;
1117
+ const retainedActuatorInputs = Array.isArray(previousActuatorInputs)
1118
+ ? previousActuatorInputs.filter((input) => typeof input === 'string')
1119
+ : [];
1120
+ if (springActuatorRequested) {
1121
+ const resolvedRepo = path.resolve(repoPath);
1122
+ const resolvedInput = path.resolve(repoPath, options.springActuatorPath);
1123
+ const relativeInput = path.relative(resolvedRepo, resolvedInput);
1124
+ const springActuatorRepoRelativeInput = relativeInput === ''
1125
+ ? '.'
1126
+ : relativeInput === '..' ||
1127
+ relativeInput.startsWith(`..${path.sep}`) ||
1128
+ path.isAbsolute(relativeInput)
1129
+ ? null
1130
+ : relativeInput.split(path.sep).join('/');
1131
+ if (springActuatorRepoRelativeInput !== null &&
1132
+ !retainedActuatorInputs.includes(springActuatorRepoRelativeInput)) {
1133
+ retainedActuatorInputs.push(springActuatorRepoRelativeInput);
1134
+ }
1135
+ if (!options.force) {
1136
+ log('Spring Actuator runtime enrichment requested; forcing a full rebuild.');
1137
+ }
1138
+ options = { ...options, force: true };
1139
+ }
1140
+ else if (springActuatorPreviouslyEnabled) {
1141
+ if (!Array.isArray(previousActuatorInputs) ||
1142
+ previousActuatorInputs.some((input) => typeof input !== 'string')) {
1143
+ throw new Error('Cannot safely disable Spring Actuator runtime enrichment because the previous ' +
1144
+ 'index did not record whether its snapshot was inside the repository. Re-run once ' +
1145
+ 'with the previous --spring-actuator path, then run again without it.');
1146
+ }
1147
+ log('Spring Actuator runtime enrichment disabled; rebuilding to remove runtime evidence.');
1148
+ options = { ...options, force: true };
1149
+ }
1150
+ const springActuatorScanExclusions = retainedActuatorInputs.length === 0 ? undefined : retainedActuatorInputs;
1151
+ // AsyncAPI documents are the same class of input as Actuator snapshots and
1152
+ // need the same treatment, for a reason git cannot see: the documents live
1153
+ // outside the tree as often as in it, and NOTHING about replacing one moves
1154
+ // the commit or dirties the working tree. Without this, the second run of an
1155
+ // out-of-band cache — the workflow the option exists for — takes the
1156
+ // already-up-to-date fast path below, never opens a document, and serves the
1157
+ // previous run's addresses while reporting success. Measured, not reasoned:
1158
+ // editing a document and re-running printed "Already up to date" and left the
1159
+ // old address in the graph.
1160
+ //
1161
+ // Forcing the rebuild also settles a second defect for free. A synthetic
1162
+ // `File` node for an out-of-tree document (`asyncapi:<label>`) carries a path
1163
+ // that is in no write set and is not covered by `isGraphWideNode`, so on an
1164
+ // incremental writeback the node is dropped while its edges — anchored to a
1165
+ // graph-wide `Destination` — are kept, and the edges then COPY against a row
1166
+ // that was never written. A full rebuild has no incremental subgraph to get
1167
+ // that wrong, so the pair cannot come apart.
1168
+ const asyncApiSpecRequested = options.asyncApiSpecPath !== undefined;
1169
+ const asyncApiSpecPreviouslyEnabled = existingMeta?.asyncApiSpec?.enabled === true;
1170
+ if (asyncApiSpecRequested) {
1171
+ if (!options.force) {
1172
+ log('AsyncAPI document reading requested; forcing a full rebuild.');
1173
+ }
1174
+ options = { ...options, force: true };
1175
+ }
1176
+ else if (asyncApiSpecPreviouslyEnabled) {
1177
+ log('AsyncAPI document reading disabled; rebuilding to remove document-derived evidence.');
1178
+ options = { ...options, force: true };
1179
+ }
1180
+ // Programmatic `useParseCache: false` must set force or the up-to-date
1181
+ // guard returns before the empty-cache construction below.
1182
+ if (options.useParseCache === false && !options.force) {
1183
+ log('Parser cache bypass requested; forcing a full rebuild so unchanged files are re-parsed.');
1184
+ options = { ...options, force: true };
1185
+ }
1086
1186
  // ── Early-return: already up to date ──────────────────────────────
1087
1187
  if (existingMeta &&
1088
1188
  !existingMeta.embeddingCheckpoint &&
@@ -1132,6 +1232,30 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1132
1232
  // later read on a host where it loads — which is a legitimate, common
1133
1233
  // state, and the invariant `analyzer-identity-cli.test.ts` pins.
1134
1234
  if (!dirty && !healUnregistered) {
1235
+ if (options.registryName) {
1236
+ await registerRepo(repoPath, existingMeta, {
1237
+ name: options.registryName,
1238
+ allowDuplicateName: options.allowDuplicateName,
1239
+ branch: placement.branch,
1240
+ });
1241
+ if (!placement.branch) {
1242
+ try {
1243
+ await generateAIContextFiles(repoPath, storagePath, options.registryName, existingMeta.stats ?? {}, undefined, {
1244
+ skipAgentsMd: options.skipAgentsMd,
1245
+ skipSkills: options.skipSkills,
1246
+ noStats: options.noStats,
1247
+ defaultBranch: options.defaultBranch,
1248
+ // Fast path does not re-run PDG. Using `options.pdg` would
1249
+ // strip PDG bullets from AGENTS.md on a rename-only analyze.
1250
+ hasPdg: existingMeta.pdg != null,
1251
+ hasSpringActuator: existingMeta.springActuator?.enabled === true,
1252
+ });
1253
+ }
1254
+ catch {
1255
+ /* best-effort — never fail the fast path over a context refresh */
1256
+ }
1257
+ }
1258
+ }
1135
1259
  // ── #2354: restamp the workspace label on a same-commit branch flip ──
1136
1260
  // The flat slot follows the checked-out working tree; a branch switch
1137
1261
  // at the SAME commit with a clean tree changes nothing the pipeline
@@ -1256,11 +1380,17 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1256
1380
  }
1257
1381
  }
1258
1382
  // ── Load incremental parse cache ──────────────────────────────────
1259
- // Content-addressed: safe to reuse across `--force` runs (chunks whose
1260
- // file contents haven't changed produce identical worker output).
1261
- // Loaded into a single ParseCache object that the pipeline mutates
1262
- // in-place (cache hits leave entries unchanged; misses add new ones).
1263
- const parseCache = await loadParseCache(storagePath);
1383
+ // Content-addressed: `--force` reuses parser shards; `useParseCache: false`
1384
+ // stages a new generation under a run-unique parse-rebuild.* dir and publishes
1385
+ // after success. Unique because index locks are per branch slot while this
1386
+ // cache root is shared across branches.
1387
+ if (options.useParseCache === false) {
1388
+ coldParseRebuildDir = await createColdParseRebuildDir(storagePath);
1389
+ forgetCreatedParseCacheDir(coldParseRebuildDir);
1390
+ }
1391
+ const parseCache = options.useParseCache === false
1392
+ ? emptyParseCache(coldParseRebuildDir)
1393
+ : await loadParseCache(storagePath);
1264
1394
  // Streamed structural emit (#2680). Resolved ONCE, so the pipeline flag and
1265
1395
  // the CSV-dir resolution below cannot disagree — and resolved HERE, not at
1266
1396
  // function entry, because the POSITION is load-bearing: the gate is
@@ -1276,46 +1406,81 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1276
1406
  // pipeline has already run by then; that run emits non-streamed, precisely as
1277
1407
  // `resolveStreamPdgEmit` — read fresh at the same point — behaves.)
1278
1408
  const streamGraphEmitActive = resolveStreamGraphEmit(options);
1409
+ // #3016: hold back Leiden and flow extraction when the persisted metadata
1410
+ // says this run is a candidate for a surgical incremental write, whose
1411
+ // derived layer is reused rather than recomputed. Deliberately the same
1412
+ // conditions as the `isIncremental` decision below MINUS the two that only
1413
+ // the pipeline can answer (the analysis-feature re-check and a non-empty
1414
+ // file list), so this is a superset: every run that turns out incremental
1415
+ // had the phases skipped, and the runs that do not are caught by
1416
+ // `runDeferredDerivedPhases` once the write plan is known. Excluded on the
1417
+ // streaming path because that is a full rebuild by construction, and the
1418
+ // deferred phases must not write into a finalized emit sink.
1419
+ const skipDerivedGraphPhases = !streamGraphEmitActive &&
1420
+ !options.force &&
1421
+ !!existingMeta &&
1422
+ !!existingMeta.fileHashes &&
1423
+ Object.keys(existingMeta.fileHashes).length > 0 &&
1424
+ repoHasGit &&
1425
+ !schemaFingerprintMismatch(existingMeta.schemaFingerprint);
1279
1426
  // ── Phase 1: Full Pipeline (0–60%) ────────────────────────────────
1280
- const pipelineResult = await runPipelineFromRepo(repoPath, (p) => {
1281
- const phaseLabel = PHASE_LABELS[p.phase] || p.phase;
1282
- const scaled = Math.round(p.percent * 0.6);
1283
- const message = p.detail
1284
- ? `${p.message || phaseLabel} (${p.detail})`
1285
- : p.message || phaseLabel;
1286
- progress(p.phase, scaled, message);
1287
- }, {
1288
- parseCache,
1289
- workerPoolSize: options.workerPoolSize,
1290
- // CFG/PDG opt-in (#2081 M1). PipelineOptions.pdg fans out to the worker
1291
- // build gate (workerData.pdg) and the scope-resolution emit gate.
1292
- pdg: options.pdg === true,
1293
- pdgMaxFunctionLines: options.pdgMaxFunctionLines,
1294
- pdgMaxEdgesPerFunction: options.pdgMaxEdgesPerFunction,
1295
- pdgMaxReachingDefEdgesPerFunction: options.pdgMaxReachingDefEdgesPerFunction,
1296
- pdgMaxCdgEdgesPerFunction: options.pdgMaxCdgEdgesPerFunction,
1297
- pdgMaxTaintFindingsPerFunction: options.pdgMaxTaintFindingsPerFunction,
1298
- pdgMaxTaintHops: options.pdgMaxTaintHops,
1299
- pdgMaxInterprocFindings: options.pdgMaxInterprocFindings,
1300
- pdgMaxInterprocHops: options.pdgMaxInterprocHops,
1301
- pdgMaxInterprocEdges: options.pdgMaxInterprocEdges,
1302
- // Streaming/chunked PDG emit (#2202) — gated to full-rebuild runs
1303
- // (force === true) so the incremental writeback never reads back an
1304
- // offloaded BasicBlock layer. Memory-only; byte-identical output.
1305
- streamPdgEmit: resolveStreamPdgEmit(options),
1306
- pdgEmitChunkSize: resolvePdgEmitChunkSize(options),
1307
- // Streamed structural emit (#2680) — same full-rebuild gate as the PDG
1308
- // toggle above, for the same incremental-writeback reason.
1309
- streamGraphEmit: streamGraphEmitActive,
1310
- // Resolved ONLY when streaming is active: on a Windows non-ASCII storage
1311
- // path this helper mkdtempSyncs a real directory, so evaluating it
1312
- // unconditionally would leak one temp dir per analyze even with the flag
1313
- // off. The PDG sibling resolves inside its guard for the same reason.
1314
- graphEmitCsvDir: streamGraphEmitActive
1315
- ? resolveNativeSafeStorageDir(storagePath, 'graph-csv')
1316
- : undefined,
1317
- fetchWrappers: options.fetchWrappers,
1318
- });
1427
+ let pipelineResult;
1428
+ try {
1429
+ pipelineResult = await runPipelineFromRepo(repoPath, (p) => {
1430
+ const phaseLabel = PHASE_LABELS[p.phase] || p.phase;
1431
+ const scaled = Math.round(p.percent * 0.6);
1432
+ const message = p.detail
1433
+ ? `${p.message || phaseLabel} (${p.detail})`
1434
+ : p.message || phaseLabel;
1435
+ progress(p.phase, scaled, message);
1436
+ }, {
1437
+ parseCache,
1438
+ workerPoolSize: options.workerPoolSize,
1439
+ // CFG/PDG opt-in (#2081 M1). PipelineOptions.pdg fans out to the worker
1440
+ // build gate (workerData.pdg) and the scope-resolution emit gate.
1441
+ pdg: options.pdg === true,
1442
+ pdgMaxFunctionLines: options.pdgMaxFunctionLines,
1443
+ pdgMaxEdgesPerFunction: options.pdgMaxEdgesPerFunction,
1444
+ pdgMaxReachingDefEdgesPerFunction: options.pdgMaxReachingDefEdgesPerFunction,
1445
+ pdgMaxCdgEdgesPerFunction: options.pdgMaxCdgEdgesPerFunction,
1446
+ pdgMaxTaintFindingsPerFunction: options.pdgMaxTaintFindingsPerFunction,
1447
+ pdgMaxTaintHops: options.pdgMaxTaintHops,
1448
+ pdgMaxInterprocFindings: options.pdgMaxInterprocFindings,
1449
+ pdgMaxInterprocHops: options.pdgMaxInterprocHops,
1450
+ pdgMaxInterprocEdges: options.pdgMaxInterprocEdges,
1451
+ // Streaming/chunked PDG emit (#2202) — gated to full-rebuild runs
1452
+ // (force === true) so the incremental writeback never reads back an
1453
+ // offloaded BasicBlock layer. Memory-only; byte-identical output.
1454
+ streamPdgEmit: resolveStreamPdgEmit(options),
1455
+ pdgEmitChunkSize: resolvePdgEmitChunkSize(options),
1456
+ // Streamed structural emit (#2680) — same full-rebuild gate as the PDG
1457
+ // toggle above, for the same incremental-writeback reason.
1458
+ streamGraphEmit: streamGraphEmitActive,
1459
+ // Resolved ONLY when streaming is active: on a Windows non-ASCII storage
1460
+ // path this helper mkdtempSyncs a real directory, so evaluating it
1461
+ // unconditionally would leak one temp dir per analyze even with the flag
1462
+ // off. The PDG sibling resolves inside its guard for the same reason.
1463
+ graphEmitCsvDir: streamGraphEmitActive
1464
+ ? resolveNativeSafeStorageDir(storagePath, 'graph-csv')
1465
+ : undefined,
1466
+ fetchWrappers: options.fetchWrappers,
1467
+ skipDerivedGraphPhases,
1468
+ springActuatorPath: options.springActuatorPath,
1469
+ asyncApiSpecPath: options.asyncApiSpecPath,
1470
+ springActuatorScanExclusions,
1471
+ });
1472
+ }
1473
+ catch (err) {
1474
+ await removeColdParseRebuildDir(coldParseRebuildDir, true);
1475
+ throw err;
1476
+ }
1477
+ if (options.force && (pipelineResult.parseCacheHitFileCount ?? 0) > 0) {
1478
+ log(`Rebuilt the graph and FTS while reusing cached parser output for ` +
1479
+ `${pipelineResult.parseCacheHitFileCount} file(s) ` +
1480
+ `(parse cache ${PARSE_CACHE_VERSION}). ` +
1481
+ `For same-version capture/query development changes, increment SCHEMA_BUMP in ` +
1482
+ `src/storage/parse-cache.ts to invalidate parser output.`);
1483
+ }
1319
1484
  // ── Phase 2: LadybugDB (60–85%) ──────────────────────────────────
1320
1485
  progress('lbug', 60, 'Loading into LadybugDB...');
1321
1486
  // Compute current per-file content hashes from the pipeline's File nodes.
@@ -1364,6 +1529,26 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1364
1529
  const hashDiff = isIncremental
1365
1530
  ? diffFileHashes(newFileHashes, existingMeta.fileHashes)
1366
1531
  : undefined;
1532
+ // #3016: `skipDerivedGraphPhases` was decided BEFORE the pipeline, from the
1533
+ // persisted metadata alone, so it can only ever be a bet that this run stays
1534
+ // surgical. Settle the bet here, where `isIncremental` and the deletion set
1535
+ // are both known, and pay it off by running the held-back phases whenever the
1536
+ // write plan needs a freshly derived layer:
1537
+ // - not incremental → full rebuild writes the whole graph, and a graph
1538
+ // with no Community/Process nodes would publish an
1539
+ // index with no communities and no flows;
1540
+ // - added/changed/deleted files → the persisted derived layer can miss new
1541
+ // symbols, keep stale memberships, or reference
1542
+ // removed ids. Only an empty file-hash diff is a
1543
+ // proof that Leiden/flows still match.
1544
+ const preserveDerivedLayer = skipDerivedGraphPhases &&
1545
+ isIncremental &&
1546
+ !!hashDiff &&
1547
+ shouldPreservePersistedDerivedGraph(hashDiff);
1548
+ if (skipDerivedGraphPhases && !preserveDerivedLayer) {
1549
+ progress('communities', 58, 'Detecting code communities and flows...');
1550
+ await pipelineResult.runDeferredDerivedPhases?.();
1551
+ }
1367
1552
  // #2 atomic index publish: on a full rebuild, build the fresh DB at a temp
1368
1553
  // path and swap it over the live index in one rename at the very end, so a
1369
1554
  // concurrent MCP reader opening mid-build only ever sees the previous
@@ -1562,6 +1747,7 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1562
1747
  // collapse check compares the whole in-memory graph against the whole DB,
1563
1748
  // which is only a like-for-like comparison on a full rebuild.
1564
1749
  let wroteChangedSubgraphOnly = false;
1750
+ let incrementalFtsRebuildTables;
1565
1751
  if (isIncremental && hashDiff) {
1566
1752
  // ── Incremental DB writeback ───────────────────────────────────
1567
1753
  // 0. Expand the writable set with transitive importers of
@@ -1697,6 +1883,29 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1697
1883
  // and extractChangedSubgraph — asymmetry between the two would
1698
1884
  // leave stale rows or PK-conflict at COPY time.
1699
1885
  const effectiveWriteSet = computeEffectiveWriteSet(pipelineResult.graph, writableFiles);
1886
+ const springConfigChanged = hashDiff.toWrite.some((filePath) => classifySpringConfigFile(filePath) !== null) ||
1887
+ hashDiff.deleted.some((filePath) => classifySpringConfigFile(filePath) !== null);
1888
+ if (springConfigChanged) {
1889
+ const unresolvedPrefix = escapeCypherString(SPRING_CONFIG_UNRESOLVED_PREFIX);
1890
+ const persistedSpringConfigConsumers = (await executeQuery('MATCH (n:Property) ' +
1891
+ `WHERE n.description CONTAINS '${unresolvedPrefix}' ` +
1892
+ 'RETURN n.id AS id, n.description AS description ' +
1893
+ 'UNION ALL ' +
1894
+ 'MATCH (n:Class) ' +
1895
+ `WHERE n.description CONTAINS '${unresolvedPrefix}' ` +
1896
+ 'RETURN n.id AS id, n.description AS description ' +
1897
+ 'UNION ALL ' +
1898
+ 'MATCH (n:Record) ' +
1899
+ `WHERE n.description CONTAINS '${unresolvedPrefix}' ` +
1900
+ 'RETURN n.id AS id, n.description AS description'));
1901
+ const springConfigConsumerDriftFiles = collectSpringConfigConsumerDriftFiles(pipelineResult.graph, persistedSpringConfigConsumers);
1902
+ for (const filePath of springConfigConsumerDriftFiles)
1903
+ effectiveWriteSet.add(filePath);
1904
+ if (springConfigConsumerDriftFiles.size > 0) {
1905
+ log(`Incremental: +${springConfigConsumerDriftFiles.size} file(s) added for ` +
1906
+ 'Spring config consumer property drift');
1907
+ }
1908
+ }
1700
1909
  // `frameworkAnnotations` is derived from cross-file JVM visibility, so
1701
1910
  // an unchanged Class row can change when a same-package declaration is
1702
1911
  // added or removed without producing an IMPORTS edge. Compare the fresh
@@ -1824,6 +2033,14 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
1824
2033
  const sizeForcedRebuild = shouldEscalateIncrementalWrite(filesToDelete.length, effectiveWriteSet.size, allFilePaths.length);
1825
2034
  if (extensionForcedRebuild || sizeForcedRebuild) {
1826
2035
  escalatedFullWrite = true;
2036
+ // #3016: escalation converts this run into a wipe + full bulk COPY of
2037
+ // the in-memory graph, so the derived layer the skip was betting on
2038
+ // preserving has to exist in that graph after all. Same reasoning as
2039
+ // the not-incremental branch above, just discovered later.
2040
+ if (preserveDerivedLayer) {
2041
+ progress('communities', 63, 'Detecting code communities and flows...');
2042
+ await pipelineResult.runDeferredDerivedPhases?.();
2043
+ }
1827
2044
  // Every live cause is named, not just the first: a DB can carry BOTH a
1828
2045
  // vector index and FTS indexes, and reporting one cause while the other
1829
2046
  // is equally fatal is how #2841 stayed mis-diagnosed for so long. §5.D:
@@ -2024,7 +2241,53 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2024
2241
  // the snapshot type exists to enforce.
2025
2242
  if (buildPath === lbugPath)
2026
2243
  liveIndexMutationStarted = true;
2027
- await dropSearchFTSIndexes(indexCatalogRows);
2244
+ // FTS narrowing is independent of Leiden/flow reuse: even when this
2245
+ // run re-derives communities, Ladybug still cannot DML a live FTS
2246
+ // index (#2589), so only the tables this write set touches should
2247
+ // lose their index. The probe is a question about the DB rather than
2248
+ // the fresh graph — a symbol the edit DELETED is in no fresh graph
2249
+ // but is still a row that has to go.
2250
+ const tablesWithRows = await nodeTablesWithRowsForFiles(filesToDelete, NODE_TABLES);
2251
+ // Narrowing 1 — the FTS sweep, from "every configured index" to "the
2252
+ // indexes this run must touch". Three sources, and dropping any one of
2253
+ // them strands something:
2254
+ // - what the writeback DELETES (the probe above), because a symbol
2255
+ // the edit removed is in no fresh graph but is still a row;
2256
+ // - what it INSERTS (the fresh graph), because inserting under a live
2257
+ // FTS index is the same #2589 hazard as deleting under one;
2258
+ // - what is MISSING right now, because narrowing to the written
2259
+ // tables would otherwise leave keyword search degraded forever on
2260
+ // tables whose index a previous escalation dropped — the next full
2261
+ // rebuild would be the only thing that ever restored them.
2262
+ // An unreadable catalog proves nothing about that third set, so it
2263
+ // withdraws the narrowing entirely rather than guess.
2264
+ const missingFts = await missingSearchFTSIndexTables(indexCatalogRows);
2265
+ const touchedFts = missingFts
2266
+ ? new Set([
2267
+ ...ftsTablesAmong(tablesWithRows),
2268
+ ...incrementalFtsTablesFromGraph(pipelineResult.graph, new Set(filesToDelete)),
2269
+ ...missingFts,
2270
+ ])
2271
+ : undefined;
2272
+ // Graph-wide Spring synthetic Class nodes are DETACH DELETEd on this
2273
+ // branch even when Class is not in the write set
2274
+ // (`deleteSpringAutoConfigurationSyntheticClasses`). Always include
2275
+ // Class so class_fts is not live across that DML (#2589), including
2276
+ // when the fresh graph no longer materializes the synthetics but the
2277
+ // DB still holds them.
2278
+ if (touchedFts) {
2279
+ touchedFts.add('Class');
2280
+ }
2281
+ incrementalFtsRebuildTables = touchedFts;
2282
+ // MEMBER_OF / STEP_IN_PROCESS / ENTRY_POINT_OF edges hang off the nodes
2283
+ // the DETACH DELETE below removes, so preserving the Community/Process
2284
+ // nodes preserves only half the layer unless these are reattached after
2285
+ // the subgraph write puts the member nodes back. Only the probed tables
2286
+ // can own such an edge, so they are the only ones worth scanning.
2287
+ const derivedSnapshot = preserveDerivedLayer
2288
+ ? await snapshotDerivedRelsForFiles(filesToDelete, [...tablesWithRows])
2289
+ : [];
2290
+ await dropSearchFTSIndexes(indexCatalogRows, incrementalFtsRebuildTables);
2028
2291
  // 1b. Remove the write set's existing rows — batched (#2409): one
2029
2292
  // DETACH DELETE per table per 200-file chunk. The former per-file
2030
2293
  // loop issued a count + delete per table per FILE — ~13k
@@ -2038,6 +2301,9 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2038
2301
  progress('lbug', 62, `Removing rows for changed files (0/${filesToDelete.length})...`);
2039
2302
  await deleteNodesForFiles(filesToDelete, {
2040
2303
  onChunk: (done, total) => progress('lbug', 62, `Removing rows for changed files (${done}/${total})...`),
2304
+ nodeTables: incrementalFtsRebuildTables
2305
+ ? nodeTablesForIncrementalDelete(NODE_TABLES, incrementalFtsRebuildTables)
2306
+ : undefined,
2041
2307
  });
2042
2308
  // Surgical path: Phase 3.5 restores exactly these files' embedding
2043
2309
  // rows (FIX 3). Sound because deleteNodesForFiles propagates errors
@@ -2045,10 +2311,12 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2045
2311
  // deterministically — and this process holds the exclusive DB lock,
2046
2312
  // so no concurrent writer can disturb the derivation.
2047
2313
  deletedFilePathsForRestore = new Set(filesToDelete);
2048
- // 2. Drop graph-wide nodes (Community, Process). They'll be re-inserted
2049
- // from the fresh pipeline output below. Required for the
2050
- // "Leiden runs on the FULL graph" correctness invariant.
2051
- await deleteAllCommunitiesAndProcesses();
2314
+ if (!preserveDerivedLayer) {
2315
+ // 2. Drop graph-wide nodes (Community, Process). They'll be re-inserted
2316
+ // from the fresh pipeline output below. Required for the
2317
+ // "Leiden runs on the FULL graph" correctness invariant.
2318
+ await deleteAllCommunitiesAndProcesses();
2319
+ }
2052
2320
  // 2a. Drop INJECTS edges (DI collection injection, #2200) — their
2053
2321
  // validity is a whole-program property (a third-file change to the
2054
2322
  // interface or an implementer creates/invalidates edges between two
@@ -2067,6 +2335,18 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2067
2335
  // Rebuild the complete ADVISED_BY set on every incremental writeback.
2068
2336
  await deleteAllAdvisedBy();
2069
2337
  await deleteSpringAopEvidenceNodes();
2338
+ // 2b-bis. Drop the whole async messaging overlay. A RESOLVED
2339
+ // Destination deliberately stores no filePath (so the per-file
2340
+ // DETACH DELETE cannot cut a node shared across files), which also
2341
+ // means the per-file delete can never REMOVE one that is now
2342
+ // orphaned, and the endpoint-writability extract can never ADD one
2343
+ // introduced by a new file. Delete-all here plus the graph-wide
2344
+ // re-include in extractChangedSubgraph rebuilds the layer whole;
2345
+ // the springDestinations phase recomputes it from the full file
2346
+ // list on every persisting analyze, so nothing is lost. Both halves
2347
+ // must move together — deleting without the re-include drops the
2348
+ // layer, re-including without the delete duplicates its edges.
2349
+ await deleteAllDestinations();
2070
2350
  // 2c. Drop Spring-owned DECLARES edges (#2415). The
2071
2351
  // auto-configuration phase scans every metadata file and recomputes
2072
2352
  // the full set each run; exact reason filtering leaves declarations
@@ -2095,7 +2375,9 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2095
2375
  // only that. Unchanged-file rows in the DB stay untouched. Pass
2096
2376
  // the SAME effectiveWriteSet so the subgraph and the deletes
2097
2377
  // cover identical files (asymmetry would silently corrupt).
2098
- const subgraph = extractChangedSubgraph(pipelineResult.graph, effectiveWriteSet);
2378
+ const subgraph = extractChangedSubgraph(pipelineResult.graph, effectiveWriteSet, {
2379
+ includeDerivedGraphWide: !preserveDerivedLayer,
2380
+ });
2099
2381
  wroteChangedSubgraphOnly = true;
2100
2382
  await saveIncrementalDirtyState('load-graph', {
2101
2383
  importerExpansion,
@@ -2108,6 +2390,9 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2108
2390
  const pct = Math.min(84, 65 + Math.round((lbugMsgCount / (lbugMsgCount + 10)) * 19));
2109
2391
  progress('lbug', pct, msg);
2110
2392
  });
2393
+ if (preserveDerivedLayer && derivedSnapshot.length > 0) {
2394
+ await restoreDerivedRels(derivedSnapshot);
2395
+ }
2111
2396
  }
2112
2397
  // Boundary drain (#2409): checkpoint at the end of the incremental
2113
2398
  // writeback so the WAL it accumulated never lingers into the FTS and
@@ -2159,6 +2444,7 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2159
2444
  // pre-existing row (#2544/#2546) must not discard this run's otherwise-
2160
2445
  // successful graph/embeddings work — only keyword search degrades.
2161
2446
  const ftsResult = await buildSearchIndexesOrDegrade(executeQuery, {
2447
+ tables: incrementalFtsRebuildTables,
2162
2448
  onIndexStart: options.verbose
2163
2449
  ? (table, indexName) => log(`FTS: creating ${table}.${indexName}`)
2164
2450
  : undefined,
@@ -2771,6 +3057,22 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2771
3057
  lastCommit: currentCommit,
2772
3058
  indexedAt: new Date().toISOString(),
2773
3059
  runnerIdentity,
3060
+ // Persist only normalized repo-relative exclusions, never absolute paths
3061
+ // or payloads. Keep them after runtime enrichment is disabled so a later
3062
+ // ordinary scan cannot rediscover an unchanged snapshot as source/FTS.
3063
+ ...(springActuatorRequested || retainedActuatorInputs.length > 0
3064
+ ? {
3065
+ springActuator: {
3066
+ enabled: springActuatorRequested,
3067
+ repoRelativeInputs: retainedActuatorInputs,
3068
+ },
3069
+ }
3070
+ : {}),
3071
+ // Written only while enabled. Once the option is dropped, the disable
3072
+ // transition above has already forced the cleanup rebuild, so carrying a
3073
+ // `{ enabled: false }` stamp forward would only make every subsequent run
3074
+ // re-decide a question that is already settled.
3075
+ ...(asyncApiSpecRequested ? { asyncApiSpec: { enabled: true } } : {}),
2774
3076
  // Branch identity this index represents (#2106). Recorded for the flat
2775
3077
  // slot too (so resolveBranchPlacement knows which branch owns it). When
2776
3078
  // the label is null (detached HEAD / non-git re-analyze) we PRESERVE an
@@ -2803,8 +3105,9 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2803
3105
  files: pipelineResult.totalFileCount,
2804
3106
  nodes: stats.nodes,
2805
3107
  edges: stats.edges,
2806
- communities: pipelineResult.communityResult?.stats.totalCommunities,
2807
- processes: pipelineResult.processResult?.stats.totalProcesses,
3108
+ communities: pipelineResult.communityResult?.stats.totalCommunities ??
3109
+ existingMeta?.stats?.communities,
3110
+ processes: pipelineResult.processResult?.stats.totalProcesses ?? existingMeta?.stats?.processes,
2808
3111
  embeddings: persistedEmbeddingCount,
2809
3112
  },
2810
3113
  capabilities: {
@@ -2870,6 +3173,7 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2870
3173
  ? existingMeta?.undecidedInterfaceSatisfaction
2871
3174
  : summarizeUndecidedSatisfaction(pipelineResult.undecidedSatisfaction),
2872
3175
  analysisFeatures: currentAnalysisFeatures,
3176
+ springVendorPrefixes: currentSpringVendorPrefixes,
2873
3177
  // Always stamped with the live resolved mode (#2331/#2339) — unlike
2874
3178
  // `pdg` below, 'none' is a meaningful value to compare, not an
2875
3179
  // absence, so this is never conditionally omitted.
@@ -2914,50 +3218,8 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2914
3218
  // meta.indexedAt = T_new while lbugPath still resolves to the pre-swap
2915
3219
  // inode (which latched the reader on the stale index permanently). The meta
2916
3220
  // object is fully computed at this point; only its write is deferred.
2917
- // Persist the incremental parse cache for the next run. Wraps in
2918
- // try/catch so a cache-write failure never breaks an otherwise
2919
- // successful indexing run. Prune stale chunk-hash entries first so
2920
- // the cache file size stays bounded across runs (chunks whose
2921
- // composition no longer matches anything in the current scan are
2922
- // dead weight; the parse phase populates `usedKeys` as it processes
2923
- // chunks).
2924
- try {
2925
- // #2106 R6: the parse cache + durable store are shared across branches.
2926
- // Before pruning to this run's keys, fold in the OTHER branches' recorded
2927
- // chunk keys so a branch switch doesn't evict their still-live shards.
2928
- // Adding to usedKeys makes them survive pruneCache AND land in the saved
2929
- // index (saveParseCache builds the index from usedKeys). Excludes this
2930
- // run's own meta dir, so a single-branch repo folds in nothing → prune
2931
- // set byte-identical to today.
2932
- const { keys: siblingKeys, complete } = await collectBranchCacheKeys(storagePath, metaDir);
2933
- if (complete) {
2934
- for (const k of siblingKeys)
2935
- parseCache.usedKeys.add(k);
2936
- }
2937
- else {
2938
- // Fail-safe toward retention: a sibling meta was unreadable, so keep
2939
- // everything currently loaded rather than evict on incomplete info.
2940
- log('Parse cache: a branch meta was unreadable — retaining all cached chunks (#2106).');
2941
- for (const k of parseCache.entries.keys())
2942
- parseCache.usedKeys.add(k);
2943
- }
2944
- const pruned = pruneCache(parseCache, parseCache.usedKeys);
2945
- if (pruned > 0) {
2946
- log(`Parse cache: pruned ${pruned} stale chunk entries`);
2947
- }
2948
- const savedKeys = await saveParseCache(storagePath, parseCache);
2949
- // Prune the durable ParsedFile store to EXACTLY the parse cache's
2950
- // surviving keys (#2038 warm-cache coverage), so the two content-addressed
2951
- // stores stay coherent: a chunk is "cached" iff both its parse-cache shard
2952
- // and its durable shards exist. A quarantined chunk (in usedKeys but with
2953
- // no parse-cache shard) drops its durable subdir here and re-dispatches
2954
- // next run. Same try/catch — a durable-store write failure must never
2955
- // break an otherwise successful run (next run treats it as a miss).
2956
- await pruneAndSaveDurableParsedFileStore(getDurableParsedFileDir(storagePath), PARSE_CACHE_VERSION, new Set(savedKeys));
2957
- }
2958
- catch (e) {
2959
- log(`Warning: could not save parse cache (${e.message}); continuing.`);
2960
- }
3221
+ // Parse-cache publish waits until after that swap + saveMeta so a failed
3222
+ // registerRepo / close / swap cannot replace live shards (#3153).
2961
3223
  // Forward the --name alias and the registry-collision bypass bit.
2962
3224
  // `allowDuplicateName` is its own concern — independent from the
2963
3225
  // pipeline `force` above. The CLI maps it from
@@ -2978,8 +3240,8 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
2978
3240
  // ── #2354: the flat workspace slot has adopted this run's branch ──────
2979
3241
  // Drop a now-shadowed `branches/<slug>/` sub-index for the same label
2980
3242
  // (unreachable once the flat slot serves it) and align the registry's
2981
- // top-level branch label. Best-effort like the parse-cache save above
2982
- // (#2364 review F5): the index is complete and registered, and a failure
3243
+ // top-level branch label. Best-effort (#2364 review F5): the index is
3244
+ // complete and registered, and a failure
2983
3245
  // here leaves only a stale registry label / undeleted shadowed dir —
2984
3246
  // never wrong routing, because the flat meta this run already stamped is
2985
3247
  // what applyBranchScope trusts. Retried by the next content-changing run
@@ -3014,15 +3276,17 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
3014
3276
  files: pipelineResult.totalFileCount,
3015
3277
  nodes: stats.nodes,
3016
3278
  edges: stats.edges,
3017
- communities: pipelineResult.communityResult?.stats.totalCommunities,
3279
+ communities: pipelineResult.communityResult?.stats.totalCommunities ??
3280
+ existingMeta?.stats?.communities,
3018
3281
  clusters: aggregatedClusterCount,
3019
- processes: pipelineResult.processResult?.stats.totalProcesses,
3282
+ processes: pipelineResult.processResult?.stats.totalProcesses ?? existingMeta?.stats?.processes,
3020
3283
  }, undefined, {
3021
3284
  skipAgentsMd: options.skipAgentsMd,
3022
3285
  skipSkills: options.skipSkills,
3023
3286
  noStats: options.noStats,
3024
3287
  defaultBranch: options.defaultBranch,
3025
3288
  hasPdg: options.pdg === true,
3289
+ hasSpringActuator: options.springActuatorPath !== undefined,
3026
3290
  });
3027
3291
  }
3028
3292
  catch {
@@ -3086,7 +3350,51 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
3086
3350
  // is a crash-safety improvement: a failed swap leaves the previous index
3087
3351
  // live and the next run recovers via the full-rebuild path.
3088
3352
  await saveMeta(metaDir, meta);
3353
+ // Persist the incremental parse cache only after a successful graph
3354
+ // publish (#3153). try/catch so a cache-write failure never breaks an
3355
+ // otherwise successful indexing run. Prune stale chunk-hash entries first
3356
+ // so the cache file size stays bounded across runs (chunks whose
3357
+ // composition no longer matches anything in the current scan are dead
3358
+ // weight; the parse phase populates `usedKeys` as it processes chunks).
3359
+ try {
3360
+ // #2106 R6: the parse cache + durable store are shared across branches.
3361
+ // Before pruning to this run's keys, fold in the OTHER branches' recorded
3362
+ // chunk keys so a branch switch doesn't evict their still-live shards.
3363
+ // Adding to usedKeys makes them survive pruneCache AND land in the saved
3364
+ // index (saveParseCache builds the index from usedKeys). Excludes this
3365
+ // run's own meta dir, so a single-branch repo folds in nothing → prune
3366
+ // set byte-identical to today.
3367
+ const { keys: siblingKeys, complete } = await collectBranchCacheKeys(storagePath, metaDir);
3368
+ if (complete) {
3369
+ for (const k of siblingKeys)
3370
+ parseCache.usedKeys.add(k);
3371
+ }
3372
+ else {
3373
+ // Fail-safe toward retention: a sibling meta was unreadable, so keep
3374
+ // everything currently loaded rather than evict on incomplete info.
3375
+ log('Parse cache: a branch meta was unreadable — retaining all cached chunks (#2106).');
3376
+ for (const k of parseCache.entries.keys())
3377
+ parseCache.usedKeys.add(k);
3378
+ }
3379
+ const pruned = pruneCache(parseCache, parseCache.usedKeys);
3380
+ if (pruned > 0) {
3381
+ log(`Parse cache: pruned ${pruned} stale chunk entries`);
3382
+ }
3383
+ const savedKeys = await saveParseCache(storagePath, parseCache);
3384
+ // Prune the durable ParsedFile store to EXACTLY the parse cache's
3385
+ // surviving keys (#2038 warm-cache coverage), so the two content-addressed
3386
+ // stores stay coherent: a chunk is "cached" iff both its parse-cache shard
3387
+ // and its durable shards exist. A quarantined chunk (in usedKeys but with
3388
+ // no parse-cache shard) drops its durable subdir here and re-dispatches
3389
+ // next run. Same try/catch — a durable-store write failure must never
3390
+ // break an otherwise successful run (next run treats it as a miss).
3391
+ await mergeStagedDurableParsedFileStore(storagePath, parseCache.storagePath ?? storagePath, PARSE_CACHE_VERSION, new Set(savedKeys));
3392
+ }
3393
+ catch (e) {
3394
+ log(`Warning: could not save parse cache (${e.message}); continuing.`);
3395
+ }
3089
3396
  progress('done', 100, 'Done');
3397
+ await removeColdParseRebuildDir(coldParseRebuildDir, true);
3090
3398
  return {
3091
3399
  repoName: projectName,
3092
3400
  repoPath,
@@ -3144,6 +3452,7 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
3144
3452
  /* swallow — orphan reclamation must never mask the real failure */
3145
3453
  }
3146
3454
  }
3455
+ await removeColdParseRebuildDir(coldParseRebuildDir, true);
3147
3456
  if (liveIndexMutationStarted) {
3148
3457
  // Preserve the original error identity/prototype: callers distinguish
3149
3458
  // IndexLockTimeoutError and other domain failures with `instanceof`.