gitnexus 1.6.6-rc.99 → 1.6.6

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 (541) hide show
  1. package/README.md +122 -68
  2. package/dist/_shared/graph/types.d.ts +17 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +3 -5
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/index.js +2 -4
  7. package/dist/_shared/index.js.map +1 -1
  8. package/dist/_shared/scope-resolution/module-scope-index.d.ts +1 -2
  9. package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
  10. package/dist/_shared/scope-resolution/module-scope-index.js +1 -2
  11. package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
  12. package/dist/_shared/scope-resolution/parsed-file.d.ts +24 -0
  13. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  14. package/dist/_shared/scope-resolution/reference-site.d.ts +13 -1
  15. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/registries/context.d.ts +1 -0
  17. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
  18. package/dist/_shared/scope-resolution/registries/context.js +6 -0
  19. package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
  20. package/dist/_shared/scope-resolution/registries/evidence.d.ts +2 -3
  21. package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -1
  22. package/dist/_shared/scope-resolution/registries/evidence.js +2 -3
  23. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -1
  24. package/dist/_shared/scope-resolution/registries/macro-registry.d.ts +29 -0
  25. package/dist/_shared/scope-resolution/registries/macro-registry.d.ts.map +1 -0
  26. package/dist/_shared/scope-resolution/registries/macro-registry.js +32 -0
  27. package/dist/_shared/scope-resolution/registries/macro-registry.js.map +1 -0
  28. package/dist/_shared/scope-resolution/symbol-definition.d.ts +9 -0
  29. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  30. package/dist/_shared/scope-resolution/types.d.ts +1 -1
  31. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  32. package/dist/cli/ai-context.d.ts +35 -1
  33. package/dist/cli/ai-context.js +111 -12
  34. package/dist/cli/analyze-config.d.ts +92 -0
  35. package/dist/cli/analyze-config.js +364 -0
  36. package/dist/cli/analyze.d.ts +17 -1
  37. package/dist/cli/analyze.js +224 -71
  38. package/dist/cli/cli-message.d.ts +1 -1
  39. package/dist/cli/doctor.d.ts +18 -0
  40. package/dist/cli/doctor.js +32 -0
  41. package/dist/cli/i18n/en.d.ts +2 -2
  42. package/dist/cli/i18n/en.js +2 -2
  43. package/dist/cli/i18n/resources.d.ts +2 -2
  44. package/dist/cli/i18n/zh-CN.js +2 -2
  45. package/dist/cli/index.js +4 -2
  46. package/dist/cli/resolve-invocation.d.ts +21 -0
  47. package/dist/cli/resolve-invocation.js +68 -0
  48. package/dist/cli/setup.d.ts +33 -0
  49. package/dist/cli/setup.js +96 -35
  50. package/dist/cli/skill-gen.js +2 -2
  51. package/dist/cli/wiki.js +20 -4
  52. package/dist/core/embeddings/embedder.d.ts +1 -1
  53. package/dist/core/embeddings/embedder.js +14 -1
  54. package/dist/core/embeddings/runtime-support.d.ts +41 -0
  55. package/dist/core/embeddings/runtime-support.js +65 -0
  56. package/dist/core/group/extractors/grpc-patterns/proto.js +2 -1
  57. package/dist/core/group/extractors/http-patterns/java.js +15 -1
  58. package/dist/core/ingestion/call-processor.d.ts +57 -227
  59. package/dist/core/ingestion/call-processor.js +172 -2595
  60. package/dist/core/ingestion/call-routing.d.ts +6 -5
  61. package/dist/core/ingestion/call-routing.js +6 -6
  62. package/dist/core/ingestion/call-types.d.ts +0 -75
  63. package/dist/core/ingestion/class-extractors/configs/c-cpp.js +26 -1
  64. package/dist/core/ingestion/class-extractors/configs/ruby.js +3 -0
  65. package/dist/core/ingestion/class-extractors/generic.js +9 -11
  66. package/dist/core/ingestion/class-types.d.ts +23 -0
  67. package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +11 -0
  68. package/dist/core/ingestion/cobol/cobol-preprocessor.js +227 -73
  69. package/dist/core/ingestion/cobol-processor.d.ts +1 -0
  70. package/dist/core/ingestion/cobol-processor.js +51 -2
  71. package/dist/core/ingestion/emit-references.js +8 -2
  72. package/dist/core/ingestion/export-detection.d.ts +2 -1
  73. package/dist/core/ingestion/export-detection.js +2 -6
  74. package/dist/core/ingestion/field-extractors/configs/dart.d.ts +0 -6
  75. package/dist/core/ingestion/field-extractors/configs/dart.js +62 -44
  76. package/dist/core/ingestion/field-extractors/configs/go.d.ts +0 -9
  77. package/dist/core/ingestion/field-extractors/configs/go.js +40 -1
  78. package/dist/core/ingestion/field-extractors/configs/jvm.js +43 -4
  79. package/dist/core/ingestion/field-extractors/configs/php.js +1 -2
  80. package/dist/core/ingestion/field-extractors/configs/swift.d.ts +12 -1
  81. package/dist/core/ingestion/field-extractors/configs/swift.js +31 -6
  82. package/dist/core/ingestion/field-extractors/generic.d.ts +6 -0
  83. package/dist/core/ingestion/field-extractors/generic.js +5 -4
  84. package/dist/core/ingestion/field-extractors/typescript.js +0 -29
  85. package/dist/core/ingestion/filesystem-walker.d.ts +0 -9
  86. package/dist/core/ingestion/filesystem-walker.js +0 -11
  87. package/dist/core/ingestion/finalize-orchestrator.js +14 -2
  88. package/dist/core/ingestion/import-resolvers/configs/swift.d.ts +21 -0
  89. package/dist/core/ingestion/import-resolvers/configs/swift.js +72 -11
  90. package/dist/core/ingestion/import-resolvers/python.d.ts +1 -1
  91. package/dist/core/ingestion/import-resolvers/standard.d.ts +2 -2
  92. package/dist/core/ingestion/import-resolvers/standard.js +6 -5
  93. package/dist/core/ingestion/import-resolvers/utils.d.ts +2 -5
  94. package/dist/core/ingestion/import-resolvers/utils.js +1 -9
  95. package/dist/core/ingestion/language-provider.d.ts +39 -119
  96. package/dist/core/ingestion/language-provider.js +0 -2
  97. package/dist/core/ingestion/languages/c/capture-side-channel.d.ts +62 -0
  98. package/dist/core/ingestion/languages/c/capture-side-channel.js +65 -0
  99. package/dist/core/ingestion/languages/c/captures.js +41 -23
  100. package/dist/core/ingestion/languages/c/import-decomposer.d.ts +9 -0
  101. package/dist/core/ingestion/languages/c/import-decomposer.js +15 -0
  102. package/dist/core/ingestion/languages/c/import-target.js +52 -9
  103. package/dist/core/ingestion/languages/c/index.d.ts +2 -1
  104. package/dist/core/ingestion/languages/c/index.js +2 -1
  105. package/dist/core/ingestion/languages/c/query.js +15 -0
  106. package/dist/core/ingestion/languages/c/scope-resolver.js +47 -4
  107. package/dist/core/ingestion/languages/c/static-linkage.d.ts +8 -0
  108. package/dist/core/ingestion/languages/c/static-linkage.js +46 -1
  109. package/dist/core/ingestion/languages/c-cpp.js +16 -7
  110. package/dist/core/ingestion/languages/cobol/captures.js +25 -2
  111. package/dist/core/ingestion/languages/cpp/adl.d.ts +88 -4
  112. package/dist/core/ingestion/languages/cpp/adl.js +380 -160
  113. package/dist/core/ingestion/languages/cpp/arity-metadata.js +2 -8
  114. package/dist/core/ingestion/languages/cpp/capture-side-channel.d.ts +65 -0
  115. package/dist/core/ingestion/languages/cpp/capture-side-channel.js +78 -0
  116. package/dist/core/ingestion/languages/cpp/captures.js +162 -54
  117. package/dist/core/ingestion/languages/cpp/file-local-linkage.d.ts +18 -52
  118. package/dist/core/ingestion/languages/cpp/file-local-linkage.js +49 -1
  119. package/dist/core/ingestion/languages/cpp/import-decomposer.d.ts +7 -0
  120. package/dist/core/ingestion/languages/cpp/import-decomposer.js +12 -0
  121. package/dist/core/ingestion/languages/cpp/index.d.ts +1 -0
  122. package/dist/core/ingestion/languages/cpp/index.js +1 -0
  123. package/dist/core/ingestion/languages/cpp/inline-namespaces.d.ts +8 -0
  124. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +21 -0
  125. package/dist/core/ingestion/languages/cpp/query.js +21 -0
  126. package/dist/core/ingestion/languages/cpp/range-bindings.js +20 -5
  127. package/dist/core/ingestion/languages/cpp/scope-resolver.js +52 -5
  128. package/dist/core/ingestion/languages/cpp/two-phase-lookup.d.ts +20 -0
  129. package/dist/core/ingestion/languages/cpp/two-phase-lookup.js +83 -3
  130. package/dist/core/ingestion/languages/csharp/captures.js +205 -41
  131. package/dist/core/ingestion/languages/csharp/index.d.ts +3 -4
  132. package/dist/core/ingestion/languages/csharp/index.js +3 -4
  133. package/dist/core/ingestion/languages/csharp/interpret.js +17 -3
  134. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +17 -7
  135. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +129 -124
  136. package/dist/core/ingestion/languages/csharp/qualified-type-names.d.ts +14 -0
  137. package/dist/core/ingestion/languages/csharp/qualified-type-names.js +59 -0
  138. package/dist/core/ingestion/languages/csharp/query.js +13 -0
  139. package/dist/core/ingestion/languages/csharp/receiver-binding.js +10 -6
  140. package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -1
  141. package/dist/core/ingestion/languages/csharp.js +0 -5
  142. package/dist/core/ingestion/languages/dart/arity-metadata.d.ts +25 -0
  143. package/dist/core/ingestion/languages/dart/arity-metadata.js +35 -0
  144. package/dist/core/ingestion/languages/dart/arity.d.ts +19 -0
  145. package/dist/core/ingestion/languages/dart/arity.js +31 -0
  146. package/dist/core/ingestion/languages/dart/built-ins.d.ts +11 -0
  147. package/dist/core/ingestion/languages/dart/built-ins.js +34 -0
  148. package/dist/core/ingestion/languages/dart/cache-stats.d.ts +13 -0
  149. package/dist/core/ingestion/languages/dart/cache-stats.js +24 -0
  150. package/dist/core/ingestion/languages/dart/captures.d.ts +29 -0
  151. package/dist/core/ingestion/languages/dart/captures.js +445 -0
  152. package/dist/core/ingestion/languages/dart/expand-wildcards.d.ts +13 -0
  153. package/dist/core/ingestion/languages/dart/expand-wildcards.js +31 -0
  154. package/dist/core/ingestion/languages/dart/import-target.d.ts +15 -0
  155. package/dist/core/ingestion/languages/dart/import-target.js +65 -0
  156. package/dist/core/ingestion/languages/dart/index.d.ts +28 -0
  157. package/dist/core/ingestion/languages/dart/index.js +28 -0
  158. package/dist/core/ingestion/languages/dart/interpret.d.ts +24 -0
  159. package/dist/core/ingestion/languages/dart/interpret.js +95 -0
  160. package/dist/core/ingestion/languages/dart/merge-bindings.d.ts +10 -0
  161. package/dist/core/ingestion/languages/dart/merge-bindings.js +39 -0
  162. package/dist/core/ingestion/languages/dart/query.d.ts +26 -0
  163. package/dist/core/ingestion/languages/dart/query.js +142 -0
  164. package/dist/core/ingestion/languages/dart/receiver-binding.d.ts +24 -0
  165. package/dist/core/ingestion/languages/dart/receiver-binding.js +87 -0
  166. package/dist/core/ingestion/languages/dart/scope-resolver.d.ts +29 -0
  167. package/dist/core/ingestion/languages/dart/scope-resolver.js +194 -0
  168. package/dist/core/ingestion/languages/dart/signature-bindings.d.ts +23 -0
  169. package/dist/core/ingestion/languages/dart/signature-bindings.js +51 -0
  170. package/dist/core/ingestion/languages/dart/simple-hooks.d.ts +20 -0
  171. package/dist/core/ingestion/languages/dart/simple-hooks.js +47 -0
  172. package/dist/core/ingestion/languages/dart.d.ts +0 -1
  173. package/dist/core/ingestion/languages/dart.js +15 -29
  174. package/dist/core/ingestion/languages/go/arity-metadata.d.ts +1 -0
  175. package/dist/core/ingestion/languages/go/arity-metadata.js +31 -1
  176. package/dist/core/ingestion/languages/go/captures.js +162 -3
  177. package/dist/core/ingestion/languages/go/interface-impls.js +476 -50
  178. package/dist/core/ingestion/languages/go/interpret.js +21 -11
  179. package/dist/core/ingestion/languages/go/method-owners.js +20 -8
  180. package/dist/core/ingestion/languages/go/query.js +15 -7
  181. package/dist/core/ingestion/languages/go/range-binding.js +24 -9
  182. package/dist/core/ingestion/languages/go/receiver-binding.js +1 -1
  183. package/dist/core/ingestion/languages/go/scope-resolver.js +2 -2
  184. package/dist/core/ingestion/languages/go/simple-hooks.js +10 -5
  185. package/dist/core/ingestion/languages/go/type-binding.d.ts +1 -0
  186. package/dist/core/ingestion/languages/go/type-binding.js +40 -2
  187. package/dist/core/ingestion/languages/go.d.ts +0 -1
  188. package/dist/core/ingestion/languages/go.js +0 -5
  189. package/dist/core/ingestion/languages/index.d.ts +0 -5
  190. package/dist/core/ingestion/languages/index.js +0 -3
  191. package/dist/core/ingestion/languages/java/captures.js +252 -23
  192. package/dist/core/ingestion/languages/java/interpret.js +12 -6
  193. package/dist/core/ingestion/languages/java/package-siblings.js +18 -5
  194. package/dist/core/ingestion/languages/java/query.js +16 -1
  195. package/dist/core/ingestion/languages/java/scope-resolver.d.ts +2 -8
  196. package/dist/core/ingestion/languages/java/scope-resolver.js +2 -8
  197. package/dist/core/ingestion/languages/java.js +0 -5
  198. package/dist/core/ingestion/languages/javascript/captures.js +185 -11
  199. package/dist/core/ingestion/languages/javascript/query.js +107 -1
  200. package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +57 -0
  201. package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +60 -0
  202. package/dist/core/ingestion/languages/kotlin/captures.js +213 -22
  203. package/dist/core/ingestion/languages/kotlin/companion-scopes.d.ts +7 -0
  204. package/dist/core/ingestion/languages/kotlin/companion-scopes.js +10 -0
  205. package/dist/core/ingestion/languages/kotlin/index.d.ts +1 -0
  206. package/dist/core/ingestion/languages/kotlin/index.js +1 -0
  207. package/dist/core/ingestion/languages/kotlin/query.js +30 -0
  208. package/dist/core/ingestion/languages/kotlin/scope-resolver.d.ts +5 -7
  209. package/dist/core/ingestion/languages/kotlin/scope-resolver.js +22 -7
  210. package/dist/core/ingestion/languages/kotlin.js +8 -5
  211. package/dist/core/ingestion/languages/php/captures.js +135 -19
  212. package/dist/core/ingestion/languages/php/import-decomposer.js +21 -63
  213. package/dist/core/ingestion/languages/php/index.d.ts +3 -4
  214. package/dist/core/ingestion/languages/php/index.js +3 -4
  215. package/dist/core/ingestion/languages/php/query.js +5 -0
  216. package/dist/core/ingestion/languages/php/receiver-binding.js +8 -4
  217. package/dist/core/ingestion/languages/php.js +0 -4
  218. package/dist/core/ingestion/languages/python/captures.js +128 -13
  219. package/dist/core/ingestion/languages/python/depends-references.js +1 -1
  220. package/dist/core/ingestion/languages/python/import-target.d.ts +4 -5
  221. package/dist/core/ingestion/languages/python/import-target.js +108 -16
  222. package/dist/core/ingestion/languages/python/index-stats.d.ts +18 -0
  223. package/dist/core/ingestion/languages/python/index-stats.js +25 -0
  224. package/dist/core/ingestion/languages/python/index.d.ts +3 -4
  225. package/dist/core/ingestion/languages/python/index.js +3 -4
  226. package/dist/core/ingestion/languages/python/query.js +17 -0
  227. package/dist/core/ingestion/languages/python/scope-resolver.js +7 -6
  228. package/dist/core/ingestion/languages/python.d.ts +0 -2
  229. package/dist/core/ingestion/languages/python.js +0 -7
  230. package/dist/core/ingestion/languages/ruby/captures.js +174 -31
  231. package/dist/core/ingestion/languages/ruby/import-target.js +2 -1
  232. package/dist/core/ingestion/languages/ruby/interpret.js +2 -1
  233. package/dist/core/ingestion/languages/ruby/query.js +10 -0
  234. package/dist/core/ingestion/languages/ruby/scope-resolver.js +61 -11
  235. package/dist/core/ingestion/languages/ruby.js +4 -53
  236. package/dist/core/ingestion/languages/rust/captures.js +95 -13
  237. package/dist/core/ingestion/languages/rust/query.js +64 -1
  238. package/dist/core/ingestion/languages/rust/range-binding.js +40 -12
  239. package/dist/core/ingestion/languages/rust/receiver-binding.js +9 -0
  240. package/dist/core/ingestion/languages/rust/scope-resolver.js +75 -1
  241. package/dist/core/ingestion/languages/rust.d.ts +0 -2
  242. package/dist/core/ingestion/languages/rust.js +0 -6
  243. package/dist/core/ingestion/languages/swift/arity-metadata.d.ts +24 -0
  244. package/dist/core/ingestion/languages/swift/arity-metadata.js +41 -0
  245. package/dist/core/ingestion/languages/swift/arity.d.ts +29 -0
  246. package/dist/core/ingestion/languages/swift/arity.js +42 -0
  247. package/dist/core/ingestion/languages/swift/base-type.d.ts +20 -0
  248. package/dist/core/ingestion/languages/swift/base-type.js +27 -0
  249. package/dist/core/ingestion/languages/swift/cache-stats.d.ts +15 -0
  250. package/dist/core/ingestion/languages/swift/cache-stats.js +26 -0
  251. package/dist/core/ingestion/languages/swift/captures.d.ts +35 -0
  252. package/dist/core/ingestion/languages/swift/captures.js +495 -0
  253. package/dist/core/ingestion/languages/swift/implicit-imports.d.ts +34 -0
  254. package/dist/core/ingestion/languages/swift/implicit-imports.js +57 -0
  255. package/dist/core/ingestion/languages/swift/import-decomposer.d.ts +24 -0
  256. package/dist/core/ingestion/languages/swift/import-decomposer.js +84 -0
  257. package/dist/core/ingestion/languages/swift/import-target.d.ts +33 -0
  258. package/dist/core/ingestion/languages/swift/import-target.js +81 -0
  259. package/dist/core/ingestion/languages/swift/index.d.ts +37 -0
  260. package/dist/core/ingestion/languages/swift/index.js +37 -0
  261. package/dist/core/ingestion/languages/swift/interpret.d.ts +15 -0
  262. package/dist/core/ingestion/languages/swift/interpret.js +89 -0
  263. package/dist/core/ingestion/languages/swift/merge-bindings.d.ts +20 -0
  264. package/dist/core/ingestion/languages/swift/merge-bindings.js +48 -0
  265. package/dist/core/ingestion/languages/swift/query.d.ts +46 -0
  266. package/dist/core/ingestion/languages/swift/query.js +194 -0
  267. package/dist/core/ingestion/languages/swift/receiver-binding.d.ts +40 -0
  268. package/dist/core/ingestion/languages/swift/receiver-binding.js +161 -0
  269. package/dist/core/ingestion/languages/swift/scope-resolver.d.ts +50 -0
  270. package/dist/core/ingestion/languages/swift/scope-resolver.js +189 -0
  271. package/dist/core/ingestion/languages/swift/sibling-type-bindings.d.ts +34 -0
  272. package/dist/core/ingestion/languages/swift/sibling-type-bindings.js +65 -0
  273. package/dist/core/ingestion/languages/swift/signature-bindings.d.ts +25 -0
  274. package/dist/core/ingestion/languages/swift/signature-bindings.js +66 -0
  275. package/dist/core/ingestion/languages/swift/simple-hooks.d.ts +37 -0
  276. package/dist/core/ingestion/languages/swift/simple-hooks.js +60 -0
  277. package/dist/core/ingestion/languages/swift/target-grouping.d.ts +51 -0
  278. package/dist/core/ingestion/languages/swift/target-grouping.js +93 -0
  279. package/dist/core/ingestion/languages/swift/target-siblings.d.ts +30 -0
  280. package/dist/core/ingestion/languages/swift/target-siblings.js +71 -0
  281. package/dist/core/ingestion/languages/swift.d.ts +0 -5
  282. package/dist/core/ingestion/languages/swift.js +11 -87
  283. package/dist/core/ingestion/languages/typescript/array-callback.d.ts +3 -29
  284. package/dist/core/ingestion/languages/typescript/array-callback.js +4 -43
  285. package/dist/core/ingestion/languages/typescript/captures.js +147 -17
  286. package/dist/core/ingestion/languages/typescript/index.d.ts +3 -4
  287. package/dist/core/ingestion/languages/typescript/index.js +3 -4
  288. package/dist/core/ingestion/languages/typescript/query.d.ts +2 -3
  289. package/dist/core/ingestion/languages/typescript/query.js +125 -4
  290. package/dist/core/ingestion/languages/typescript/receiver-binding.js +0 -1
  291. package/dist/core/ingestion/languages/typescript.d.ts +2 -3
  292. package/dist/core/ingestion/languages/typescript.js +46 -23
  293. package/dist/core/ingestion/languages/vue/captures.d.ts +46 -0
  294. package/dist/core/ingestion/languages/vue/captures.js +67 -0
  295. package/dist/core/ingestion/languages/vue/import-target.d.ts +27 -0
  296. package/dist/core/ingestion/languages/vue/import-target.js +58 -0
  297. package/dist/core/ingestion/languages/vue/index.d.ts +49 -0
  298. package/dist/core/ingestion/languages/vue/index.js +49 -0
  299. package/dist/core/ingestion/languages/vue/scope-resolver.d.ts +55 -0
  300. package/dist/core/ingestion/languages/vue/scope-resolver.js +292 -0
  301. package/dist/core/ingestion/languages/vue.js +12 -4
  302. package/dist/core/ingestion/local-symbol-pruner.d.ts +11 -0
  303. package/dist/core/ingestion/local-symbol-pruner.js +63 -0
  304. package/dist/core/ingestion/method-extractors/configs/dart.js +14 -1
  305. package/dist/core/ingestion/method-extractors/configs/jvm.js +37 -20
  306. package/dist/core/ingestion/method-extractors/configs/php.js +0 -1
  307. package/dist/core/ingestion/method-extractors/configs/swift.js +11 -4
  308. package/dist/core/ingestion/method-types.d.ts +2 -2
  309. package/dist/core/ingestion/model/index.d.ts +0 -3
  310. package/dist/core/ingestion/model/index.js +0 -8
  311. package/dist/core/ingestion/model/resolve.d.ts +7 -43
  312. package/dist/core/ingestion/model/resolve.js +7 -226
  313. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +46 -1
  314. package/dist/core/ingestion/model/semantic-model.d.ts +4 -6
  315. package/dist/core/ingestion/model/semantic-model.js +1 -1
  316. package/dist/core/ingestion/model/symbol-table.d.ts +3 -4
  317. package/dist/core/ingestion/model/symbol-table.js +3 -4
  318. package/dist/core/ingestion/parsing-processor.d.ts +30 -24
  319. package/dist/core/ingestion/parsing-processor.js +76 -640
  320. package/dist/core/ingestion/pipeline-phases/communities.d.ts +1 -1
  321. package/dist/core/ingestion/pipeline-phases/communities.js +5 -2
  322. package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +9 -8
  323. package/dist/core/ingestion/pipeline-phases/cross-file.js +16 -12
  324. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  325. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  326. package/dist/core/ingestion/pipeline-phases/mro.d.ts +1 -1
  327. package/dist/core/ingestion/pipeline-phases/mro.js +2 -2
  328. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +44 -22
  329. package/dist/core/ingestion/pipeline-phases/parse-impl.js +426 -565
  330. package/dist/core/ingestion/pipeline-phases/parse.d.ts +13 -33
  331. package/dist/core/ingestion/pipeline-phases/parse.js +2 -2
  332. package/dist/core/ingestion/pipeline-phases/processes.d.ts +1 -1
  333. package/dist/core/ingestion/pipeline-phases/processes.js +5 -3
  334. package/dist/core/ingestion/pipeline-phases/prune-local-symbols.d.ts +14 -0
  335. package/dist/core/ingestion/pipeline-phases/prune-local-symbols.js +26 -0
  336. package/dist/core/ingestion/pipeline.d.ts +27 -19
  337. package/dist/core/ingestion/pipeline.js +4 -2
  338. package/dist/core/ingestion/resolve-references.js +15 -3
  339. package/dist/core/ingestion/route-extractors/laravel.d.ts +10 -0
  340. package/dist/core/ingestion/route-extractors/laravel.js +88 -9
  341. package/dist/core/ingestion/scope-extractor-bridge.d.ts +2 -1
  342. package/dist/core/ingestion/scope-extractor-bridge.js +2 -2
  343. package/dist/core/ingestion/scope-extractor.js +13 -0
  344. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +181 -15
  345. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +21 -10
  346. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +10 -2
  347. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +7 -1
  348. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +58 -6
  349. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +24 -5
  350. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +68 -1
  351. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +189 -29
  352. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +21 -1
  353. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +1 -1
  354. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +8 -11
  355. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +171 -50
  356. package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +3 -4
  357. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +9 -4
  358. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +28 -5
  359. package/dist/core/ingestion/scope-resolution/pipeline/run.js +191 -24
  360. package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +31 -0
  361. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +72 -0
  362. package/dist/core/ingestion/scope-resolution/scope/walkers.js +440 -39
  363. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +18 -2
  364. package/dist/core/ingestion/scope-resolution/workspace-index.js +94 -7
  365. package/dist/core/ingestion/tree-sitter-queries.d.ts +14 -14
  366. package/dist/core/ingestion/tree-sitter-queries.js +380 -158
  367. package/dist/core/ingestion/ts-js-hoc-utils.d.ts +12 -0
  368. package/dist/core/ingestion/ts-js-hoc-utils.js +105 -0
  369. package/dist/core/ingestion/type-env.d.ts +2 -1
  370. package/dist/core/ingestion/type-env.js +6 -8
  371. package/dist/core/ingestion/type-extractors/c-cpp.js +8 -10
  372. package/dist/core/ingestion/type-extractors/csharp.js +18 -34
  373. package/dist/core/ingestion/type-extractors/go.js +2 -10
  374. package/dist/core/ingestion/type-extractors/jvm.js +6 -5
  375. package/dist/core/ingestion/type-extractors/php.js +1 -1
  376. package/dist/core/ingestion/type-extractors/python.js +1 -1
  377. package/dist/core/ingestion/type-extractors/ruby.js +2 -2
  378. package/dist/core/ingestion/type-extractors/rust.js +0 -16
  379. package/dist/core/ingestion/type-extractors/shared.d.ts +2 -2
  380. package/dist/core/ingestion/type-extractors/shared.js +25 -11
  381. package/dist/core/ingestion/type-extractors/swift.js +5 -5
  382. package/dist/core/ingestion/type-extractors/types.d.ts +1 -1
  383. package/dist/core/ingestion/type-extractors/typescript.js +1 -1
  384. package/dist/core/ingestion/utils/ast-helpers.d.ts +119 -1
  385. package/dist/core/ingestion/utils/ast-helpers.js +260 -13
  386. package/dist/core/ingestion/utils/callable-labels.d.ts +13 -0
  387. package/dist/core/ingestion/utils/callable-labels.js +14 -0
  388. package/dist/core/ingestion/utils/deferred-resolution-profile.d.ts +10 -0
  389. package/dist/core/ingestion/utils/deferred-resolution-profile.js +30 -0
  390. package/dist/core/ingestion/utils/heap-probe.d.ts +20 -0
  391. package/dist/core/ingestion/utils/heap-probe.js +39 -0
  392. package/dist/core/ingestion/utils/heritage-marker.d.ts +33 -0
  393. package/dist/core/ingestion/utils/heritage-marker.js +41 -0
  394. package/dist/core/ingestion/utils/qualified-name.d.ts +37 -0
  395. package/dist/core/ingestion/utils/qualified-name.js +67 -0
  396. package/dist/core/ingestion/variable-extractors/configs/c-cpp.js +49 -0
  397. package/dist/core/ingestion/variable-extractors/configs/dart.js +76 -62
  398. package/dist/core/ingestion/variable-extractors/configs/go.js +74 -7
  399. package/dist/core/ingestion/variable-extractors/configs/jvm.js +35 -8
  400. package/dist/core/ingestion/variable-extractors/generic.js +40 -12
  401. package/dist/core/ingestion/variable-types.d.ts +9 -0
  402. package/dist/core/ingestion/vue-sfc-extractor.d.ts +97 -7
  403. package/dist/core/ingestion/vue-sfc-extractor.js +433 -11
  404. package/dist/core/ingestion/workers/parse-worker.d.ts +0 -11
  405. package/dist/core/ingestion/workers/parse-worker.js +358 -187
  406. package/dist/core/ingestion/workers/worker-pool.d.ts +78 -9
  407. package/dist/core/ingestion/workers/worker-pool.js +346 -49
  408. package/dist/core/lbug/csv-generator.js +20 -2
  409. package/dist/core/lbug/lbug-adapter.js +29 -10
  410. package/dist/core/lbug/native-check.js +17 -5
  411. package/dist/core/lbug/pool-adapter.d.ts +0 -19
  412. package/dist/core/lbug/pool-adapter.js +40 -77
  413. package/dist/core/lbug/query-result-utils.d.ts +19 -0
  414. package/dist/core/lbug/query-result-utils.js +28 -0
  415. package/dist/core/run-analyze.d.ts +9 -2
  416. package/dist/core/run-analyze.js +24 -3
  417. package/dist/core/tree-sitter/parser-loader.d.ts +13 -0
  418. package/dist/core/tree-sitter/parser-loader.js +4 -0
  419. package/dist/core/tree-sitter/safe-parse.d.ts +70 -3
  420. package/dist/core/tree-sitter/safe-parse.js +214 -11
  421. package/dist/core/wiki/generator.js +13 -5
  422. package/dist/core/wiki/graph-queries.d.ts +3 -0
  423. package/dist/core/wiki/graph-queries.js +9 -1
  424. package/dist/core/wiki/llm-client.d.ts +1 -1
  425. package/dist/core/wiki/llm-client.js +7 -2
  426. package/dist/core/wiki/local-cli-client.d.ts +2 -1
  427. package/dist/core/wiki/local-cli-client.js +62 -4
  428. package/dist/mcp/compatible-stdio-transport.d.ts +3 -0
  429. package/dist/mcp/compatible-stdio-transport.js +22 -0
  430. package/dist/mcp/core/embedder.d.ts +1 -1
  431. package/dist/mcp/core/embedder.js +13 -1
  432. package/dist/mcp/local/local-backend.d.ts +37 -5
  433. package/dist/mcp/local/local-backend.js +227 -129
  434. package/dist/mcp/resources.js +3 -3
  435. package/dist/mcp/server.js +9 -1
  436. package/dist/storage/git.d.ts +11 -0
  437. package/dist/storage/git.js +30 -0
  438. package/dist/storage/parse-cache.d.ts +23 -1
  439. package/dist/storage/parse-cache.js +138 -32
  440. package/dist/storage/parsedfile-store.d.ts +142 -0
  441. package/dist/storage/parsedfile-store.js +367 -0
  442. package/dist/storage/repo-manager.d.ts +2 -1
  443. package/dist/storage/scope-index-store.d.ts +114 -0
  444. package/dist/storage/scope-index-store.js +236 -0
  445. package/dist/types/pipeline.d.ts +6 -3
  446. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +2 -1
  447. package/hooks/claude/gitnexus-hook.cjs +2 -1
  448. package/hooks/claude/resolve-analyze-cmd.cjs +323 -0
  449. package/package.json +4 -5
  450. package/scripts/bench-scope-resolution.ts +1 -3
  451. package/scripts/cross-platform-tests.ts +4 -1
  452. package/skills/gitnexus-cli.md +8 -6
  453. package/skills/gitnexus-debugging.md +15 -15
  454. package/skills/gitnexus-exploring.md +11 -11
  455. package/skills/gitnexus-guide.md +1 -1
  456. package/skills/gitnexus-impact-analysis.md +10 -10
  457. package/skills/gitnexus-pr-review.md +19 -19
  458. package/skills/gitnexus-refactoring.md +25 -25
  459. package/web/assets/{agent-BRX-afm3.js → agent-EkX7webX.js} +156 -131
  460. package/web/assets/{architectureDiagram-UL44E2DR-B1oYB8-c.js → architectureDiagram-UL44E2DR-BoczH8cv.js} +1 -1
  461. package/web/assets/{chunk-LCXTWHL2-SPC6pgWI.js → chunk-LCXTWHL2-By5HB3b-.js} +1 -1
  462. package/web/assets/{chunk-RG4AUYOV-DZXr4Yv3.js → chunk-RG4AUYOV-BQE_DkpL.js} +1 -1
  463. package/web/assets/{classDiagram-KGZ6W3CR-BqpOj8L9.js → classDiagram-KGZ6W3CR-Dj_02GUH.js} +1 -1
  464. package/web/assets/{classDiagram-v2-72OJOZXJ-FmElywK2.js → classDiagram-v2-72OJOZXJ-D-NArMpf.js} +1 -1
  465. package/web/assets/{diagram-3NCE3AQN-BWtJXMDq.js → diagram-3NCE3AQN-VXtXDO95.js} +1 -1
  466. package/web/assets/{diagram-GF46GFSD-Hh-fChxU.js → diagram-GF46GFSD-B1qab2Ah.js} +1 -1
  467. package/web/assets/{diagram-QXG6HAR7-DYeQHxGU.js → diagram-QXG6HAR7-BSCHGzzT.js} +1 -1
  468. package/web/assets/{diagram-WEQXMOUZ-DYGU4T0y.js → diagram-WEQXMOUZ-DHtknmNT.js} +1 -1
  469. package/web/assets/{erDiagram-L5TCEMPS-bN3wlP2A.js → erDiagram-L5TCEMPS-Db-IYTmm.js} +1 -1
  470. package/web/assets/{flowDiagram-H6V6AXG4-YACVtF3-.js → flowDiagram-H6V6AXG4-BHDoyVoW.js} +1 -1
  471. package/web/assets/{index-BhnAdWTt.js → index-BR0IhgbR.js} +10 -10
  472. package/web/assets/{infoDiagram-3YFTVSEB-BWBzD28-.js → infoDiagram-3YFTVSEB-6h8wbWEe.js} +1 -1
  473. package/web/assets/{ishikawaDiagram-BNXS4ZKH-B8qYT-j9.js → ishikawaDiagram-BNXS4ZKH-Dx2OkPk1.js} +1 -1
  474. package/web/assets/{kanban-definition-75IXJCU3-BeoPr5Tl.js → kanban-definition-75IXJCU3-CIIEZfqV.js} +1 -1
  475. package/web/assets/{mindmap-definition-2TDM6QVE-CdLLN_so.js → mindmap-definition-2TDM6QVE-DUAqydRq.js} +1 -1
  476. package/web/assets/{pieDiagram-CU6KROY3-DEd2a_k_.js → pieDiagram-CU6KROY3-BntYuYZa.js} +1 -1
  477. package/web/assets/{requirementDiagram-JXO7QTGE-QNeTo5C_.js → requirementDiagram-JXO7QTGE-QCD6I29n.js} +1 -1
  478. package/web/assets/{sequenceDiagram-VS2MUI6T-BHC7q7yJ.js → sequenceDiagram-VS2MUI6T-DScFwLz2.js} +1 -1
  479. package/web/assets/{stateDiagram-7D4R322I-KONnc2Px.js → stateDiagram-7D4R322I-AkRAm6Hl.js} +1 -1
  480. package/web/assets/{stateDiagram-v2-36443NZ5-C2QfuMlu.js → stateDiagram-v2-36443NZ5-BCK1vigR.js} +1 -1
  481. package/web/assets/{timeline-definition-O6YCAMPW-D4t4NJx1.js → timeline-definition-O6YCAMPW-BvpIVXjX.js} +1 -1
  482. package/web/assets/{vennDiagram-MWXL3ELB-6bctC9Yg.js → vennDiagram-MWXL3ELB-BYGWk6dK.js} +1 -1
  483. package/web/assets/{wardleyDiagram-CUQ6CDDI-BPZYNTvC.js → wardleyDiagram-CUQ6CDDI-Dk1z7H2T.js} +1 -1
  484. package/web/assets/{xychartDiagram-N2JHSOCM-lBI9O6DW.js → xychartDiagram-N2JHSOCM-2N6NCSWN.js} +1 -1
  485. package/web/index.html +1 -1
  486. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +0 -63
  487. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +0 -1
  488. package/dist/_shared/scope-resolution/shadow/aggregate.js +0 -122
  489. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +0 -1
  490. package/dist/_shared/scope-resolution/shadow/diff.d.ts +0 -59
  491. package/dist/_shared/scope-resolution/shadow/diff.d.ts.map +0 -1
  492. package/dist/_shared/scope-resolution/shadow/diff.js +0 -79
  493. package/dist/_shared/scope-resolution/shadow/diff.js.map +0 -1
  494. package/dist/core/ingestion/ast-cache.d.ts +0 -26
  495. package/dist/core/ingestion/ast-cache.js +0 -48
  496. package/dist/core/ingestion/heritage-extractors/configs/go.d.ts +0 -13
  497. package/dist/core/ingestion/heritage-extractors/configs/go.js +0 -20
  498. package/dist/core/ingestion/heritage-extractors/configs/ruby.d.ts +0 -18
  499. package/dist/core/ingestion/heritage-extractors/configs/ruby.js +0 -65
  500. package/dist/core/ingestion/heritage-extractors/generic.d.ts +0 -23
  501. package/dist/core/ingestion/heritage-extractors/generic.js +0 -47
  502. package/dist/core/ingestion/heritage-processor.d.ts +0 -54
  503. package/dist/core/ingestion/heritage-processor.js +0 -367
  504. package/dist/core/ingestion/heritage-types.d.ts +0 -73
  505. package/dist/core/ingestion/heritage-types.js +0 -2
  506. package/dist/core/ingestion/import-processor.d.ts +0 -23
  507. package/dist/core/ingestion/import-processor.js +0 -377
  508. package/dist/core/ingestion/model/heritage-map.d.ts +0 -105
  509. package/dist/core/ingestion/model/heritage-map.js +0 -294
  510. package/dist/core/ingestion/model/resolution-context.d.ts +0 -93
  511. package/dist/core/ingestion/model/resolution-context.js +0 -337
  512. package/dist/core/ingestion/named-bindings/csharp.d.ts +0 -3
  513. package/dist/core/ingestion/named-bindings/csharp.js +0 -37
  514. package/dist/core/ingestion/named-bindings/java.d.ts +0 -3
  515. package/dist/core/ingestion/named-bindings/java.js +0 -29
  516. package/dist/core/ingestion/named-bindings/kotlin.d.ts +0 -3
  517. package/dist/core/ingestion/named-bindings/kotlin.js +0 -36
  518. package/dist/core/ingestion/named-bindings/php.d.ts +0 -3
  519. package/dist/core/ingestion/named-bindings/php.js +0 -61
  520. package/dist/core/ingestion/named-bindings/python.d.ts +0 -3
  521. package/dist/core/ingestion/named-bindings/python.js +0 -49
  522. package/dist/core/ingestion/named-bindings/rust.d.ts +0 -3
  523. package/dist/core/ingestion/named-bindings/rust.js +0 -66
  524. package/dist/core/ingestion/named-bindings/types.d.ts +0 -16
  525. package/dist/core/ingestion/named-bindings/types.js +0 -6
  526. package/dist/core/ingestion/named-bindings/typescript.d.ts +0 -3
  527. package/dist/core/ingestion/named-bindings/typescript.js +0 -58
  528. package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +0 -17
  529. package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +0 -209
  530. package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +0 -22
  531. package/dist/core/ingestion/pipeline-phases/orm-extraction.js +0 -92
  532. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +0 -70
  533. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +0 -312
  534. package/dist/core/ingestion/registry-primary-flag.d.ts +0 -89
  535. package/dist/core/ingestion/registry-primary-flag.js +0 -122
  536. package/dist/core/ingestion/shadow-harness.d.ts +0 -113
  537. package/dist/core/ingestion/shadow-harness.js +0 -148
  538. package/dist/core/ingestion/utils/ruby-self-call.d.ts +0 -52
  539. package/dist/core/ingestion/utils/ruby-self-call.js +0 -59
  540. package/scripts/ci-list-migrated-languages.ts +0 -24
  541. package/scripts/run-parity.ts +0 -128
@@ -79,10 +79,29 @@ export class WorkerPoolDispatchError extends Error {
79
79
  }
80
80
  export class WorkerPoolInitializationError extends WorkerPoolDispatchError {
81
81
  readinessFailures;
82
- constructor(message, quarantinedPaths = [], readinessFailures = []) {
82
+ /** Pool's automatic classification of the startup crash (#1741). */
83
+ crashClass;
84
+ constructor(message, quarantinedPaths = [], readinessFailures = [], crashClass = 'transient-exhausted') {
83
85
  super(message, quarantinedPaths);
84
86
  this.name = 'WorkerPoolInitializationError';
85
87
  this.readinessFailures = readinessFailures;
88
+ this.crashClass = crashClass;
89
+ }
90
+ }
91
+ /**
92
+ * Thrown when a caller asks GitNexus to parse without the worker pool —
93
+ * `--workers 0`, `GITNEXUS_WORKER_POOL_SIZE=0`, or `skipWorkers: true`.
94
+ *
95
+ * GitNexus no longer has a sequential parser: the worker pool (with its
96
+ * quarantine + respawn/recycle + circuit-breaker resilience) is the SOLE
97
+ * parse path. These channels used to select an in-process fallback; they are
98
+ * now hard configuration errors so the operator gets an actionable message
99
+ * instead of silently parsing through a (deleted) slower path.
100
+ */
101
+ export class WorkerPoolDisabledError extends Error {
102
+ constructor(message) {
103
+ super(message);
104
+ this.name = 'WorkerPoolDisabledError';
86
105
  }
87
106
  }
88
107
  /**
@@ -117,6 +136,89 @@ const WORKER_READY_TIMEOUT_MS = 5_000;
117
136
  * `GITNEXUS_WORKER_POOL_SIZE` or `--workers <N>`.
118
137
  */
119
138
  const DEFAULT_POOL_SIZE_CAP = 16;
139
+ // ── Self-healing startup restart policy (#1741) ──────────────────────────────
140
+ // A worker that crashes during top-of-script init (broken native binding, bad
141
+ // import) is retried a BOUNDED number of times with jittered backoff before
142
+ // its slot is dropped, so a transient blip self-heals with no operator
143
+ // intervention. The bound is the whole point of #1741: recovery must never
144
+ // become a silent, unbounded "stuck" run. When the budget is exhausted (or a
145
+ // deterministic crash-loop is detected), the slot is dropped; if every slot is
146
+ // dropped the first dispatch fails fast with the captured cause.
147
+ /** Retries beyond the first attempt, per slot, to bring a startup worker ready. */
148
+ const STARTUP_RESTART_BUDGET = 2;
149
+ const RESTART_BACKOFF_BASE_MS = 250;
150
+ const RESTART_BACKOFF_CAP_MS = 2_000;
151
+ /**
152
+ * When this many freshly-spawned workers crash with the SAME crash signature
153
+ * before ANY worker reaches the `{type:'ready'}` handshake, the failure is
154
+ * deterministic (the #1741 missing-binding case: every worker prints a
155
+ * byte-identical native-binding stack). The pool stops retrying immediately
156
+ * instead of burning every slot's budget, and fails fast with the cause.
157
+ */
158
+ const DETERMINISTIC_STARTUP_FINGERPRINT_THRESHOLD = 2;
159
+ /**
160
+ * Capped exponential backoff with FULL jitter (AWS "Exponential Backoff And
161
+ * Jitter"): random(0, min(CAP, BASE·2^attempt)). Full jitter de-synchronizes
162
+ * the N workers that crash near-simultaneously on a shared startup fault so
163
+ * their respawns don't re-storm in lockstep (Google SRE thundering herd).
164
+ */
165
+ function startupBackoffMs(attempt) {
166
+ const ceil = Math.min(RESTART_BACKOFF_CAP_MS, RESTART_BACKOFF_BASE_MS * 2 ** attempt);
167
+ return Math.floor(Math.random() * (ceil + 1));
168
+ }
169
+ /**
170
+ * Sleep used between startup self-heal retries. The timer is intentionally NOT
171
+ * `unref`'d: a pending retry is necessary work, so it must keep the event loop
172
+ * alive long enough to actually respawn — otherwise a pool whose only live work
173
+ * is a startup backoff could let the process exit mid-recovery (#1741). To
174
+ * avoid wedging shutdown, the timer registers a cancel function in `pending`;
175
+ * `terminate()` invokes those cancels to `clearTimeout` and resolve early, and
176
+ * a normally-fired timer removes its own cancel. `aborted()` is checked once up
177
+ * front; the CALLER re-checks after wake (it owns the terminated/deterministic
178
+ * decision) — this function does not itself re-evaluate abort on wake.
179
+ */
180
+ function abortableSleep(ms, aborted, pending) {
181
+ return new Promise((resolve) => {
182
+ if (ms <= 0 || aborted()) {
183
+ resolve();
184
+ return;
185
+ }
186
+ // `cancel` is registered so terminate() can clear a pending backoff; it is
187
+ // also the timer's own callback, so a normally-fired sleep self-deregisters.
188
+ const cancel = () => {
189
+ clearTimeout(timer);
190
+ pending.delete(cancel);
191
+ resolve();
192
+ };
193
+ const timer = setTimeout(cancel, ms);
194
+ pending.add(cancel);
195
+ });
196
+ }
197
+ /**
198
+ * Normalize a worker crash message into a stable signature so two instances of
199
+ * the SAME deterministic crash compare equal while unrelated crashes don't.
200
+ * Strips hex addresses, digit runs (pids / line numbers / timestamps) and
201
+ * absolute paths. Best-effort by design: the deterministic classification's
202
+ * correctness rests on the STRUCTURAL signal (zero workers ever ready + startup
203
+ * budget exhausted), so an imperfect signature only changes how fast the
204
+ * short-circuit fires, never whether the pool ultimately fails fast. Even a
205
+ * stderr-less crash normalizes its "exited with code N" message to a stable
206
+ * key, so the empty-stderr timing case still groups.
207
+ *
208
+ * @internal Exported for unit tests; production callers are in this module.
209
+ */
210
+ export function crashSignature(message) {
211
+ return (message
212
+ .replace(/0x[0-9a-fA-F]+/g, '0xADDR') // 0x-prefixed addresses
213
+ // Windows backslash paths (optional drive letter), e.g. C:\Users\ci\Temp\w-7f3a.js
214
+ .replace(/(?:[A-Za-z]:)?(?:\\[^\s\\'"]+)+/g, '\\PATH')
215
+ .replace(/(?:\/[^\s:'"]+)+/g, '/PATH') // POSIX paths
216
+ .replace(/\b[0-9a-fA-F]{6,}\b/g, 'HEX') // bare hex runs (ASLR addrs / backtrace tokens)
217
+ .replace(/[0-9]+/g, 'N') // pids / line numbers / exit codes / timestamps
218
+ .replace(/\s+/g, ' ')
219
+ .trim()
220
+ .slice(0, 300));
221
+ }
120
222
  function positiveInteger(value) {
121
223
  const parsed = typeof value === 'string' ? Number(value) : value;
122
224
  return typeof parsed === 'number' && Number.isFinite(parsed) && parsed > 0
@@ -152,12 +254,37 @@ export function resolveWorkerPoolOptions(options = {}, poolSize) {
152
254
  Math.max(DEFAULT_CONSECUTIVE_FAILURE_THRESHOLD_FLOOR, poolSize ?? 0),
153
255
  };
154
256
  }
257
+ /**
258
+ * The pool size requested via the `GITNEXUS_WORKER_POOL_SIZE` env var, or
259
+ * `undefined` when unset, empty/whitespace, or invalid. Module-internal sizing
260
+ * reader consumed by {@link resolveAutoPoolSize} (the env override) and
261
+ * {@link workerPoolDisabledByEnv} (the disabled-channel check). Reads only —
262
+ * never mutates `process.env`. Empty/whitespace is treated as *unset* (falls
263
+ * through to the auto formula), not as 0 — an empty assignment (`export
264
+ * GITNEXUS_WORKER_POOL_SIZE=`) is an accident, not a request for zero workers;
265
+ * only a literal `0` disables the pool.
266
+ */
267
+ function envWorkerPoolSize() {
268
+ const raw = process.env.GITNEXUS_WORKER_POOL_SIZE;
269
+ if (raw === undefined || raw.trim() === '')
270
+ return undefined;
271
+ return nonNegativeInteger(raw);
272
+ }
273
+ /**
274
+ * True when the operator set `GITNEXUS_WORKER_POOL_SIZE=0` — the env-channel
275
+ * equivalent of `--workers 0`. The parse phase consults this (only when no
276
+ * explicit `--workers <N>` was passed) and HARD-ERRORS: sequential parsing was
277
+ * removed, so a disabled pool is an actionable configuration error, not a
278
+ * silent fallback. An explicit positive `--workers N` always wins.
279
+ */
280
+ export function workerPoolDisabledByEnv() {
281
+ return envWorkerPoolSize() === 0;
282
+ }
155
283
  /**
156
284
  * Resolve the auto-default worker pool size when no explicit `poolSize`
157
285
  * arg is passed to `createWorkerPool`. Precedence:
158
286
  *
159
- * 1. `GITNEXUS_WORKER_POOL_SIZE` env var (operator override; set by
160
- * `--workers <N>` on the CLI).
287
+ * 1. `GITNEXUS_WORKER_POOL_SIZE` env var (operator override).
161
288
  * 2. `os.cpus().length - 1`, clamped to `[1, DEFAULT_POOL_SIZE_CAP]`.
162
289
  *
163
290
  * The cap exists because past ~16 workers the main-thread merge /
@@ -170,7 +297,7 @@ export function resolveWorkerPoolOptions(options = {}, poolSize) {
170
297
  * on the env / default.
171
298
  */
172
299
  export function resolveAutoPoolSize() {
173
- const envOverride = nonNegativeInteger(process.env.GITNEXUS_WORKER_POOL_SIZE);
300
+ const envOverride = envWorkerPoolSize();
174
301
  if (envOverride !== undefined)
175
302
  return envOverride;
176
303
  // Prefer os.availableParallelism (Node 18.14+) so cgroup CPU limits
@@ -184,6 +311,69 @@ export function resolveAutoPoolSize() {
184
311
  const cores = typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length;
185
312
  return Math.min(DEFAULT_POOL_SIZE_CAP, Math.max(1, cores - 1));
186
313
  }
314
+ /**
315
+ * Max characters of a worker's stderr retained for crash diagnostics. A
316
+ * native-binding load failure or a top-of-script throw prints a stack to
317
+ * stderr; we keep the tail so `waitForWorkerReady` can attach the real
318
+ * reason to its rejection instead of the generic "did not report ready".
319
+ */
320
+ const WORKER_STDERR_TAIL_LIMIT = 4000;
321
+ /**
322
+ * Per-worker captured stderr tail. Populated only for workers spawned with
323
+ * `{ stderr: true }` (the production factory below). Test-injected workers
324
+ * via `workerFactory` typically inherit the parent's stderr and have no
325
+ * `worker.stderr` stream — those are simply skipped (empty tail). A WeakMap
326
+ * so the buffer is released when the worker is GC'd.
327
+ */
328
+ const workerStderrTails = new WeakMap();
329
+ /**
330
+ * Tee a worker's stderr into a bounded in-memory tail (for surfacing the
331
+ * real crash on a startup failure) while still mirroring it to the parent
332
+ * process's stderr — preserving the live-diagnostics behavior workers had
333
+ * when they inherited stderr, before `{ stderr: true }` redirected it to a
334
+ * stream. No-op when the worker has no `stderr` stream (test factories).
335
+ */
336
+ function captureWorkerStderr(worker) {
337
+ const stream = worker.stderr;
338
+ if (!stream)
339
+ return;
340
+ const buf = { text: '' };
341
+ workerStderrTails.set(worker, buf);
342
+ stream.on('data', (chunk) => {
343
+ const s = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
344
+ process.stderr.write(s);
345
+ buf.text = (buf.text + s).slice(-WORKER_STDERR_TAIL_LIMIT);
346
+ });
347
+ // A stderr stream error must never crash the pool.
348
+ stream.on('error', () => undefined);
349
+ }
350
+ /** Captured stderr tail for a worker, trimmed; '' when nothing was captured. */
351
+ function workerStderrTail(worker) {
352
+ return workerStderrTails.get(worker)?.text.trim() ?? '';
353
+ }
354
+ /** Append the worker's captured stderr to a readiness-failure message. */
355
+ function withStderr(worker, message) {
356
+ const tail = workerStderrTail(worker);
357
+ return tail ? `${message}. Worker stderr:\n${tail}` : message;
358
+ }
359
+ /**
360
+ * Build a worker-death reason string that carries the worker-side stack when one
361
+ * is available (#2068). The stack is appended AFTER the `Worker N error: <msg>`
362
+ * prefix so every prefix/substring consumer downstream — recoverAndResume →
363
+ * handleWorkerDeath → the circuit-breaker `WorkerPoolDispatchError` message, and
364
+ * the tests that regex-match those — keeps working unchanged, while the operator
365
+ * now gets the real frame instead of a bare one-liner. The stack's first line is
366
+ * normally the message itself; keeping both is harmless and the indented block
367
+ * scans cleanly in a log. The stack is capped at WORKER_STDERR_TAIL_LIMIT,
368
+ * mirroring the sibling stderr-tail bound, so a pathological error type (or a
369
+ * raised `Error.stackTraceLimit`) can't bloat the death reason. `stack` is
370
+ * `undefined` for an older worker build (or a thrown non-Error), in which case
371
+ * the reason is exactly the prior message-only form.
372
+ */
373
+ function workerErrorReason(workerIndex, message, stack) {
374
+ const base = `Worker ${workerIndex} error: ${message}`;
375
+ return stack ? `${base}\n worker stack:\n${stack.slice(0, WORKER_STDERR_TAIL_LIMIT)}` : base;
376
+ }
187
377
  /**
188
378
  * Wait for a freshly-spawned replacement worker to emit the
189
379
  * `{type:'ready'}` handshake from `parse-worker.ts` before treating its
@@ -220,22 +410,24 @@ function waitForWorkerReady(worker) {
220
410
  };
221
411
  const onError = (err) => {
222
412
  cleanup();
223
- reject(err);
413
+ // The 'error' event carries the real top-of-script exception; enrich it
414
+ // with the worker's stderr tail (native-binding stacks land there).
415
+ reject(new Error(withStderr(worker, err.message)));
224
416
  };
225
417
  const onExit = (code) => {
226
418
  cleanup();
227
- reject(new Error(`Replacement worker exited with code ${code} before reporting ready`));
419
+ reject(new Error(withStderr(worker, `Replacement worker exited with code ${code} before reporting ready`)));
228
420
  };
229
421
  const onMessageError = (err) => {
230
422
  cleanup();
231
- reject(new Error(`Replacement worker emitted messageerror before reporting ready: ${err.message}`));
423
+ reject(new Error(withStderr(worker, `Replacement worker emitted messageerror before reporting ready: ${err.message}`)));
232
424
  };
233
425
  // `timer` is declared after `cleanup` so the cleanup closure can reference
234
426
  // it. The const is reached before any handler attaches below, so no TDZ
235
427
  // access can fire from the listeners.
236
428
  const timer = setTimeout(() => {
237
429
  cleanup();
238
- reject(new Error(`Replacement worker did not report ready within ${WORKER_READY_TIMEOUT_MS}ms — likely crashed during top-of-script init`));
430
+ reject(new Error(withStderr(worker, `Replacement worker did not report ready within ${WORKER_READY_TIMEOUT_MS}ms — likely crashed during top-of-script init`)));
239
431
  }, WORKER_READY_TIMEOUT_MS);
240
432
  worker.on('message', onMessage);
241
433
  worker.once('error', onError);
@@ -275,7 +467,7 @@ function inFlightExcludePath(job, lastProgress) {
275
467
  const path = itemPath(job.items[lastProgress]);
276
468
  return path ? [path] : [];
277
469
  }
278
- function createJobs(items, maxItems, maxBytes, timeoutMs) {
470
+ function createJobs(items, maxItems, maxBytes, timeoutMs, chunkHash) {
279
471
  const jobs = [];
280
472
  let startIndex = 0;
281
473
  let batch = [];
@@ -289,6 +481,7 @@ function createJobs(items, maxItems, maxBytes, timeoutMs) {
289
481
  estimatedBytes: batchBytes,
290
482
  attempt: 0,
291
483
  splitDepth: 0,
484
+ chunkHash,
292
485
  timeoutMs,
293
486
  cumulativeTimeoutMs: timeoutMs,
294
487
  });
@@ -340,7 +533,31 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
340
533
  }
341
534
  const size = poolSize ?? resolveAutoPoolSize();
342
535
  const poolOptions = resolveWorkerPoolOptions(options, size);
343
- const spawnWorker = options?.workerFactory ?? ((url) => new Worker(url));
536
+ // Production factory spawns with `{ stderr: true }` so a worker's crash
537
+ // output is redirected to a `worker.stderr` stream we can tee + capture
538
+ // (see captureWorkerStderr) and attach to readiness-failure messages —
539
+ // instead of the generic "did not report ready" that hid the real cause
540
+ // in #1741. Test factories (workerFactory) are used verbatim.
541
+ // Bake the (immutable) ParsedFile store path into the factory closure so it
542
+ // reaches EVERY spawned worker — including respawns, which reuse this same
543
+ // factory — via `workerData`, read once at worker init. The `(url) => Worker`
544
+ // signature is unchanged so the zero-arg test factories keep working.
545
+ const parsedFileStoreStoragePath = options?.parsedFileStoreStoragePath;
546
+ const durableParsedFileStoragePath = options?.durableParsedFileStoragePath;
547
+ const workerStoreData = parsedFileStoreStoragePath || durableParsedFileStoragePath
548
+ ? { parsedFileStoreStoragePath, durableParsedFileStoragePath }
549
+ : undefined;
550
+ const spawnWorker = options?.workerFactory ??
551
+ ((url) => new Worker(url, {
552
+ stderr: true,
553
+ workerData: workerStoreData,
554
+ }));
555
+ /** Spawn + wire stderr capture in one step (used by all spawn sites). */
556
+ const spawnAndCapture = (url) => {
557
+ const worker = spawnWorker(url);
558
+ captureWorkerStderr(worker);
559
+ return worker;
560
+ };
344
561
  const workers = new Array(size);
345
562
  const retiredWorkers = new Set();
346
563
  const respawnCount = new Array(size).fill(0);
@@ -369,6 +586,9 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
369
586
  const slotGenerations = new Array(size).fill(0);
370
587
  let poolBroken = false;
371
588
  let poolFailure;
589
+ // Set by `terminate()` (below). Also read by the self-healing startup loop so
590
+ // a terminate during startup aborts pending backoff/retries (#1741).
591
+ let terminated = false;
372
592
  const terminateTrackedWorkers = async (liveWorkers) => {
373
593
  const retired = Array.from(retiredWorkers);
374
594
  await Promise.all([
@@ -378,42 +598,97 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
378
598
  retiredWorkers.clear();
379
599
  };
380
600
  for (let i = 0; i < size; i++) {
381
- workers[i] = spawnWorker(workerUrl);
601
+ workers[i] = spawnAndCapture(workerUrl);
382
602
  activeSlots.add(i);
383
603
  }
384
- // Symmetrize the readiness gate across initial and replacement spawn
385
- // paths. `replaceWorker` already awaits `waitForWorkerReady` per
386
- // replacement so an init-crashing worker is dropped before dispatch
387
- // sees it. The initial-spawn loop above didn't a worker whose
388
- // top-of-script init crashes (failed tree-sitter native binding,
389
- // missing dependency) would only be noticed at the first dispatch's
390
- // 30s idle timeout, vs the 5s WORKER_READY_TIMEOUT_MS bound that
391
- // replacements enjoy.
604
+ // ── Self-healing startup readiness (#1741) ────────────────────────────────
605
+ // Bring every initial slot to readiness with a BOUNDED, jittered retry loop
606
+ // instead of dropping it on the first crash. This symmetrizes the gate with
607
+ // the runtime `replaceWorker` path (which already respawns a crashed slot),
608
+ // and adds genuine self-healing at startup:
392
609
  //
393
- // The promise below settles every initial slot in parallel and drops
394
- // unready slots from `activeSlots` before any dispatch can fire.
395
- // `dispatch` awaits it via `initialReadyGate` on first invocation.
396
- // Wrapped in a single `Promise.allSettled` so a slow worker doesn't
397
- // block ready workers from being usable first dispatch waits for
398
- // all slots' verdicts (good or bad).
399
- const initialReadyGate = Promise.allSettled(workers.map(async (w, i) => {
400
- if (!w)
401
- return;
402
- try {
403
- await waitForWorkerReady(w);
404
- }
405
- catch (err) {
406
- initialReadinessFailures.push(err instanceof Error ? err.message : String(err));
407
- logger.warn({
408
- workerIndex: i,
409
- err: err instanceof Error ? err.message : String(err),
410
- }, `Worker ${i} did not report ready on initial spawn; dropping slot.`);
411
- await w.terminate().catch(() => undefined);
412
- workers[i] = undefined;
413
- activeSlots.delete(i);
610
+ // - TRANSIENT crash (a one-off OS hiccup / fork throttle): the slot is
611
+ // respawned after jittered backoff and retried, up to STARTUP_RESTART_BUDGET
612
+ // so a blip heals itself with no operator intervention.
613
+ // - DETERMINISTIC crash-loop (every worker dies with the SAME signature
614
+ // before any reaches ready the #1741 missing-binding case): detected via
615
+ // `crashSignature` and short-circuited immediately, so the pool gives up in
616
+ // ~1s rather than burning every slot's budget.
617
+ //
618
+ // When the loop exhausts, the slot is dropped from `activeSlots`. If EVERY
619
+ // slot is dropped, the first dispatch throws WorkerPoolInitializationError
620
+ // carrying the captured crash cause + classification — never a silent hang.
621
+ // Correctness of the deterministic short-circuit rests on the STRUCTURAL
622
+ // signal (zero workers ever ready + budget exhausted), not on signature
623
+ // matching alone: a missed match only costs a few seconds of extra retrying.
624
+ // Deterministic crash-loop detection (#1741). A crash counts toward
625
+ // "deterministic" ONLY after its signature reproduces across a respawn on the
626
+ // same slot so every slot is guaranteed at least one self-heal attempt and
627
+ // a simultaneous attempt-0 crash storm (e.g. transient `spawn EAGAIN` under
628
+ // fork pressure) cannot be misclassified as deterministic. We short-circuit
629
+ // once enough DISTINCT slots have each reproduced: ≥2 normally, or 1 for a
630
+ // size-1 pool. Until then the structural floor (every slot exhausts its
631
+ // budget) still bounds the worst case, so a missed match only costs retries.
632
+ const lastStartupSignature = new Map();
633
+ const reproducedStartupSlots = new Set();
634
+ const deterministicSlotThreshold = Math.min(DETERMINISTIC_STARTUP_FINGERPRINT_THRESHOLD, size);
635
+ let deterministicStartupDetected = false;
636
+ let anyWorkerReachedReady = false;
637
+ // Cancel functions for in-flight startup backoffs (see abortableSleep). The
638
+ // backoff timer is ref'd so a retry actually runs; terminate() invokes these
639
+ // to clear pending backoffs and resolve their sleeps so the slot loops wake,
640
+ // see `terminated`, and give up — instead of the process staying pinned for
641
+ // the backoff cap after terminate (#1741).
642
+ const pendingStartupTimers = new Set();
643
+ const bringSlotReady = async (i) => {
644
+ for (let attempt = 0;; attempt++) {
645
+ const worker = workers[i];
646
+ if (!worker)
647
+ return; // terminated mid-startup
648
+ try {
649
+ await waitForWorkerReady(worker);
650
+ anyWorkerReachedReady = true;
651
+ return; // ready — slot stays in activeSlots
652
+ }
653
+ catch (err) {
654
+ const msg = err instanceof Error ? err.message : String(err);
655
+ const sig = crashSignature(msg);
656
+ // Same signature as this slot's previous attempt => it survived a
657
+ // respawn, so retrying this slot is futile. (First crash has no prior
658
+ // signature, so attempt 0 never counts — every slot self-heals once.)
659
+ if (lastStartupSignature.get(i) === sig)
660
+ reproducedStartupSlots.add(i);
661
+ lastStartupSignature.set(i, sig);
662
+ if (!anyWorkerReachedReady && reproducedStartupSlots.size >= deterministicSlotThreshold) {
663
+ deterministicStartupDetected = true;
664
+ }
665
+ await worker.terminate().catch(() => undefined);
666
+ workers[i] = undefined;
667
+ const giveUp = terminated || deterministicStartupDetected || attempt >= STARTUP_RESTART_BUDGET;
668
+ if (giveUp) {
669
+ initialReadinessFailures.push(msg);
670
+ activeSlots.delete(i);
671
+ logger.warn({ workerIndex: i, attempt, err: msg, deterministic: deterministicStartupDetected }, deterministicStartupDetected
672
+ ? `Worker ${i} hit a deterministic startup crash-loop; dropping slot without further retries.`
673
+ : `Worker ${i} did not report ready after ${attempt + 1} attempt(s); dropping slot.`);
674
+ return;
675
+ }
676
+ // Transient: jittered backoff, then respawn the slot and retry.
677
+ await abortableSleep(startupBackoffMs(attempt), () => terminated || deterministicStartupDetected, pendingStartupTimers);
678
+ if (terminated || deterministicStartupDetected) {
679
+ initialReadinessFailures.push(msg);
680
+ activeSlots.delete(i);
681
+ return;
682
+ }
683
+ logger.warn({ workerIndex: i, attempt: attempt + 1 }, `Worker ${i} crashed during startup; respawning slot (self-heal attempt ${attempt + 1}/${STARTUP_RESTART_BUDGET}).`);
684
+ workers[i] = spawnAndCapture(workerUrl);
685
+ }
414
686
  }
415
- })).then(() => undefined);
416
- const dispatch = async (items, onProgress) => {
687
+ };
688
+ // First dispatch awaits this; it settles every slot's bounded retry loop in
689
+ // parallel and drops the unrecoverable ones before any dispatch can fire.
690
+ const initialReadyGate = Promise.allSettled(workers.map((_, i) => bringSlotReady(i))).then(() => undefined);
691
+ const dispatch = async (items, onProgress, chunkHash) => {
417
692
  // Await the initial-spawn readiness gate (F13). On first dispatch
418
693
  // this blocks for up to WORKER_READY_TIMEOUT_MS while every initial
419
694
  // worker's `{type:'ready'}` handshake is checked; on subsequent
@@ -434,7 +709,13 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
434
709
  const detail = initialReadinessFailures.length > 0
435
710
  ? ` after initial ready handshake: ${initialReadinessFailures.join('; ')}`
436
711
  : '';
437
- throw new WorkerPoolInitializationError(`Worker pool has no active workers${detail}`, [], initialReadinessFailures);
712
+ // The bounded self-heal exhausted (or short-circuited a deterministic
713
+ // crash-loop). Classify automatically so the caller renders the real
714
+ // cause without consulting any operator flag (#1741).
715
+ const crashClass = deterministicStartupDetected
716
+ ? 'deterministic-startup'
717
+ : 'transient-exhausted';
718
+ throw new WorkerPoolInitializationError(`Worker pool has no active workers${detail}`, [], initialReadinessFailures, crashClass);
438
719
  }
439
720
  // Layer 3: filter out quarantined paths so a known-bad file never reaches
440
721
  // a worker again this pool lifetime. The caller queries
@@ -448,7 +729,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
448
729
  }
449
730
  if (dispatchableItems.length === 0)
450
731
  return [];
451
- const jobs = createJobs(dispatchableItems, poolOptions.subBatchSize, poolOptions.subBatchMaxBytes, poolOptions.subBatchIdleTimeoutMs);
732
+ const jobs = createJobs(dispatchableItems, poolOptions.subBatchSize, poolOptions.subBatchMaxBytes, poolOptions.subBatchIdleTimeoutMs, chunkHash);
452
733
  return new Promise((resolve, reject) => {
453
734
  const results = [];
454
735
  const inFlightProgress = new Array(size).fill(0);
@@ -549,7 +830,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
549
830
  await removeWorkerFromSlot(workerIndex, mode, reason);
550
831
  if (stopped)
551
832
  return false;
552
- const replacement = spawnWorker(workerUrl);
833
+ const replacement = spawnAndCapture(workerUrl);
553
834
  try {
554
835
  await waitForWorkerReady(replacement);
555
836
  }
@@ -663,6 +944,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
663
944
  estimatedBytes: filtered.reduce((sum, item) => sum + estimateItemBytes(item), 0),
664
945
  attempt: job.attempt,
665
946
  splitDepth: job.splitDepth,
947
+ chunkHash: job.chunkHash,
666
948
  timeoutMs: job.timeoutMs,
667
949
  cumulativeTimeoutMs: job.cumulativeTimeoutMs,
668
950
  });
@@ -753,6 +1035,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
753
1035
  estimatedBytes: firstItems.reduce((sum, item) => sum + estimateItemBytes(item), 0),
754
1036
  attempt: job.attempt,
755
1037
  splitDepth: job.splitDepth + 1,
1038
+ chunkHash: job.chunkHash,
756
1039
  timeoutMs: nextTimeout,
757
1040
  cumulativeTimeoutMs: nextCumulative,
758
1041
  };
@@ -762,6 +1045,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
762
1045
  estimatedBytes: secondItems.reduce((sum, item) => sum + estimateItemBytes(item), 0),
763
1046
  attempt: job.attempt,
764
1047
  splitDepth: job.splitDepth + 1,
1048
+ chunkHash: job.chunkHash,
765
1049
  timeoutMs: nextTimeout,
766
1050
  cumulativeTimeoutMs: nextCumulative,
767
1051
  };
@@ -1064,12 +1348,15 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
1064
1348
  else if (msg.type === 'sub-batch-done') {
1065
1349
  waitingForFlush = true;
1066
1350
  resetIdleTimer();
1067
- worker.postMessage({ type: 'flush' });
1351
+ // Carry the chunk hash on the flush so the worker can write a
1352
+ // durable, content-addressed ParsedFile shard (warm-cache reuse)
1353
+ // at the flush boundary where `accumulated.parsedFiles` is complete.
1354
+ worker.postMessage({ type: 'flush', chunkHash: job.chunkHash });
1068
1355
  }
1069
1356
  else if (msg.type === 'error') {
1070
1357
  settled = true;
1071
1358
  cleanup();
1072
- void recoverAndResume(`Worker ${workerIndex} error: ${msg.error}`, resolveExcludePaths());
1359
+ void recoverAndResume(workerErrorReason(workerIndex, msg.error, msg.errorStack), resolveExcludePaths());
1073
1360
  }
1074
1361
  else if (msg.type === 'result') {
1075
1362
  if (!waitingForFlush) {
@@ -1110,7 +1397,12 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
1110
1397
  if (!settled) {
1111
1398
  settled = true;
1112
1399
  cleanup();
1113
- void recoverAndResume(`Worker ${workerIndex} error: ${err.message}`, resolveExcludePaths());
1400
+ // The Node 'error' event fires on an UNCAUGHT worker throw (one that
1401
+ // escaped the worker's own try/catch, or an async rejection). Unlike
1402
+ // the `{type:'error'}` message, the event delivers a real Error whose
1403
+ // `.stack` is the worker-side frame — carry it so the surfaced reason
1404
+ // points at the actual failure site, not just `err.message` (#2068).
1405
+ void recoverAndResume(workerErrorReason(workerIndex, err.message, err.stack), resolveExcludePaths());
1114
1406
  }
1115
1407
  };
1116
1408
  const exitHandler = (code) => {
@@ -1165,9 +1457,13 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
1165
1457
  runWorker(slotIndex);
1166
1458
  });
1167
1459
  };
1168
- let terminated = false;
1169
1460
  const terminate = async () => {
1170
1461
  terminated = true;
1462
+ // Cancel any in-flight startup backoff so its ref'd timer doesn't keep the
1463
+ // event loop alive after terminate; each cancel resolves the awaiting sleep
1464
+ // and the slot loop then sees `terminated` and gives up (#1741).
1465
+ for (const cancel of [...pendingStartupTimers])
1466
+ cancel();
1171
1467
  // `.catch(() => undefined)` per-worker matches every other terminate
1172
1468
  // site in this file. Without it, a hung/OOM-killed worker's terminate
1173
1469
  // rejection escapes `Promise.all` and replaces the original pipeline
@@ -1190,6 +1486,7 @@ export const createWorkerPool = (workerUrl, poolSize, options) => {
1190
1486
  quarantined: quarantine.size,
1191
1487
  poolBroken,
1192
1488
  terminated,
1489
+ pendingStartupTimers: pendingStartupTimers.size,
1193
1490
  slotGenerations: slotGenerations.slice(),
1194
1491
  }),
1195
1492
  };
@@ -14,6 +14,21 @@
14
14
  import fs from 'fs/promises';
15
15
  import { createWriteStream } from 'fs';
16
16
  import path from 'path';
17
+ import { parseTruthyEnv } from '../ingestion/utils/env.js';
18
+ /**
19
+ * Deterministic output ordering — optional (out-of-core / windowed-resolve
20
+ * enabler). When `GITNEXUS_SORT_GRAPH_OUTPUT` is set, nodes and relationships
21
+ * are emitted sorted by their (unique, dedup-key) graph `id` rather than in
22
+ * graph-insertion order, making the CSV a pure function of the graph's node/edge
23
+ * SET instead of of emit order. Default off returns the iterator untouched, so
24
+ * the bytes are identical to today. With it on, a windowed/out-of-core emit
25
+ * (the later windowed-resolve work) need only reproduce the same edge SET, not the global insertion order —
26
+ * which removes "CSV row order == Map insertion order" as a byte-identical
27
+ * hazard for every later windowing step.
28
+ */
29
+ const byGraphId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
30
+ const orderedNodes = (graph, sorted) => sorted ? [...graph.iterNodes()].sort(byGraphId) : graph.iterNodes();
31
+ const orderedRelationships = (graph, sorted) => sorted ? [...graph.iterRelationships()].sort(byGraphId) : graph.iterRelationships();
17
32
  /** Flush buffered rows to disk every N rows */
18
33
  const FLUSH_EVERY = 500;
19
34
  // ============================================================================
@@ -185,6 +200,9 @@ class BufferedCSVWriter {
185
200
  * File contents are lazy-read from disk with a generous LRU cache.
186
201
  */
187
202
  export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
203
+ // Deterministic (id-sorted) node/relationship row order when enabled;
204
+ // default off = today's graph-insertion order (byte-identical).
205
+ const sortOutput = parseTruthyEnv(process.env.GITNEXUS_SORT_GRAPH_OUTPUT);
188
206
  // Remove stale CSVs from previous crashed runs, then recreate
189
207
  try {
190
208
  await fs.rm(csvDir, { recursive: true, force: true });
@@ -253,7 +271,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
253
271
  // A single Set covering every label prevents PK violations on COPY.
254
272
  const seenNodeIds = new Set();
255
273
  // --- SINGLE PASS over all nodes ---
256
- for (const node of graph.iterNodes()) {
274
+ for (const node of orderedNodes(graph, sortOutput)) {
257
275
  if (seenNodeIds.has(node.id))
258
276
  continue;
259
277
  seenNodeIds.add(node.id);
@@ -421,7 +439,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir) => {
421
439
  // --- Stream relationship CSV ---
422
440
  const relCsvPath = path.join(csvDir, 'relations.csv');
423
441
  const relWriter = new BufferedCSVWriter(relCsvPath, 'from,to,type,confidence,reason,step');
424
- for (const rel of graph.iterRelationships()) {
442
+ for (const rel of orderedRelationships(graph, sortOutput)) {
425
443
  await relWriter.addRow([
426
444
  escapeCSVField(rel.sourceId),
427
445
  escapeCSVField(rel.targetId),