code-auditor-mcp 5.0.1 → 5.2.0

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 (360) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +227 -3
  3. package/README.md +24 -2
  4. package/configs/hhra-compat.json +12 -118
  5. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  6. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +11 -1
  7. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  8. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  9. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +57 -52
  10. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  11. package/dist/analyzers/handleIdentification.d.ts.map +1 -1
  12. package/dist/analyzers/handleIdentification.js +4 -1
  13. package/dist/analyzers/handleIdentification.js.map +1 -1
  14. package/dist/analyzers/provenance.d.ts +9 -4
  15. package/dist/analyzers/provenance.d.ts.map +1 -1
  16. package/dist/analyzers/provenance.js +8 -25
  17. package/dist/analyzers/provenance.js.map +1 -1
  18. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  19. package/dist/analyzers/reactAnalyzer.js +0 -5
  20. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  21. package/dist/analyzers/receiverRoot.d.ts +20 -0
  22. package/dist/analyzers/receiverRoot.d.ts.map +1 -1
  23. package/dist/analyzers/receiverRoot.js +42 -0
  24. package/dist/analyzers/receiverRoot.js.map +1 -1
  25. package/dist/analyzers/ruleRegistry.js +4 -4
  26. package/dist/analyzers/ruleRegistry.js.map +1 -1
  27. package/dist/analyzers/ruleTiming.d.ts +1 -1
  28. package/dist/analyzers/ruleTiming.js +2 -2
  29. package/dist/analyzers/ruleTiming.js.map +1 -1
  30. package/dist/analyzers/tsEcosystem.d.ts.map +1 -1
  31. package/dist/analyzers/tsEcosystem.js +10 -0
  32. package/dist/analyzers/tsEcosystem.js.map +1 -1
  33. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  34. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +22 -26
  35. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  36. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +32 -15
  37. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  38. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +189 -115
  39. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  40. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -2
  41. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  42. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +0 -5
  43. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  44. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +2 -10
  45. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  46. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +7 -49
  47. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  48. package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
  49. package/dist/analyzers/universal/schema/config.js +0 -2
  50. package/dist/analyzers/universal/schema/config.js.map +1 -1
  51. package/dist/analyzers/universal/schema/types.d.ts +0 -2
  52. package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
  53. package/dist/analyzers/vendorSurfaces.d.ts +126 -0
  54. package/dist/analyzers/vendorSurfaces.d.ts.map +1 -0
  55. package/dist/analyzers/vendorSurfaces.js +580 -0
  56. package/dist/analyzers/vendorSurfaces.js.map +1 -0
  57. package/dist/auditRunner.d.ts +2 -0
  58. package/dist/auditRunner.d.ts.map +1 -1
  59. package/dist/auditRunner.js +159 -160
  60. package/dist/auditRunner.js.map +1 -1
  61. package/dist/churn/churnExtractor.d.ts +0 -1
  62. package/dist/churn/churnExtractor.d.ts.map +1 -1
  63. package/dist/churn/churnExtractor.js +2 -4
  64. package/dist/churn/churnExtractor.js.map +1 -1
  65. package/dist/cli.d.ts +1 -1
  66. package/dist/cli.d.ts.map +1 -1
  67. package/dist/cli.js +212 -29
  68. package/dist/cli.js.map +1 -1
  69. package/dist/codeIndex/migrations.d.ts.map +1 -1
  70. package/dist/codeIndex/migrations.js +4 -3
  71. package/dist/codeIndex/migrations.js.map +1 -1
  72. package/dist/codeIndexDB.d.ts +11 -2
  73. package/dist/codeIndexDB.d.ts.map +1 -1
  74. package/dist/codeIndexDB.js +27 -2
  75. package/dist/codeIndexDB.js.map +1 -1
  76. package/dist/config/configLoader.d.ts +60 -7
  77. package/dist/config/configLoader.d.ts.map +1 -1
  78. package/dist/config/configLoader.js +252 -60
  79. package/dist/config/configLoader.js.map +1 -1
  80. package/dist/config/defaults.d.ts +77 -18
  81. package/dist/config/defaults.d.ts.map +1 -1
  82. package/dist/config/defaults.js +105 -43
  83. package/dist/config/defaults.js.map +1 -1
  84. package/dist/constants.d.ts +2 -1
  85. package/dist/constants.d.ts.map +1 -1
  86. package/dist/constants.js +4 -0
  87. package/dist/constants.js.map +1 -1
  88. package/dist/daemon/main.d.ts +0 -1
  89. package/dist/daemon/main.d.ts.map +1 -1
  90. package/dist/daemon/main.js +31 -8
  91. package/dist/daemon/main.js.map +1 -1
  92. package/dist/dataPaths.d.ts +17 -11
  93. package/dist/dataPaths.d.ts.map +1 -1
  94. package/dist/dataPaths.js +51 -16
  95. package/dist/dataPaths.js.map +1 -1
  96. package/dist/graph/resolutionEnvironment.d.ts +61 -0
  97. package/dist/graph/resolutionEnvironment.d.ts.map +1 -0
  98. package/dist/graph/resolutionEnvironment.js +57 -0
  99. package/dist/graph/resolutionEnvironment.js.map +1 -0
  100. package/dist/hookLog.d.ts +54 -0
  101. package/dist/hookLog.d.ts.map +1 -0
  102. package/dist/hookLog.js +105 -0
  103. package/dist/hookLog.js.map +1 -0
  104. package/dist/hookLogPath.generated.d.ts +3 -0
  105. package/dist/hookLogPath.generated.d.ts.map +1 -0
  106. package/dist/hookLogPath.generated.js +8 -0
  107. package/dist/hookLogPath.generated.js.map +1 -0
  108. package/dist/index.d.ts +0 -5
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +0 -7
  111. package/dist/index.js.map +1 -1
  112. package/dist/installConfig.d.ts +15 -2
  113. package/dist/installConfig.d.ts.map +1 -1
  114. package/dist/installConfig.js +17 -2
  115. package/dist/installConfig.js.map +1 -1
  116. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  117. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +18 -3
  118. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  119. package/dist/mcp-tools/workflowGuide.js +8 -8
  120. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  121. package/dist/mcp-tools-shared.d.ts.map +1 -1
  122. package/dist/mcp-tools-shared.js +5 -2
  123. package/dist/mcp-tools-shared.js.map +1 -1
  124. package/dist/mcp-ui-simple.d.ts +5 -0
  125. package/dist/mcp-ui-simple.d.ts.map +1 -1
  126. package/dist/mcp-ui-simple.js +9 -2
  127. package/dist/mcp-ui-simple.js.map +1 -1
  128. package/dist/mcp.d.ts +0 -1
  129. package/dist/mcp.d.ts.map +1 -1
  130. package/dist/mcp.js +36 -33
  131. package/dist/mcp.js.map +1 -1
  132. package/dist/mcpAuditJobs.d.ts +10 -0
  133. package/dist/mcpAuditJobs.d.ts.map +1 -1
  134. package/dist/mcpAuditJobs.js +10 -1
  135. package/dist/mcpAuditJobs.js.map +1 -1
  136. package/dist/mcpAutoIndex.d.ts.map +1 -1
  137. package/dist/mcpAutoIndex.js +2 -1
  138. package/dist/mcpAutoIndex.js.map +1 -1
  139. package/dist/phase/colorMath.d.ts +18 -5
  140. package/dist/phase/colorMath.d.ts.map +1 -1
  141. package/dist/phase/colorMath.js +88 -5
  142. package/dist/phase/colorMath.js.map +1 -1
  143. package/dist/phase/dataAccessCallsCandidates.js +1 -1
  144. package/dist/phase/dataAccessCallsCandidates.js.map +1 -1
  145. package/dist/phase/goFunctionAnalysis.d.ts +2 -2
  146. package/dist/phase/goFunctionAnalysis.js +2 -2
  147. package/dist/phase/goFunctions.d.ts +2 -2
  148. package/dist/phase/goFunctions.js +2 -2
  149. package/dist/phase/goImports.d.ts +2 -2
  150. package/dist/phase/goImports.js +2 -2
  151. package/dist/phase/goSwitches.d.ts +2 -2
  152. package/dist/phase/goSwitches.js +2 -2
  153. package/dist/phase/loopQueryCandidates.js +1 -1
  154. package/dist/phase/loopQueryCandidates.js.map +1 -1
  155. package/dist/phase/phaseModel.d.ts +37 -8
  156. package/dist/phase/phaseModel.d.ts.map +1 -1
  157. package/dist/phase/phaseModel.js +94 -46
  158. package/dist/phase/phaseModel.js.map +1 -1
  159. package/dist/phase/producers.d.ts.map +1 -1
  160. package/dist/phase/producers.js +27 -17
  161. package/dist/phase/producers.js.map +1 -1
  162. package/dist/phase/reachability.d.ts +44 -1
  163. package/dist/phase/reachability.d.ts.map +1 -1
  164. package/dist/phase/reachability.js +79 -0
  165. package/dist/phase/reachability.js.map +1 -1
  166. package/dist/phase/receiverConsumers.d.ts +8 -1
  167. package/dist/phase/receiverConsumers.d.ts.map +1 -1
  168. package/dist/phase/receiverConsumers.js +59 -21
  169. package/dist/phase/receiverConsumers.js.map +1 -1
  170. package/dist/phase/receiverProvenance.d.ts +23 -0
  171. package/dist/phase/receiverProvenance.d.ts.map +1 -1
  172. package/dist/phase/receiverProvenance.js +40 -0
  173. package/dist/phase/receiverProvenance.js.map +1 -1
  174. package/dist/phase/rules/crossDomain.d.ts.map +1 -1
  175. package/dist/phase/rules/crossDomain.js +16 -6
  176. package/dist/phase/rules/crossDomain.js.map +1 -1
  177. package/dist/phase/rules/dry.d.ts.map +1 -1
  178. package/dist/phase/rules/dry.js +0 -1
  179. package/dist/phase/rules/dry.js.map +1 -1
  180. package/dist/phase/rules/goRules.d.ts +2 -2
  181. package/dist/phase/rules/goRules.js +2 -2
  182. package/dist/phase/rules/secrets.d.ts +22 -8
  183. package/dist/phase/rules/secrets.d.ts.map +1 -1
  184. package/dist/phase/rules/secrets.js +48 -67
  185. package/dist/phase/rules/secrets.js.map +1 -1
  186. package/dist/phase/rules/styles.d.ts +23 -9
  187. package/dist/phase/rules/styles.d.ts.map +1 -1
  188. package/dist/phase/rules/styles.js +47 -15
  189. package/dist/phase/rules/styles.js.map +1 -1
  190. package/dist/phase/rules/unreferencedModule.d.ts.map +1 -1
  191. package/dist/phase/rules/unreferencedModule.js +16 -5
  192. package/dist/phase/rules/unreferencedModule.js.map +1 -1
  193. package/dist/phase/runner.d.ts +16 -7
  194. package/dist/phase/runner.d.ts.map +1 -1
  195. package/dist/phase/runner.js +32 -20
  196. package/dist/phase/runner.js.map +1 -1
  197. package/dist/phase/stylesCss.d.ts.map +1 -1
  198. package/dist/phase/stylesCss.js +4 -0
  199. package/dist/phase/stylesCss.js.map +1 -1
  200. package/dist/phase/stylesMarkup.d.ts.map +1 -1
  201. package/dist/phase/stylesMarkup.js +4 -0
  202. package/dist/phase/stylesMarkup.js.map +1 -1
  203. package/dist/phase/stylesSource.d.ts.map +1 -1
  204. package/dist/phase/stylesSource.js +6 -0
  205. package/dist/phase/stylesSource.js.map +1 -1
  206. package/dist/phase/testProgram.d.ts +57 -0
  207. package/dist/phase/testProgram.d.ts.map +1 -0
  208. package/dist/phase/testProgram.js +244 -0
  209. package/dist/phase/testProgram.js.map +1 -0
  210. package/dist/phase/typeDeclarations.d.ts +2 -2
  211. package/dist/phase/typeDeclarations.js +2 -2
  212. package/dist/phase/types.d.ts +54 -0
  213. package/dist/phase/types.d.ts.map +1 -1
  214. package/dist/pipelineAdapters.d.ts +1 -171
  215. package/dist/pipelineAdapters.d.ts.map +1 -1
  216. package/dist/pipelineAdapters.js +47 -1281
  217. package/dist/pipelineAdapters.js.map +1 -1
  218. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  219. package/dist/reporting/csvReportGenerator.js +0 -6
  220. package/dist/reporting/csvReportGenerator.js.map +1 -1
  221. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  222. package/dist/reporting/htmlReportGenerator.js +0 -4
  223. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  224. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  225. package/dist/reporting/jsonReportGenerator.js +0 -1
  226. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  227. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  228. package/dist/reporting/sarifReportGenerator.js +0 -7
  229. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  230. package/dist/sqlite/driver.d.ts +17 -1
  231. package/dist/sqlite/driver.d.ts.map +1 -1
  232. package/dist/sqlite/driver.js +28 -5
  233. package/dist/sqlite/driver.js.map +1 -1
  234. package/dist/styles/cssInJsHarvest.d.ts +36 -0
  235. package/dist/styles/cssInJsHarvest.d.ts.map +1 -0
  236. package/dist/styles/cssInJsHarvest.js +78 -0
  237. package/dist/styles/cssInJsHarvest.js.map +1 -0
  238. package/dist/styles/definedClasses.d.ts +95 -0
  239. package/dist/styles/definedClasses.d.ts.map +1 -0
  240. package/dist/styles/definedClasses.js +129 -0
  241. package/dist/styles/definedClasses.js.map +1 -0
  242. package/dist/styles/markupStyleHarvest.d.ts +33 -0
  243. package/dist/styles/markupStyleHarvest.d.ts.map +1 -0
  244. package/dist/styles/markupStyleHarvest.js +73 -0
  245. package/dist/styles/markupStyleHarvest.js.map +1 -0
  246. package/dist/styles/styleExtractor.d.ts +37 -0
  247. package/dist/styles/styleExtractor.d.ts.map +1 -1
  248. package/dist/styles/styleExtractor.js +89 -33
  249. package/dist/styles/styleExtractor.js.map +1 -1
  250. package/dist/styles/styleIndexer.d.ts.map +1 -1
  251. package/dist/styles/styleIndexer.js +11 -10
  252. package/dist/styles/styleIndexer.js.map +1 -1
  253. package/dist/styles/tailwindExpander.d.ts.map +1 -1
  254. package/dist/styles/tailwindExpander.js +31 -10
  255. package/dist/styles/tailwindExpander.js.map +1 -1
  256. package/dist/telemetry.d.ts +67 -1
  257. package/dist/telemetry.d.ts.map +1 -1
  258. package/dist/telemetry.js +100 -20
  259. package/dist/telemetry.js.map +1 -1
  260. package/dist/types/crossLanguage.d.ts +1 -0
  261. package/dist/types/crossLanguage.d.ts.map +1 -1
  262. package/dist/types.d.ts +36 -56
  263. package/dist/types.d.ts.map +1 -1
  264. package/dist/types.js +4 -4
  265. package/dist/types.js.map +1 -1
  266. package/dist/version.generated.d.ts +1 -1
  267. package/dist/version.generated.js +1 -1
  268. package/examples/.auditrc.example.json +7 -22
  269. package/examples/.auditrc.json +12 -155
  270. package/examples/.code-auditor.json +8 -51
  271. package/examples/nextjs.auditrc.json +7 -34
  272. package/examples/node-api.auditrc.json +6 -46
  273. package/examples/react.auditrc.json +8 -25
  274. package/examples/tolerant-solid.auditrc.json +12 -44
  275. package/package.json +13 -9
  276. package/plugin/.claude-plugin/manifest.json +10 -9
  277. package/plugin/.claude-plugin/plugin.json +1 -1
  278. package/plugin/hooks/hooks.json +4 -4
  279. package/plugin/scripts/hook-audit.sh +14 -2
  280. package/plugin/scripts/hook-common.sh +41 -11
  281. package/plugin/scripts/hook-drift-check.sh +19 -6
  282. package/plugin/scripts/hook-log-path.sh +7 -0
  283. package/plugin/scripts/hook-self-audit.sh +14 -2
  284. package/plugin/scripts/hook-warm.sh +22 -17
  285. package/plugin/skills/code-auditor/SKILL.md +26 -3
  286. package/server.json +10 -4
  287. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +0 -6
  288. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +0 -1
  289. package/dist/__tests__/fixtures/spec-20/scripts/tool.js +0 -12
  290. package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +0 -1
  291. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +0 -7
  292. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +0 -1
  293. package/dist/__tests__/fixtures/spec-20/src/demo-module.js +0 -13
  294. package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +0 -1
  295. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts +0 -14
  296. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts.map +0 -1
  297. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js +0 -16
  298. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js.map +0 -1
  299. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts +0 -15
  300. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts.map +0 -1
  301. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js +0 -17
  302. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js.map +0 -1
  303. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +0 -14
  304. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +0 -1
  305. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +0 -19
  306. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +0 -1
  307. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +0 -18
  308. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +0 -1
  309. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +0 -19
  310. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +0 -1
  311. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts +0 -19
  312. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts.map +0 -1
  313. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js +0 -19
  314. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js.map +0 -1
  315. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +0 -17
  316. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +0 -1
  317. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js +0 -19
  318. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +0 -1
  319. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +0 -25
  320. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +0 -1
  321. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js +0 -21
  322. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +0 -1
  323. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts +0 -15
  324. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts.map +0 -1
  325. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js +0 -19
  326. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js.map +0 -1
  327. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts +0 -14
  328. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts.map +0 -1
  329. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js +0 -14
  330. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js.map +0 -1
  331. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +0 -15
  332. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +0 -1
  333. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js +0 -16
  334. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +0 -1
  335. package/dist/languages/go/analyzer-darwin-amd64 +0 -0
  336. package/dist/languages/go/analyzer-darwin-arm64 +0 -0
  337. package/dist/languages/go/analyzer-linux-amd64 +0 -0
  338. package/dist/languages/go/analyzer-linux-arm64 +0 -0
  339. package/dist/languages/go/analyzer-src/analyzer.go +0 -643
  340. package/dist/languages/go/analyzer-src/dataaccess.go +0 -388
  341. package/dist/languages/go/analyzer-src/go.mod +0 -3
  342. package/dist/languages/go/analyzer-src/indexer.go +0 -268
  343. package/dist/languages/go/analyzer-src/parser.go +0 -461
  344. package/dist/languages/go/analyzer-src/solid.go +0 -311
  345. package/dist/languages/go/analyzer-src/testconventions.go +0 -25
  346. package/dist/languages/go/analyzer-src/types.go +0 -156
  347. package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
  348. package/dist/languages/go/go.mod +0 -7
  349. package/dist/languages/go/main.go +0 -203
  350. package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
  351. package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
  352. package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
  353. package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
  354. package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
  355. package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
  356. package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
  357. package/dist/native-bootstrap.d.ts +0 -15
  358. package/dist/native-bootstrap.d.ts.map +0 -1
  359. package/dist/native-bootstrap.js +0 -75
  360. package/dist/native-bootstrap.js.map +0 -1
@@ -23,9 +23,7 @@ import { isMcpDebugEnabled, logMcpDebug, logMcpInfo } from './mcpDiagnostics.js'
23
23
  import { loadBaseline, matchFindings, hashBaseline } from './baseline.js';
24
24
  import { applyDismissals } from './dismissals.js';
25
25
  import { computeImpact, LATENCY_BUDGET_MS } from './graph/blastRadius.js';
26
- import { readTsconfigAliases, readPackageEntryPoints, readDeclaredTypePackages, DEFAULT_VIRTUAL_MODULES } from './graph/importClassification.js';
27
- import { normalizeDialect } from './mcp-tools/discoveryQueries.js';
28
- import { detectDialect } from './languages/sql/dialectDetection.js';
26
+ import { buildResolutionEnvironment } from './graph/resolutionEnvironment.js';
29
27
  // Import universal analyzers
30
28
  import { initializeLanguages } from './languages/index.js';
31
29
  import { initializeOrmAdapters } from './analyzers/orm/index.js';
@@ -37,12 +35,12 @@ import { writeAuditToLedger, detectRunInput } from './ledger.js';
37
35
  import { runPipeline, makeVisitorStatus, getFilesProcessed, isVisitorStatus } from './pipeline.js';
38
36
  import { getRuleTimingSortedDesc } from './analyzers/ruleTiming.js';
39
37
  import { createFunctionIndexVisitor, createStylesCssVisitor, createStylesSourceVisitor, createStylesReducer, createInvariantsReducer, createSchemaCodeVisitor, } from './pipelineAdapters.js';
40
- import { computeSizeDistributions } from './reporting/sizeDistribution.js';
41
- import { splitRoutes, attributeRoutes, enabledMigratedRules } from './phase/routing.js';
42
38
  import { runPhaseModel } from './phase/phaseModel.js';
39
+ import { resolveVendorSurfaces } from './analyzers/vendorSurfaces.js';
43
40
  import { resolvePhaseThresholds } from './phase/config.js';
44
41
  import { deriveCoverage, presentFormatsOf } from './phase/coverage.js';
45
42
  import { MIGRATED_RULES, RULE_ANALYZER } from './phase/rules/registry.js';
43
+ import { computeReachabilityScores } from './phase/reachability.js';
46
44
  import { seedDryPairs, resolveBlockConfig } from './phase/rules/dry.js';
47
45
  import { checkUnresolvedReceiverImports, checkUnprovenQueryReceivers, checkUnresolvedQueries, checkUnparseableSql, dedupeCannotFireByReceiver } from './analyzers/universal/schema/codeAnalysis.js';
48
46
  import { readProjectManifest, computeManifestStaleness } from './analyzers/manifestStaleness.js';
@@ -202,7 +200,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
202
200
  phase: 'discovery',
203
201
  message: `Discovering files... (scope: ${scopeResultType})`
204
202
  });
205
- const { files, skippedExtensions } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
203
+ const { files, skippedExtensions, changedScopeNoBaseline } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
206
204
  throwIfAborted(mergedOptions.abortSignal);
207
205
  // Detect changed functions for non-all scopes
208
206
  let changedFunctions;
@@ -230,6 +228,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
230
228
  scope,
231
229
  isScoped,
232
230
  scopeResultType,
231
+ changedScopeNoBaseline,
233
232
  };
234
233
  }
235
234
  /**
@@ -240,6 +239,11 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
240
239
  */
241
240
  async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
242
241
  let files;
242
+ // True when a bare `changed` scope found no baseline in the index to diff
243
+ // against (an empty `functions` table). That is "the diff cannot be computed",
244
+ // not "nothing changed" — surfaced as a diagnostic so the run never reports a
245
+ // false clean. Explicit-files and all/other scopes leave this false.
246
+ let changedScopeNoBaseline = false;
243
247
  // Extensions present on disk that discovery skipped (Spec 43 R5 follow-up).
244
248
  // Populated only for the `all` scope — scoped runs are explicitly scoped by
245
249
  // the user, so "what wasn't analyzed" there is the scope itself, not the
@@ -271,12 +275,29 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
271
275
  error: err instanceof Error ? err.message : String(err)
272
276
  });
273
277
  }
274
- const modifiedFiles = mergedOptions.explicitFiles !== undefined
275
- ? mergedOptions.explicitFiles
276
- : (db ? await db.detectModifiedFiles(path.resolve(mergedOptions.projectRoot || process.cwd())) : []);
278
+ let modifiedFiles;
279
+ if (mergedOptions.explicitFiles !== undefined) {
280
+ modifiedFiles = mergedOptions.explicitFiles;
281
+ }
282
+ else if (db) {
283
+ // `undefined` here means *no baseline* (an empty `functions` table) — a
284
+ // different answer from `[]` ("baseline present, nothing modified"). The
285
+ // former is reported as an uncomputable diff, never as a clean run. The
286
+ // `db`-unavailable case (init threw, e.g. a future schema version) keeps
287
+ // its existing fail-open zero-file path and the later engine-error
288
+ // diagnostic.
289
+ const detected = await db.detectModifiedFiles(path.resolve(mergedOptions.projectRoot || process.cwd()));
290
+ if (detected === undefined)
291
+ changedScopeNoBaseline = true;
292
+ modifiedFiles = detected ?? [];
293
+ }
294
+ else {
295
+ modifiedFiles = [];
296
+ }
277
297
  files = [...new Set(modifiedFiles.map((f) => path.resolve(f)))].sort();
278
298
  logMcpInfo('discovery', 'changed scope resolved', {
279
- fileCount: files.length
299
+ fileCount: files.length,
300
+ noBaseline: changedScopeNoBaseline
280
301
  });
281
302
  }
282
303
  else if (Array.isArray(scope)) {
@@ -292,7 +313,7 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
292
313
  files = discovered.files;
293
314
  skippedExtensions = discovered.skippedExtensions;
294
315
  }
295
- return { files, skippedExtensions };
316
+ return { files, skippedExtensions, changedScopeNoBaseline };
296
317
  }
297
318
  /**
298
319
  * Detect changed functions for a scoped run via the code index (Spec 60.1).
@@ -529,8 +550,6 @@ function buildPipelineAdapters(analyzers) {
529
550
  const pipelineVisitors = [];
530
551
  const pipelineReducers = [];
531
552
  const pipelineDerivedReducers = [];
532
- let reactBundle;
533
- let solidBundle;
534
553
  // Always-on infrastructure: function-index visitor populates the
535
554
  // `functions` table so conventions + cross-domain reducers have data
536
555
  // even on a cold run with no prior index sync.
@@ -569,8 +588,6 @@ function buildPipelineAdapters(analyzers) {
569
588
  pipelineVisitors,
570
589
  pipelineReducers,
571
590
  pipelineDerivedReducers,
572
- reactBundle,
573
- solidBundle,
574
591
  };
575
592
  }
576
593
  /**
@@ -589,16 +606,12 @@ function buildPipelineAnalyzerConfig(analyzers, mergedOptions, root, files, corp
589
606
  // better-sqlite3/D1 → sqlite, mysql2 → mysql). A null dialect means the SQL
590
607
  // facts `cannot-fire` — and `sqlDialectReason` names *why* (undetermined or
591
608
  // ambiguous) so the abstention is visible rather than silent.
592
- const detection = mergedOptions.databaseType
593
- ? (() => {
594
- const explicit = normalizeDialect(mergedOptions.databaseType);
595
- return explicit
596
- ? { dialect: explicit, reason: null }
597
- : { dialect: null, reason: `dialect undetermined (unsupported databaseType '${mergedOptions.databaseType}')` };
598
- })()
599
- : detectDialect(root);
600
- const sqlDialect = detection.dialect;
601
- const sqlDialectReason = detection.reason;
609
+ const resolution = buildResolutionEnvironment(root, {
610
+ databaseType: mergedOptions.databaseType,
611
+ importVirtualModules: mergedOptions.importVirtualModules,
612
+ });
613
+ const sqlDialect = resolution.sqlDialect;
614
+ const sqlDialectReason = resolution.sqlDialectReason;
602
615
  // Spec 62 Amendment B — the missing-org-filter Stage-4 reducer reads the
603
616
  // data-access config namespace, so it inherits the data-access analyzer's
604
617
  // config rather than a fresh empty namespace.
@@ -662,10 +675,10 @@ function buildPipelineAnalyzerConfig(analyzers, mergedOptions, root, files, corp
662
675
  _provenanceTiming: provenanceTiming,
663
676
  files,
664
677
  corpusFiles,
665
- importVirtualModules: mergedOptions.importVirtualModules ?? DEFAULT_VIRTUAL_MODULES,
666
- tsconfigAliases: readTsconfigAliases(root),
667
- packageEntryPoints: readPackageEntryPoints(root).entryPaths,
668
- declaredTypePackages: readDeclaredTypePackages(root),
678
+ importVirtualModules: resolution.importVirtualModules,
679
+ tsconfigAliases: resolution.tsconfigAliases,
680
+ packageEntryPoints: resolution.packageEntryPoints,
681
+ declaredTypePackages: resolution.declaredTypePackages,
669
682
  // Spec 70 R1 — surfaced under `_infra` so `runPipeline` can hand the named
670
683
  // dialect to the phase model's data-access producer without reading the
671
684
  // data-access namespace.
@@ -713,19 +726,18 @@ function buildPipelineConfig(inputs) {
713
726
  };
714
727
  }
715
728
  /**
716
- * Run the both-paths split (Spec 68 §11.1): strip migrated rules' legacy
717
- * emission and re-emit phase findings into the correct analyzer buckets.
718
- * Mutates `analyzerResults` and `pipelineAnalyzerConfig` (dry divergence).
729
+ * Run the phase model — the single analysis path (Spec 68 §15) — and re-emit its
730
+ * findings into the correct analyzer buckets. The legacy analyzer path is
731
+ * deleted; there is no route split to choose between. Mutates `analyzerResults`
732
+ * and `pipelineAnalyzerConfig` (dry divergence).
719
733
  */
720
- async function applyPhaseModelSplit(inputs) {
734
+ async function runPhaseModelStage(inputs) {
721
735
  const { mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle } = inputs;
722
736
  let phaseFindings = [];
723
737
  let phaseIncompleteFacts = new Map();
724
- let routeAttribution;
738
+ let phaseReachability;
725
739
  let receiverDiagnostics = [];
726
- const { migrated } = splitRoutes();
727
- routeAttribution = Object.fromEntries(attributeRoutes());
728
- if (migrated.size > 0) {
740
+ if (MIGRATED_RULES.length > 0) {
729
741
  // The `dry/diverging-clone` rule reads its knobs from `dry.divergence`.
730
742
  const topLevelDivergence = mergedOptions.divergence;
731
743
  if (topLevelDivergence && !pipelineAnalyzerConfig['dry']?.divergence) {
@@ -764,12 +776,17 @@ async function applyPhaseModelSplit(inputs) {
764
776
  tsconfigAliases: infraConfig.tsconfigAliases,
765
777
  packageEntryPoints: infraConfig.packageEntryPoints,
766
778
  indexHandle: pipelineIndexHandle,
767
- enabledRules: enabledMigratedRules(),
779
+ enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
768
780
  externalTables,
769
781
  workerCount: resolveWorkerCount(),
770
782
  sqlDialect: infraConfig.sqlDialect,
771
783
  sqlDialectReason: infraConfig.sqlDialectReason,
772
784
  declaredTypePackages: infraConfig.declaredTypePackages,
785
+ // Item B (reissued) — resolve each vendor's declaration from the tree under
786
+ // analysis (its own node_modules / workspace path) once per run. The
787
+ // loop-query / data-access producers read these resolved surfaces; nothing
788
+ // about a vendor's API is committed and nothing is fetched at analysis time.
789
+ vendorSurfaces: resolveVendorSurfaces(root),
773
790
  // Spec 70 2b — the diverging-clone write: seed `dry_pair_history` from the
774
791
  // phase `code-block` fact (replacing `createDryVisitor`/`persistDryPairs`).
775
792
  persistDryPairHistory: (facts) => {
@@ -780,6 +797,11 @@ async function applyPhaseModelSplit(inputs) {
780
797
  });
781
798
  phaseFindings = phaseResult.findings;
782
799
  phaseIncompleteFacts = phaseResult.incompleteFacts;
800
+ const reachabilityFact = phaseResult.facts.get('reachability');
801
+ const fileImports = phaseResult.facts.get('file-imports');
802
+ phaseReachability = reachabilityFact && fileImports
803
+ ? computeReachabilityScores(fileImports, reachabilityFact)
804
+ : undefined;
783
805
  // Spec 69 §10 + Part 2b — re-home the pre-pass's diagnostics (deleted from
784
806
  // `pipeline.ts`) on the phase result. Cannot-fire: re-derive per file from
785
807
  // `phaseResult.unprovenQueryReceivers` + `phaseResult.unresolvedImports`, keyed
@@ -787,12 +809,34 @@ async function applyPhaseModelSplit(inputs) {
787
809
  // receivers/imports never reached it). Manifest-stale: the ecosystem-list
788
810
  // self-check, unchanged from the deleted `buildPipelineResult` loop.
789
811
  receiverDiagnostics = await buildReceiverDiagnostics(phaseResult.unprovenQueryReceivers, phaseResult.unresolvedImports, phaseResult.unresolvedQuerySites, phaseResult.unparseableSql, root);
790
- // Strip the migrated rules' legacy emission from every analyzer result.
791
- let strippedCount = 0;
812
+ // Spec 68 §8 — `unreferenced-module`'s unproven modules: a module with no
813
+ // resolved importer while the graph carries a computed-specifier dynamic
814
+ // import. The rule withholds the dead-code finding for these; this surfaces
815
+ // the disposition (with its reason) as a coverage diagnostic instead.
816
+ for (const m of phaseResult.unprovenModules) {
817
+ receiverDiagnostics.push({
818
+ analyzerName: 'dependency-graph',
819
+ kind: 'unresolved-dynamic-import',
820
+ message: m.reason,
821
+ file: m.file,
822
+ line: m.line,
823
+ });
824
+ }
825
+ // §15 — a rule that could not run (fail-open, missing dependency) reported
826
+ // itself on the phase result's diagnostic channel. Merge those alongside the
827
+ // receiver-diagnostics the pre-pass re-derived, so the styles
828
+ // `undefined-class-disabled` diagnostic (re-homed from the deleted
829
+ // `initTailwindProbe`) reaches the same off-ladder surface.
830
+ receiverDiagnostics.push(...phaseResult.diagnostics);
831
+ // Strip the migrated rules' legacy emission from every analyzer result —
832
+ // the phase model is their single source of truth now (§15), and the legacy
833
+ // factories that keep running for their infra side-effect (styles reducer,
834
+ // schema-code visitor) still emit their migrated findings alongside. This is
835
+ // a direct projection of MIGRATED_RULES, not a route split: there is no
836
+ // "phase vs legacy" choice anymore, only discarding a stale duplicate.
837
+ const migratedRules = new Set(MIGRATED_RULES.map((r) => r.id));
792
838
  for (const result of Object.values(analyzerResults)) {
793
- const before = result.violations.length;
794
- result.violations = result.violations.filter((v) => !migrated.has(v.rule));
795
- strippedCount += before - result.violations.length;
839
+ result.violations = result.violations.filter((v) => !migratedRules.has(v.rule));
796
840
  }
797
841
  // Re-emit phase findings as violations, bucketed by the rule's analyzer.
798
842
  const byAnalyzer = new Map();
@@ -842,13 +886,8 @@ async function applyPhaseModelSplit(inputs) {
842
886
  analyzerResults[analyzer] = makeEmptyAnalyzerResult(analyzer, phaseFiles.length);
843
887
  }
844
888
  }
845
- logMcpInfo('analysis', 'phase model (both paths)', {
846
- migrated: migrated.size,
847
- stripped: strippedCount,
848
- reemitted: phaseFindings.length,
849
- });
850
889
  }
851
- return { phaseFindings, phaseIncompleteFacts, routeAttribution, receiverDiagnostics };
890
+ return { phaseFindings, phaseIncompleteFacts, phaseReachability, receiverDiagnostics };
852
891
  }
853
892
  /**
854
893
  * Spec 69 §10 + Part 2b — re-derive the pre-pass's coverage diagnostics from the
@@ -968,29 +1007,6 @@ export async function persistDryPairs(dryPairs, root) {
968
1007
  // dry_pair_history persistence is advisory — non-fatal
969
1008
  }
970
1009
  }
971
- /**
972
- * Run the React analyzer's cross-component finalization (require-error-boundary
973
- * / raw-element checks), appending extra violations into the `react` result.
974
- */
975
- async function finalizeReact(reactBundle, analyzerResults, pipelineAnalyzerConfig) {
976
- if (reactBundle) {
977
- try {
978
- const reactResult = analyzerResults['react'];
979
- if (reactResult) {
980
- // Pass the react namespace (not the whole namespaced config) so
981
- // finalize reads `requireErrorBoundaries`/`rawElementCheck` at the
982
- // same level the visitor's `context.config` does.
983
- const extraViolations = await reactBundle.finalizeCrossComponent(pipelineAnalyzerConfig['react'] ?? {});
984
- reactResult.violations.push(...extraViolations);
985
- }
986
- }
987
- catch (err) {
988
- logMcpInfo('analysis', 'react finalization failed (non-fatal)', {
989
- error: err instanceof Error ? err.message : String(err)
990
- });
991
- }
992
- }
993
- }
994
1010
  /**
995
1011
  * Build the pipeline, run it, and fold its outputs — plus the both-paths phase
996
1012
  * split and derived coverage — into a single result bundle for `run`.
@@ -1001,7 +1017,7 @@ async function runPipelineStage(inputs) {
1001
1017
  let indexFactsWritten = 0;
1002
1018
  let writeIndexFactsMs = 0;
1003
1019
  const { auditIndex, pipelineIndexHandle, indexSkipDiagnostic } = await initializeAuditIndex(root);
1004
- const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers, reactBundle, solidBundle, } = buildPipelineAdapters(analyzers);
1020
+ const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers, } = buildPipelineAdapters(analyzers);
1005
1021
  // ── Safeguard warnings ────────────────────────────────────────────────
1006
1022
  if (auditIndex && analyzers.includes('cross-domain')) {
1007
1023
  const suCount = auditIndex.rawSql.count('schema_usage');
@@ -1017,6 +1033,7 @@ async function runPipelineStage(inputs) {
1017
1033
  // Outputs hoisted so derived coverage (after the split) can read them.
1018
1034
  let phaseFindings = [];
1019
1035
  let phaseIncompleteFacts = new Map();
1036
+ let phaseReachability;
1020
1037
  let pipelineCoverage;
1021
1038
  let pipelineTableCatalog;
1022
1039
  let pipelineStageTiming;
@@ -1029,7 +1046,6 @@ async function runPipelineStage(inputs) {
1029
1046
  let pipelineTestCoverage;
1030
1047
  let pipelineDeadClusters;
1031
1048
  let pipelineSizeDistributions;
1032
- let routeAttribution;
1033
1049
  let receiverDiagnostics = [];
1034
1050
  if (hasPipelineAnalyzers) {
1035
1051
  const pipelineAnalyzerConfig = buildPipelineAnalyzerConfig(analyzers, mergedOptions, root, files, corpusFiles, provenanceTiming);
@@ -1058,13 +1074,12 @@ async function runPipelineStage(inputs) {
1058
1074
  status: ar.status?.status,
1059
1075
  });
1060
1076
  }
1061
- await finalizeReact(reactBundle, analyzerResults, pipelineAnalyzerConfig);
1062
- const split = await applyPhaseModelSplit({
1077
+ const split = await runPhaseModelStage({
1063
1078
  mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle,
1064
1079
  });
1065
1080
  phaseFindings = split.phaseFindings;
1066
1081
  phaseIncompleteFacts = split.phaseIncompleteFacts;
1067
- routeAttribution = split.routeAttribution;
1082
+ phaseReachability = split.phaseReachability;
1068
1083
  receiverDiagnostics = split.receiverDiagnostics;
1069
1084
  // Spec 68 §8 — derived coverage, computed AFTER the split so phaseFindings
1070
1085
  // holds the migrated rules' re-emitted findings.
@@ -1072,7 +1087,7 @@ async function runPipelineStage(inputs) {
1072
1087
  rules: MIGRATED_RULES,
1073
1088
  findings: phaseFindings,
1074
1089
  presentFormats: presentFormatsOf(files),
1075
- enabledRules: enabledMigratedRules(),
1090
+ enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
1076
1091
  incompleteFacts: phaseIncompleteFacts,
1077
1092
  groupOf: (ruleId) => RULE_ANALYZER.get(ruleId) ?? ruleId,
1078
1093
  });
@@ -1094,11 +1109,6 @@ async function runPipelineStage(inputs) {
1094
1109
  ];
1095
1110
  pipelineTestCoverage = pipelineResult.metadata?.testCoverage;
1096
1111
  pipelineDeadClusters = pipelineResult.metadata?.deadClusters;
1097
- // Spec 60 R2 — aggregate the SOLID analyzer's raw size readings.
1098
- if (solidBundle) {
1099
- const samples = await solidBundle.getSizeSamples();
1100
- pipelineSizeDistributions = computeSizeDistributions(samples);
1101
- }
1102
1112
  }
1103
1113
  catch (error) {
1104
1114
  if (error instanceof AuditAbortedError) {
@@ -1144,6 +1154,7 @@ async function runPipelineStage(inputs) {
1144
1154
  analyzerResults,
1145
1155
  phaseFindings,
1146
1156
  phaseIncompleteFacts,
1157
+ phaseReachability,
1147
1158
  pipelineCoverage,
1148
1159
  pipelineTableCatalog,
1149
1160
  pipelineStageTiming,
@@ -1156,7 +1167,6 @@ async function runPipelineStage(inputs) {
1156
1167
  pipelineTestCoverage,
1157
1168
  pipelineDeadClusters,
1158
1169
  pipelineSizeDistributions,
1159
- routeAttribution,
1160
1170
  indexFactsWritten,
1161
1171
  writeIndexFactsMs,
1162
1172
  };
@@ -1260,75 +1270,40 @@ function stampAnalyzerFields(ordered) {
1260
1270
  }
1261
1271
  }
1262
1272
  /**
1263
- * Spec 13 R2 — attach hotspot + reachability scores to every violation and
1264
- * reorder within severity tiers (live code first, then higher hotspot, original
1265
- * order as tiebreaker). Advisory — failure is non-fatal (no churn/hotspot data
1266
- * is common on a first run).
1273
+ * Spec 13 R2 — attach a reachability score to every violation and reorder within
1274
+ * severity tiers (live code first: entry point → imported → dead), original
1275
+ * order as tiebreaker. Derived from the phase `reachability` fact (reverse
1276
+ * import adjacency + package entry points); no DB read. Advisory — a missing
1277
+ * fact (a run where the phase model produced no reachability) leaves every
1278
+ * finding at the neutral 0.5 and preserves original order.
1267
1279
  */
1268
- async function applyHotspotScoring(ordered, root) {
1269
- try {
1270
- const indexDb = CodeIndexDB.getInstance(undefined, root);
1271
- await indexDb.initialize();
1272
- const hotspotRows = indexDb.rawDb
1273
- .prepare('SELECT target, type, score FROM hotspot_scores')
1274
- .all();
1275
- const hotspotByTarget = new Map();
1276
- const hotspotByFile = new Map();
1277
- for (const row of hotspotRows) {
1278
- hotspotByTarget.set(row.target, row.score);
1279
- if (row.type === 'file') {
1280
- hotspotByFile.set(row.target, row.score);
1281
- }
1282
- }
1283
- const reachabilityByFile = new Map();
1284
- const reachRows = indexDb.rawDb
1285
- .prepare("SELECT node_key, weight FROM graph_cache WHERE graph_type = 'reachability'")
1286
- .all();
1287
- for (const row of reachRows) {
1288
- reachabilityByFile.set(row.node_key, row.weight);
1289
- }
1290
- for (const analyzerName of Object.keys(ordered)) {
1291
- const result = ordered[analyzerName];
1292
- const violations = result.violations;
1293
- for (const v of violations) {
1294
- const funcName = v.symbol;
1295
- const file = v.file;
1296
- // Try function-level hotspot first: "filePath::symbol"
1297
- const funcTarget = funcName ? `${file}::${funcName}` : undefined;
1298
- v.hotspot = (funcTarget ? hotspotByTarget.get(funcTarget) : undefined)
1299
- ?? hotspotByFile.get(file)
1300
- ?? 0;
1301
- // Files with no cross-language entities (no reachability data) get a
1302
- // neutral 0.5 so we never demote a finding we know nothing about.
1303
- v.reachability = reachabilityByFile.get(file) ?? 0.5;
1304
- }
1305
- const severityOrder = {
1306
- critical: 0,
1307
- severe: 1,
1308
- high: 2,
1309
- info: 3,
1310
- };
1311
- const indexed = violations.map((v, i) => ({ v, i }));
1312
- indexed.sort((a, b) => {
1313
- const sevA = severityOrder[a.v.severity] ?? 99;
1314
- const sevB = severityOrder[b.v.severity] ?? 99;
1315
- if (sevA !== sevB)
1316
- return sevA - sevB;
1317
- const reachA = a.v.reachability ?? 0.5;
1318
- const reachB = b.v.reachability ?? 0.5;
1319
- if (reachA !== reachB)
1320
- return reachB - reachA;
1321
- const hsA = a.v.hotspot ?? 0;
1322
- const hsB = b.v.hotspot ?? 0;
1323
- if (hsA !== hsB)
1324
- return hsB - hsA;
1325
- return a.i - b.i;
1326
- });
1327
- result.violations = indexed.map((x) => x.v);
1280
+ function applyReachabilityRanking(ordered, reachabilityByFile) {
1281
+ const severityOrder = {
1282
+ critical: 0,
1283
+ severe: 1,
1284
+ high: 2,
1285
+ info: 3,
1286
+ };
1287
+ for (const result of Object.values(ordered)) {
1288
+ const violations = result.violations;
1289
+ for (const v of violations) {
1290
+ // A file absent from the corpus graph gets a neutral 0.5 so we never
1291
+ // demote a finding we know nothing about.
1292
+ v.reachability = reachabilityByFile?.get(v.file) ?? 0.5;
1328
1293
  }
1329
- }
1330
- catch {
1331
- // Hotspot/reachability reordering is advisory — failure is non-fatal
1294
+ const indexed = violations.map((v, i) => ({ v, i }));
1295
+ indexed.sort((a, b) => {
1296
+ const sevA = severityOrder[a.v.severity] ?? 99;
1297
+ const sevB = severityOrder[b.v.severity] ?? 99;
1298
+ if (sevA !== sevB)
1299
+ return sevA - sevB;
1300
+ const reachA = a.v.reachability ?? 0.5;
1301
+ const reachB = b.v.reachability ?? 0.5;
1302
+ if (reachA !== reachB)
1303
+ return reachB - reachA;
1304
+ return a.i - b.i;
1305
+ });
1306
+ result.violations = indexed.map((x) => x.v);
1332
1307
  }
1333
1308
  }
1334
1309
  /**
@@ -1375,7 +1350,7 @@ function classifyBaselineFindings(ordered, mergedOptions, files, isScoped) {
1375
1350
  * Spec 60 size/test/distribution reports), and hook-contract-clean violations.
1376
1351
  */
1377
1352
  function buildAuditResult(inputs) {
1378
- const { ordered, files, analyzers, mergedOptions, isScoped, scopeResultType, indexFactsWritten, writeIndexFactsMs, startTime, startCpu, provenanceTiming, blastRadius, zeroFilesDiagnostics, pipelineDiagnostics, rejected, lintConfigDiagnostics, configPath, baselineMetadata, pipelineCoverage, routeAttribution, pipelineTableCatalog, pipelineStageTiming, thresholdChanges, thresholdSources, pipelineSkippedFiles, pipelineUnparsedFiles, skippedExtensions, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, collectedFunctions, fileToFunctionsMap, } = inputs;
1353
+ const { ordered, files, analyzers, mergedOptions, isScoped, scopeResultType, indexFactsWritten, writeIndexFactsMs, startTime, startCpu, provenanceTiming, blastRadius, changedScopeNoBaseline, zeroFilesDiagnostics, pipelineDiagnostics, rejected, lintConfigDiagnostics, configPath, baselineMetadata, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, thresholdChanges, thresholdSources, pipelineSkippedFiles, pipelineUnparsedFiles, skippedExtensions, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, collectedFunctions, fileToFunctionsMap, } = inputs;
1379
1354
  // Spec 69 §10 R4 — count query-shaped call sites whose DB receiver is
1380
1355
  // unproven, keyed by the analyzer that owns the receiver-resolution
1381
1356
  // instrument (`schema`). This is the site-level `cannot-fire` surface that
@@ -1386,6 +1361,19 @@ function buildAuditResult(inputs) {
1386
1361
  continue;
1387
1362
  unprovenSitesByAnalyzer.set(d.analyzerName, (unprovenSitesByAnalyzer.get(d.analyzerName) ?? 0) + 1);
1388
1363
  }
1364
+ // A bare `changed` scope that found no baseline to diff against (an empty
1365
+ // `functions` table) is reported as an uncomputable diff, never as a clean
1366
+ // run. The diagnostic names both possible causes: the index was never
1367
+ // populated, or it was discarded (e.g. after corruption).
1368
+ const noBaselineDiagnostics = changedScopeNoBaseline
1369
+ ? [{
1370
+ analyzerName: 'code-index',
1371
+ kind: 'no-baseline',
1372
+ message: 'Cannot determine what changed: the code index holds no baseline to diff against ' +
1373
+ '(no previously indexed files). No index has been built yet, or a prior index was ' +
1374
+ 'discarded. Run a full audit to build the baseline, then re-run `changed`.',
1375
+ }]
1376
+ : [];
1389
1377
  const summary = generateSummary(ordered, files.length, unprovenSitesByAnalyzer);
1390
1378
  return {
1391
1379
  timestamp: new Date(),
@@ -1403,8 +1391,9 @@ function buildAuditResult(inputs) {
1403
1391
  scope: scopeResultType,
1404
1392
  provenanceResolutionMs: provenanceTiming.totalMs,
1405
1393
  ...(blastRadius && { blastRadius }),
1406
- ...((zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
1394
+ ...((noBaselineDiagnostics.length > 0 || zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
1407
1395
  diagnostics: [
1396
+ ...noBaselineDiagnostics,
1408
1397
  ...zeroFilesDiagnostics,
1409
1398
  ...(pipelineDiagnostics ?? []),
1410
1399
  ...lintConfigDiagnostics,
@@ -1419,7 +1408,6 @@ function buildAuditResult(inputs) {
1419
1408
  }),
1420
1409
  ...(baselineMetadata && { baseline: baselineMetadata }),
1421
1410
  ...(pipelineCoverage && { coverage: pipelineCoverage }),
1422
- ...(routeAttribution && { routeAttribution }),
1423
1411
  ...(pipelineTableCatalog && { tableCatalog: pipelineTableCatalog }),
1424
1412
  ...(pipelineStageTiming && { stageTiming: pipelineStageTiming }),
1425
1413
  ...(thresholdChanges.length > 0 && { thresholdChanges }),
@@ -1498,7 +1486,7 @@ export function createAuditRunner(options = {}) {
1498
1486
  // discovery (full `all` scope) and the pipeline (stage 1/2) so every touched
1499
1487
  // file lands in exactly one terminal state and the balance can be asserted.
1500
1488
  const fileAccounting = new FileAccounting();
1501
- const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
1489
+ const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, changedScopeNoBaseline, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
1502
1490
  const { collectedFunctions, fileToFunctionsMap } = await collectIndexedFunctions(mergedOptions, files);
1503
1491
  // Run analyzers
1504
1492
  // Spec 68 §15 — no selection model: the full pipeline always runs. `indexOnly`
@@ -1516,7 +1504,7 @@ export function createAuditRunner(options = {}) {
1516
1504
  mergedOptions, root, files, corpusFiles, isScoped, fileAccounting,
1517
1505
  styleConsumedFiles, styleContributingFiles, analyzers, provenanceTiming,
1518
1506
  });
1519
- const { analyzerResults, phaseFindings, phaseIncompleteFacts, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, routeAttribution, indexFactsWritten, writeIndexFactsMs, } = pipeline;
1507
+ const { analyzerResults, phaseFindings, phaseIncompleteFacts, phaseReachability, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, indexFactsWritten, writeIndexFactsMs, } = pipeline;
1520
1508
  // ── Zero-files diagnostic ─────────────────────────────────────────────
1521
1509
  // Extracted to runZeroFilesDiagnostics() for testability. Runs against the
1522
1510
  // raw analyzerResults before truthiness filtering so the "no result" pass
@@ -1532,8 +1520,8 @@ export function createAuditRunner(options = {}) {
1532
1520
  // Normalize the analyzer field on every violation to its result key (the
1533
1521
  // single source of truth — see stampAnalyzerFields).
1534
1522
  stampAnalyzerFields(orderedAnalyzerResults);
1535
- // Spec 13 R2 — hotspot scoring & finding reordering (advisory).
1536
- await applyHotspotScoring(orderedAnalyzerResults, root);
1523
+ // Spec 13 R2 — reachability scoring & finding reordering (advisory).
1524
+ applyReachabilityRanking(orderedAnalyzerResults, phaseReachability);
1537
1525
  // Hook-contract guard: no violation may carry an empty file path, line 0, or
1538
1526
  // missing line. A sentinel violation with file:'' broke the Claude Code hook's
1539
1527
  // JSON consumer (Spec 15 regression); line:0 from CrossDomainAnalyzer broke it
@@ -1558,6 +1546,7 @@ export function createAuditRunner(options = {}) {
1558
1546
  startCpu,
1559
1547
  provenanceTiming,
1560
1548
  blastRadius,
1549
+ changedScopeNoBaseline,
1561
1550
  zeroFilesDiagnostics,
1562
1551
  pipelineDiagnostics,
1563
1552
  rejected,
@@ -1565,7 +1554,6 @@ export function createAuditRunner(options = {}) {
1565
1554
  configPath,
1566
1555
  baselineMetadata,
1567
1556
  pipelineCoverage,
1568
- routeAttribution,
1569
1557
  pipelineTableCatalog,
1570
1558
  pipelineStageTiming,
1571
1559
  thresholdChanges,
@@ -1765,6 +1753,7 @@ export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByA
1765
1753
  let criticalIssues = 0;
1766
1754
  let severe = 0;
1767
1755
  let high = 0;
1756
+ let heldOut = 0;
1768
1757
  const violationsByCategory = {};
1769
1758
  const byAnalyzer = {};
1770
1759
  for (const [analyzer, result] of Object.entries(analyzerResults)) {
@@ -1772,6 +1761,8 @@ export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByA
1772
1761
  for (const violation of result.violations) {
1773
1762
  totalViolations++;
1774
1763
  analyzerViolations++;
1764
+ if (violation.gateExcluded)
1765
+ heldOut++;
1775
1766
  switch (violation.severity) {
1776
1767
  case 'critical':
1777
1768
  criticalIssues++;
@@ -1798,6 +1789,12 @@ export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByA
1798
1789
  .sort(([, a], [, b]) => b - a)
1799
1790
  .slice(0, 5)
1800
1791
  .map(([type, count]) => ({ type, count }));
1792
+ // Spec 69 §10 R4 follow-up — the site-level cannot-fire total, summed across
1793
+ // analyzers, so the headline can state findings + unproven sites + held-out
1794
+ // readings together without re-deriving from per-analyzer buckets.
1795
+ let unprovenSites = 0;
1796
+ for (const n of unprovenSitesByAnalyzer.values())
1797
+ unprovenSites += n;
1801
1798
  return {
1802
1799
  totalFiles: filesAnalyzed,
1803
1800
  totalViolations,
@@ -1806,7 +1803,9 @@ export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByA
1806
1803
  high,
1807
1804
  violationsByCategory,
1808
1805
  byAnalyzer,
1809
- topIssues
1806
+ topIssues,
1807
+ unprovenSites,
1808
+ heldOut
1810
1809
  };
1811
1810
  }
1812
1811
  /**