code-auditor-mcp 5.0.2 → 5.3.3

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 (419) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +326 -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/provenance.d.ts +9 -4
  12. package/dist/analyzers/provenance.d.ts.map +1 -1
  13. package/dist/analyzers/provenance.js +8 -25
  14. package/dist/analyzers/provenance.js.map +1 -1
  15. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  16. package/dist/analyzers/reactAnalyzer.js +35 -14
  17. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  18. package/dist/analyzers/ruleRegistry.js +4 -4
  19. package/dist/analyzers/ruleRegistry.js.map +1 -1
  20. package/dist/analyzers/ruleTiming.d.ts +1 -1
  21. package/dist/analyzers/ruleTiming.js +2 -2
  22. package/dist/analyzers/ruleTiming.js.map +1 -1
  23. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  24. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +22 -26
  25. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  26. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +0 -2
  27. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  28. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +4 -5
  29. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  30. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +32 -15
  31. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  32. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +189 -115
  33. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  34. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -2
  35. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  36. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +0 -5
  37. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  38. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +2 -10
  39. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  40. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +7 -49
  41. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  42. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
  43. package/dist/analyzers/universal/schema/codeAnalysis.js +51 -3
  44. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
  45. package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
  46. package/dist/analyzers/universal/schema/config.js +0 -2
  47. package/dist/analyzers/universal/schema/config.js.map +1 -1
  48. package/dist/analyzers/universal/schema/types.d.ts +0 -2
  49. package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
  50. package/dist/analyzers/vendorSurfaces.d.ts +126 -0
  51. package/dist/analyzers/vendorSurfaces.d.ts.map +1 -0
  52. package/dist/analyzers/vendorSurfaces.js +580 -0
  53. package/dist/analyzers/vendorSurfaces.js.map +1 -0
  54. package/dist/auditRunner.d.ts +9 -1
  55. package/dist/auditRunner.d.ts.map +1 -1
  56. package/dist/auditRunner.js +198 -146
  57. package/dist/auditRunner.js.map +1 -1
  58. package/dist/cacheGc.d.ts +88 -0
  59. package/dist/cacheGc.d.ts.map +1 -0
  60. package/dist/cacheGc.js +196 -0
  61. package/dist/cacheGc.js.map +1 -0
  62. package/dist/churn/churnExtractor.d.ts +0 -1
  63. package/dist/churn/churnExtractor.d.ts.map +1 -1
  64. package/dist/churn/churnExtractor.js +2 -4
  65. package/dist/churn/churnExtractor.js.map +1 -1
  66. package/dist/cli.d.ts +1 -1
  67. package/dist/cli.d.ts.map +1 -1
  68. package/dist/cli.js +260 -47
  69. package/dist/cli.js.map +1 -1
  70. package/dist/codeIndex/migrations.d.ts.map +1 -1
  71. package/dist/codeIndex/migrations.js +4 -3
  72. package/dist/codeIndex/migrations.js.map +1 -1
  73. package/dist/codeIndexDB.d.ts +11 -2
  74. package/dist/codeIndexDB.d.ts.map +1 -1
  75. package/dist/codeIndexDB.js +27 -2
  76. package/dist/codeIndexDB.js.map +1 -1
  77. package/dist/config/configLoader.d.ts +60 -7
  78. package/dist/config/configLoader.d.ts.map +1 -1
  79. package/dist/config/configLoader.js +252 -60
  80. package/dist/config/configLoader.js.map +1 -1
  81. package/dist/config/defaults.d.ts +137 -43
  82. package/dist/config/defaults.d.ts.map +1 -1
  83. package/dist/config/defaults.js +174 -101
  84. package/dist/config/defaults.js.map +1 -1
  85. package/dist/config/effectiveConfig.d.ts +1 -1
  86. package/dist/config/effectiveConfig.d.ts.map +1 -1
  87. package/dist/config/effectiveConfig.js +4 -6
  88. package/dist/config/effectiveConfig.js.map +1 -1
  89. package/dist/config/pathProfiles.d.ts +4 -3
  90. package/dist/config/pathProfiles.d.ts.map +1 -1
  91. package/dist/config/pathProfiles.js +3 -2
  92. package/dist/config/pathProfiles.js.map +1 -1
  93. package/dist/constants.d.ts +2 -1
  94. package/dist/constants.d.ts.map +1 -1
  95. package/dist/constants.js +4 -0
  96. package/dist/constants.js.map +1 -1
  97. package/dist/daemon/main.d.ts +0 -1
  98. package/dist/daemon/main.d.ts.map +1 -1
  99. package/dist/daemon/main.js +11 -4
  100. package/dist/daemon/main.js.map +1 -1
  101. package/dist/dataPaths.d.ts +32 -9
  102. package/dist/dataPaths.d.ts.map +1 -1
  103. package/dist/dataPaths.js +69 -16
  104. package/dist/dataPaths.js.map +1 -1
  105. package/dist/hookLog.d.ts +54 -0
  106. package/dist/hookLog.d.ts.map +1 -0
  107. package/dist/hookLog.js +105 -0
  108. package/dist/hookLog.js.map +1 -0
  109. package/dist/hookLogPath.generated.d.ts +3 -0
  110. package/dist/hookLogPath.generated.d.ts.map +1 -0
  111. package/dist/hookLogPath.generated.js +8 -0
  112. package/dist/hookLogPath.generated.js.map +1 -0
  113. package/dist/index.d.ts +0 -5
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +0 -7
  116. package/dist/index.js.map +1 -1
  117. package/dist/installConfig.d.ts +15 -2
  118. package/dist/installConfig.d.ts.map +1 -1
  119. package/dist/installConfig.js +17 -2
  120. package/dist/installConfig.js.map +1 -1
  121. package/dist/installer.d.ts.map +1 -1
  122. package/dist/installer.js +2 -1
  123. package/dist/installer.js.map +1 -1
  124. package/dist/languages/adapterBridge.d.ts +11 -0
  125. package/dist/languages/adapterBridge.d.ts.map +1 -1
  126. package/dist/languages/adapterBridge.js +11 -0
  127. package/dist/languages/adapterBridge.js.map +1 -1
  128. package/dist/languages/types.d.ts +8 -0
  129. package/dist/languages/types.d.ts.map +1 -1
  130. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +13 -0
  131. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  132. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +135 -18
  133. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  134. package/dist/ledger.d.ts +7 -0
  135. package/dist/ledger.d.ts.map +1 -1
  136. package/dist/ledger.js +7 -0
  137. package/dist/ledger.js.map +1 -1
  138. package/dist/mcp-tools/workflowGuide.js +8 -8
  139. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  140. package/dist/mcp-tools-shared.d.ts.map +1 -1
  141. package/dist/mcp-tools-shared.js +5 -2
  142. package/dist/mcp-tools-shared.js.map +1 -1
  143. package/dist/mcp-ui-simple.d.ts +5 -0
  144. package/dist/mcp-ui-simple.d.ts.map +1 -1
  145. package/dist/mcp-ui-simple.js +9 -2
  146. package/dist/mcp-ui-simple.js.map +1 -1
  147. package/dist/mcp.d.ts +0 -1
  148. package/dist/mcp.d.ts.map +1 -1
  149. package/dist/mcp.js +36 -33
  150. package/dist/mcp.js.map +1 -1
  151. package/dist/mcpAuditJobs.d.ts +10 -0
  152. package/dist/mcpAuditJobs.d.ts.map +1 -1
  153. package/dist/mcpAuditJobs.js +10 -1
  154. package/dist/mcpAuditJobs.js.map +1 -1
  155. package/dist/mcpAutoIndex.d.ts.map +1 -1
  156. package/dist/mcpAutoIndex.js +2 -1
  157. package/dist/mcpAutoIndex.js.map +1 -1
  158. package/dist/mcpDiagnostics.d.ts +2 -0
  159. package/dist/mcpDiagnostics.d.ts.map +1 -1
  160. package/dist/mcpDiagnostics.js +2 -0
  161. package/dist/mcpDiagnostics.js.map +1 -1
  162. package/dist/phase/candidates.d.ts +53 -0
  163. package/dist/phase/candidates.d.ts.map +1 -0
  164. package/dist/phase/candidates.js +67 -0
  165. package/dist/phase/candidates.js.map +1 -0
  166. package/dist/phase/colorMath.d.ts +18 -5
  167. package/dist/phase/colorMath.d.ts.map +1 -1
  168. package/dist/phase/colorMath.js +88 -5
  169. package/dist/phase/colorMath.js.map +1 -1
  170. package/dist/phase/dataAccessCallsCandidates.js +1 -1
  171. package/dist/phase/dataAccessCallsCandidates.js.map +1 -1
  172. package/dist/phase/goFunctionAnalysis.d.ts +2 -2
  173. package/dist/phase/goFunctionAnalysis.js +2 -2
  174. package/dist/phase/goFunctions.d.ts +2 -2
  175. package/dist/phase/goFunctions.js +2 -2
  176. package/dist/phase/goImports.d.ts +2 -2
  177. package/dist/phase/goImports.js +2 -2
  178. package/dist/phase/goSwitches.d.ts +2 -2
  179. package/dist/phase/goSwitches.js +2 -2
  180. package/dist/phase/imports.d.ts.map +1 -1
  181. package/dist/phase/imports.js +1 -0
  182. package/dist/phase/imports.js.map +1 -1
  183. package/dist/phase/loopQueryCandidates.js +1 -1
  184. package/dist/phase/loopQueryCandidates.js.map +1 -1
  185. package/dist/phase/oracles.d.ts +28 -0
  186. package/dist/phase/oracles.d.ts.map +1 -1
  187. package/dist/phase/oracles.js +28 -0
  188. package/dist/phase/oracles.js.map +1 -1
  189. package/dist/phase/phaseModel.d.ts +47 -8
  190. package/dist/phase/phaseModel.d.ts.map +1 -1
  191. package/dist/phase/phaseModel.js +44 -8
  192. package/dist/phase/phaseModel.js.map +1 -1
  193. package/dist/phase/producers.d.ts.map +1 -1
  194. package/dist/phase/producers.js +24 -17
  195. package/dist/phase/producers.js.map +1 -1
  196. package/dist/phase/reachability.d.ts +44 -1
  197. package/dist/phase/reachability.d.ts.map +1 -1
  198. package/dist/phase/reachability.js +79 -0
  199. package/dist/phase/reachability.js.map +1 -1
  200. package/dist/phase/rules/conventions.d.ts.map +1 -1
  201. package/dist/phase/rules/conventions.js +6 -2
  202. package/dist/phase/rules/conventions.js.map +1 -1
  203. package/dist/phase/rules/crossDomain.d.ts.map +1 -1
  204. package/dist/phase/rules/crossDomain.js +16 -6
  205. package/dist/phase/rules/crossDomain.js.map +1 -1
  206. package/dist/phase/rules/dataAccess.d.ts.map +1 -1
  207. package/dist/phase/rules/dataAccess.js +13 -0
  208. package/dist/phase/rules/dataAccess.js.map +1 -1
  209. package/dist/phase/rules/documentation.d.ts.map +1 -1
  210. package/dist/phase/rules/documentation.js +11 -1
  211. package/dist/phase/rules/documentation.js.map +1 -1
  212. package/dist/phase/rules/dry.d.ts +16 -7
  213. package/dist/phase/rules/dry.d.ts.map +1 -1
  214. package/dist/phase/rules/dry.js +51 -32
  215. package/dist/phase/rules/dry.js.map +1 -1
  216. package/dist/phase/rules/goRules.d.ts +2 -2
  217. package/dist/phase/rules/goRules.js +2 -2
  218. package/dist/phase/rules/secrets.d.ts +22 -8
  219. package/dist/phase/rules/secrets.d.ts.map +1 -1
  220. package/dist/phase/rules/secrets.js +75 -69
  221. package/dist/phase/rules/secrets.js.map +1 -1
  222. package/dist/phase/rules/styles.d.ts +23 -9
  223. package/dist/phase/rules/styles.d.ts.map +1 -1
  224. package/dist/phase/rules/styles.js +61 -15
  225. package/dist/phase/rules/styles.js.map +1 -1
  226. package/dist/phase/rules/unreferencedModule.d.ts.map +1 -1
  227. package/dist/phase/rules/unreferencedModule.js +32 -10
  228. package/dist/phase/rules/unreferencedModule.js.map +1 -1
  229. package/dist/phase/runner.d.ts +16 -7
  230. package/dist/phase/runner.d.ts.map +1 -1
  231. package/dist/phase/runner.js +32 -20
  232. package/dist/phase/runner.js.map +1 -1
  233. package/dist/phase/stringLiterals.d.ts.map +1 -1
  234. package/dist/phase/stringLiterals.js +16 -7
  235. package/dist/phase/stringLiterals.js.map +1 -1
  236. package/dist/phase/stylesCss.d.ts.map +1 -1
  237. package/dist/phase/stylesCss.js +4 -0
  238. package/dist/phase/stylesCss.js.map +1 -1
  239. package/dist/phase/stylesMarkup.d.ts.map +1 -1
  240. package/dist/phase/stylesMarkup.js +4 -0
  241. package/dist/phase/stylesMarkup.js.map +1 -1
  242. package/dist/phase/stylesSource.d.ts.map +1 -1
  243. package/dist/phase/stylesSource.js +6 -0
  244. package/dist/phase/stylesSource.js.map +1 -1
  245. package/dist/phase/testProgram.d.ts +57 -0
  246. package/dist/phase/testProgram.d.ts.map +1 -0
  247. package/dist/phase/testProgram.js +244 -0
  248. package/dist/phase/testProgram.js.map +1 -0
  249. package/dist/phase/typeDeclarations.d.ts +2 -2
  250. package/dist/phase/typeDeclarations.js +2 -2
  251. package/dist/phase/types.d.ts +104 -0
  252. package/dist/phase/types.d.ts.map +1 -1
  253. package/dist/pipeline.d.ts +4 -0
  254. package/dist/pipeline.d.ts.map +1 -1
  255. package/dist/pipeline.js +4 -0
  256. package/dist/pipeline.js.map +1 -1
  257. package/dist/pipelineAdapters.d.ts +1 -171
  258. package/dist/pipelineAdapters.d.ts.map +1 -1
  259. package/dist/pipelineAdapters.js +47 -1281
  260. package/dist/pipelineAdapters.js.map +1 -1
  261. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  262. package/dist/reporting/csvReportGenerator.js +0 -6
  263. package/dist/reporting/csvReportGenerator.js.map +1 -1
  264. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  265. package/dist/reporting/htmlReportGenerator.js +0 -4
  266. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  267. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  268. package/dist/reporting/jsonReportGenerator.js +0 -1
  269. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  270. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  271. package/dist/reporting/sarifReportGenerator.js +0 -7
  272. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  273. package/dist/services/auditJobService.d.ts +15 -0
  274. package/dist/services/auditJobService.d.ts.map +1 -1
  275. package/dist/services/auditJobService.js +15 -0
  276. package/dist/services/auditJobService.js.map +1 -1
  277. package/dist/services/projectTaskHelpers.d.ts +7 -0
  278. package/dist/services/projectTaskHelpers.d.ts.map +1 -1
  279. package/dist/services/projectTaskHelpers.js +7 -0
  280. package/dist/services/projectTaskHelpers.js.map +1 -1
  281. package/dist/services/whitelistService.d.ts +28 -0
  282. package/dist/services/whitelistService.d.ts.map +1 -1
  283. package/dist/services/whitelistService.js +28 -0
  284. package/dist/services/whitelistService.js.map +1 -1
  285. package/dist/sqlite/driver.d.ts +17 -1
  286. package/dist/sqlite/driver.d.ts.map +1 -1
  287. package/dist/sqlite/driver.js +28 -5
  288. package/dist/sqlite/driver.js.map +1 -1
  289. package/dist/styles/cssInJsHarvest.d.ts +36 -0
  290. package/dist/styles/cssInJsHarvest.d.ts.map +1 -0
  291. package/dist/styles/cssInJsHarvest.js +78 -0
  292. package/dist/styles/cssInJsHarvest.js.map +1 -0
  293. package/dist/styles/definedClasses.d.ts +95 -0
  294. package/dist/styles/definedClasses.d.ts.map +1 -0
  295. package/dist/styles/definedClasses.js +129 -0
  296. package/dist/styles/definedClasses.js.map +1 -0
  297. package/dist/styles/markupStyleHarvest.d.ts +33 -0
  298. package/dist/styles/markupStyleHarvest.d.ts.map +1 -0
  299. package/dist/styles/markupStyleHarvest.js +73 -0
  300. package/dist/styles/markupStyleHarvest.js.map +1 -0
  301. package/dist/styles/styleExtractor.d.ts +37 -0
  302. package/dist/styles/styleExtractor.d.ts.map +1 -1
  303. package/dist/styles/styleExtractor.js +89 -33
  304. package/dist/styles/styleExtractor.js.map +1 -1
  305. package/dist/styles/styleIndexer.d.ts.map +1 -1
  306. package/dist/styles/styleIndexer.js +11 -10
  307. package/dist/styles/styleIndexer.js.map +1 -1
  308. package/dist/styles/tailwindExpander.d.ts.map +1 -1
  309. package/dist/styles/tailwindExpander.js +31 -10
  310. package/dist/styles/tailwindExpander.js.map +1 -1
  311. package/dist/styles/tailwindUtilityExpander.d.ts +4 -0
  312. package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
  313. package/dist/styles/tailwindUtilityExpander.js +4 -0
  314. package/dist/styles/tailwindUtilityExpander.js.map +1 -1
  315. package/dist/telemetry.d.ts +67 -1
  316. package/dist/telemetry.d.ts.map +1 -1
  317. package/dist/telemetry.js +100 -20
  318. package/dist/telemetry.js.map +1 -1
  319. package/dist/types/crossLanguage.d.ts +1 -0
  320. package/dist/types/crossLanguage.d.ts.map +1 -1
  321. package/dist/types.d.ts +73 -56
  322. package/dist/types.d.ts.map +1 -1
  323. package/dist/types.js +4 -4
  324. package/dist/types.js.map +1 -1
  325. package/dist/version.generated.d.ts +1 -1
  326. package/dist/version.generated.js +1 -1
  327. package/examples/.auditrc.example.json +7 -22
  328. package/examples/.auditrc.json +12 -155
  329. package/examples/.code-auditor.json +8 -51
  330. package/examples/nextjs.auditrc.json +7 -34
  331. package/examples/node-api.auditrc.json +6 -46
  332. package/examples/react.auditrc.json +8 -25
  333. package/examples/tolerant-solid.auditrc.json +12 -44
  334. package/package.json +8 -5
  335. package/plugin/.claude-plugin/manifest.json +10 -9
  336. package/plugin/.claude-plugin/plugin.json +1 -1
  337. package/plugin/hooks/hooks.json +4 -4
  338. package/plugin/scripts/hook-audit.sh +25 -10
  339. package/plugin/scripts/hook-common.sh +12 -7
  340. package/plugin/scripts/hook-drift-check.sh +10 -1
  341. package/plugin/scripts/hook-log-path.sh +7 -0
  342. package/plugin/scripts/hook-self-audit.sh +21 -6
  343. package/plugin/scripts/hook-warm.sh +17 -11
  344. package/plugin/skills/code-auditor/SKILL.md +26 -3
  345. package/server.json +2 -2
  346. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +0 -6
  347. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +0 -1
  348. package/dist/__tests__/fixtures/spec-20/scripts/tool.js +0 -12
  349. package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +0 -1
  350. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +0 -7
  351. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +0 -1
  352. package/dist/__tests__/fixtures/spec-20/src/demo-module.js +0 -13
  353. package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +0 -1
  354. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts +0 -14
  355. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts.map +0 -1
  356. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js +0 -16
  357. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js.map +0 -1
  358. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts +0 -15
  359. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts.map +0 -1
  360. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js +0 -17
  361. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js.map +0 -1
  362. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +0 -14
  363. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +0 -1
  364. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +0 -19
  365. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +0 -1
  366. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +0 -18
  367. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +0 -1
  368. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +0 -19
  369. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +0 -1
  370. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts +0 -19
  371. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts.map +0 -1
  372. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js +0 -19
  373. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js.map +0 -1
  374. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +0 -17
  375. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +0 -1
  376. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js +0 -19
  377. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +0 -1
  378. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +0 -25
  379. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +0 -1
  380. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js +0 -21
  381. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +0 -1
  382. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts +0 -15
  383. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts.map +0 -1
  384. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js +0 -19
  385. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js.map +0 -1
  386. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts +0 -14
  387. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts.map +0 -1
  388. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js +0 -14
  389. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js.map +0 -1
  390. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +0 -15
  391. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +0 -1
  392. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js +0 -16
  393. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +0 -1
  394. package/dist/languages/go/analyzer-darwin-amd64 +0 -0
  395. package/dist/languages/go/analyzer-darwin-arm64 +0 -0
  396. package/dist/languages/go/analyzer-linux-amd64 +0 -0
  397. package/dist/languages/go/analyzer-linux-arm64 +0 -0
  398. package/dist/languages/go/analyzer-src/analyzer.go +0 -643
  399. package/dist/languages/go/analyzer-src/dataaccess.go +0 -388
  400. package/dist/languages/go/analyzer-src/go.mod +0 -3
  401. package/dist/languages/go/analyzer-src/indexer.go +0 -268
  402. package/dist/languages/go/analyzer-src/parser.go +0 -461
  403. package/dist/languages/go/analyzer-src/solid.go +0 -311
  404. package/dist/languages/go/analyzer-src/testconventions.go +0 -25
  405. package/dist/languages/go/analyzer-src/types.go +0 -156
  406. package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
  407. package/dist/languages/go/go.mod +0 -7
  408. package/dist/languages/go/main.go +0 -203
  409. package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
  410. package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
  411. package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
  412. package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
  413. package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
  414. package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
  415. package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
  416. package/dist/native-bootstrap.d.ts +0 -15
  417. package/dist/native-bootstrap.d.ts.map +0 -1
  418. package/dist/native-bootstrap.js +0 -75
  419. package/dist/native-bootstrap.js.map +0 -1
@@ -30,17 +30,18 @@ import { initializeOrmAdapters } from './analyzers/orm/index.js';
30
30
  import { syncStyleIndex } from './styles/styleIndexer.js';
31
31
  import { CodeIndexDB } from './codeIndexDB.js';
32
32
  import { notifyLegacyIndexLocation } from './legacyIndexNotice.js';
33
+ import { gcStaleCacheDirsOnce } from './cacheGc.js';
33
34
  import { writeAuditToLedger, detectRunInput } from './ledger.js';
34
35
  // Pipeline imports (Spec 25 — pipeline replaces hand-rolled analyzer loop)
35
36
  import { runPipeline, makeVisitorStatus, getFilesProcessed, isVisitorStatus } from './pipeline.js';
36
37
  import { getRuleTimingSortedDesc } from './analyzers/ruleTiming.js';
37
38
  import { createFunctionIndexVisitor, createStylesCssVisitor, createStylesSourceVisitor, createStylesReducer, createInvariantsReducer, createSchemaCodeVisitor, } from './pipelineAdapters.js';
38
- import { computeSizeDistributions } from './reporting/sizeDistribution.js';
39
- import { splitRoutes, attributeRoutes, enabledMigratedRules } from './phase/routing.js';
40
39
  import { runPhaseModel } from './phase/phaseModel.js';
40
+ import { resolveVendorSurfaces } from './analyzers/vendorSurfaces.js';
41
41
  import { resolvePhaseThresholds } from './phase/config.js';
42
42
  import { deriveCoverage, presentFormatsOf } from './phase/coverage.js';
43
43
  import { MIGRATED_RULES, RULE_ANALYZER } from './phase/rules/registry.js';
44
+ import { computeReachabilityScores } from './phase/reachability.js';
44
45
  import { seedDryPairs, resolveBlockConfig } from './phase/rules/dry.js';
45
46
  import { checkUnresolvedReceiverImports, checkUnprovenQueryReceivers, checkUnresolvedQueries, checkUnparseableSql, dedupeCannotFireByReceiver } from './analyzers/universal/schema/codeAnalysis.js';
46
47
  import { readProjectManifest, computeManifestStaleness } from './analyzers/manifestStaleness.js';
@@ -171,8 +172,10 @@ async function resolveRunConfig(baseOptions, runOptions) {
171
172
  lintThresholds,
172
173
  presets: resolvedPresets,
173
174
  });
174
- // Always merge built-in path profiles — corpus audits and projects without
175
- // .codeauditor.json must still get the built-in scripts-and-tests profile.
175
+ // Always merge built-in path profiles (none ship today — the compiled-in
176
+ // "scripts-and-tests" gate exemption was removed). This stays as the seam
177
+ // that would apply any future built-in profile to corpus audits and to
178
+ // projects without a .codeauditor.json.
176
179
  mergedOptions.pathProfiles = mergePathProfiles(mergedOptions.pathProfiles, mergedOptions.builtin);
177
180
  return {
178
181
  mergedOptions,
@@ -200,7 +203,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
200
203
  phase: 'discovery',
201
204
  message: `Discovering files... (scope: ${scopeResultType})`
202
205
  });
203
- const { files, skippedExtensions } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
206
+ const { files, skippedExtensions, changedScopeNoBaseline } = await resolveDiscoveryScope(mergedOptions, scope, fileAccounting);
204
207
  throwIfAborted(mergedOptions.abortSignal);
205
208
  // Detect changed functions for non-all scopes
206
209
  let changedFunctions;
@@ -228,6 +231,7 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
228
231
  scope,
229
232
  isScoped,
230
233
  scopeResultType,
234
+ changedScopeNoBaseline,
231
235
  };
232
236
  }
233
237
  /**
@@ -238,6 +242,11 @@ async function discoverAuditFiles(mergedOptions, root, fileAccounting) {
238
242
  */
239
243
  async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
240
244
  let files;
245
+ // True when a bare `changed` scope found no baseline in the index to diff
246
+ // against (an empty `functions` table). That is "the diff cannot be computed",
247
+ // not "nothing changed" — surfaced as a diagnostic so the run never reports a
248
+ // false clean. Explicit-files and all/other scopes leave this false.
249
+ let changedScopeNoBaseline = false;
241
250
  // Extensions present on disk that discovery skipped (Spec 43 R5 follow-up).
242
251
  // Populated only for the `all` scope — scoped runs are explicitly scoped by
243
252
  // the user, so "what wasn't analyzed" there is the scope itself, not the
@@ -269,12 +278,29 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
269
278
  error: err instanceof Error ? err.message : String(err)
270
279
  });
271
280
  }
272
- const modifiedFiles = mergedOptions.explicitFiles !== undefined
273
- ? mergedOptions.explicitFiles
274
- : (db ? await db.detectModifiedFiles(path.resolve(mergedOptions.projectRoot || process.cwd())) : []);
281
+ let modifiedFiles;
282
+ if (mergedOptions.explicitFiles !== undefined) {
283
+ modifiedFiles = mergedOptions.explicitFiles;
284
+ }
285
+ else if (db) {
286
+ // `undefined` here means *no baseline* (an empty `functions` table) — a
287
+ // different answer from `[]` ("baseline present, nothing modified"). The
288
+ // former is reported as an uncomputable diff, never as a clean run. The
289
+ // `db`-unavailable case (init threw, e.g. a future schema version) keeps
290
+ // its existing fail-open zero-file path and the later engine-error
291
+ // diagnostic.
292
+ const detected = await db.detectModifiedFiles(path.resolve(mergedOptions.projectRoot || process.cwd()));
293
+ if (detected === undefined)
294
+ changedScopeNoBaseline = true;
295
+ modifiedFiles = detected ?? [];
296
+ }
297
+ else {
298
+ modifiedFiles = [];
299
+ }
275
300
  files = [...new Set(modifiedFiles.map((f) => path.resolve(f)))].sort();
276
301
  logMcpInfo('discovery', 'changed scope resolved', {
277
- fileCount: files.length
302
+ fileCount: files.length,
303
+ noBaseline: changedScopeNoBaseline
278
304
  });
279
305
  }
280
306
  else if (Array.isArray(scope)) {
@@ -290,7 +316,7 @@ async function resolveDiscoveryScope(mergedOptions, scope, fileAccounting) {
290
316
  files = discovered.files;
291
317
  skippedExtensions = discovered.skippedExtensions;
292
318
  }
293
- return { files, skippedExtensions };
319
+ return { files, skippedExtensions, changedScopeNoBaseline };
294
320
  }
295
321
  /**
296
322
  * Detect changed functions for a scoped run via the code index (Spec 60.1).
@@ -527,8 +553,6 @@ function buildPipelineAdapters(analyzers) {
527
553
  const pipelineVisitors = [];
528
554
  const pipelineReducers = [];
529
555
  const pipelineDerivedReducers = [];
530
- let reactBundle;
531
- let solidBundle;
532
556
  // Always-on infrastructure: function-index visitor populates the
533
557
  // `functions` table so conventions + cross-domain reducers have data
534
558
  // even on a cold run with no prior index sync.
@@ -567,8 +591,6 @@ function buildPipelineAdapters(analyzers) {
567
591
  pipelineVisitors,
568
592
  pipelineReducers,
569
593
  pipelineDerivedReducers,
570
- reactBundle,
571
- solidBundle,
572
594
  };
573
595
  }
574
596
  /**
@@ -707,19 +729,20 @@ function buildPipelineConfig(inputs) {
707
729
  };
708
730
  }
709
731
  /**
710
- * Run the both-paths split (Spec 68 §11.1): strip migrated rules' legacy
711
- * emission and re-emit phase findings into the correct analyzer buckets.
712
- * Mutates `analyzerResults` and `pipelineAnalyzerConfig` (dry divergence).
732
+ * Run the phase model — the single analysis path (Spec 68 §15) — and re-emit its
733
+ * findings into the correct analyzer buckets. The legacy analyzer path is
734
+ * deleted; there is no route split to choose between. Mutates `analyzerResults`
735
+ * and `pipelineAnalyzerConfig` (dry divergence).
713
736
  */
714
- async function applyPhaseModelSplit(inputs) {
737
+ async function runPhaseModelStage(inputs) {
715
738
  const { mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle } = inputs;
716
739
  let phaseFindings = [];
717
740
  let phaseIncompleteFacts = new Map();
718
- let routeAttribution;
741
+ let phaseReachability;
719
742
  let receiverDiagnostics = [];
720
- const { migrated } = splitRoutes();
721
- routeAttribution = Object.fromEntries(attributeRoutes());
722
- if (migrated.size > 0) {
743
+ let phaseCandidates = [];
744
+ let receiverResolutions = 0;
745
+ if (MIGRATED_RULES.length > 0) {
723
746
  // The `dry/diverging-clone` rule reads its knobs from `dry.divergence`.
724
747
  const topLevelDivergence = mergedOptions.divergence;
725
748
  if (topLevelDivergence && !pipelineAnalyzerConfig['dry']?.divergence) {
@@ -758,12 +781,17 @@ async function applyPhaseModelSplit(inputs) {
758
781
  tsconfigAliases: infraConfig.tsconfigAliases,
759
782
  packageEntryPoints: infraConfig.packageEntryPoints,
760
783
  indexHandle: pipelineIndexHandle,
761
- enabledRules: enabledMigratedRules(),
784
+ enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
762
785
  externalTables,
763
786
  workerCount: resolveWorkerCount(),
764
787
  sqlDialect: infraConfig.sqlDialect,
765
788
  sqlDialectReason: infraConfig.sqlDialectReason,
766
789
  declaredTypePackages: infraConfig.declaredTypePackages,
790
+ // Item B (reissued) — resolve each vendor's declaration from the tree under
791
+ // analysis (its own node_modules / workspace path) once per run. The
792
+ // loop-query / data-access producers read these resolved surfaces; nothing
793
+ // about a vendor's API is committed and nothing is fetched at analysis time.
794
+ vendorSurfaces: resolveVendorSurfaces(root),
767
795
  // Spec 70 2b — the diverging-clone write: seed `dry_pair_history` from the
768
796
  // phase `code-block` fact (replacing `createDryVisitor`/`persistDryPairs`).
769
797
  persistDryPairHistory: (facts) => {
@@ -774,6 +802,13 @@ async function applyPhaseModelSplit(inputs) {
774
802
  });
775
803
  phaseFindings = phaseResult.findings;
776
804
  phaseIncompleteFacts = phaseResult.incompleteFacts;
805
+ phaseCandidates = [...phaseResult.candidates];
806
+ receiverResolutions = phaseResult.receiverResolutions;
807
+ const reachabilityFact = phaseResult.facts.get('reachability');
808
+ const fileImports = phaseResult.facts.get('file-imports');
809
+ phaseReachability = reachabilityFact && fileImports
810
+ ? computeReachabilityScores(fileImports, reachabilityFact)
811
+ : undefined;
777
812
  // Spec 69 §10 + Part 2b — re-home the pre-pass's diagnostics (deleted from
778
813
  // `pipeline.ts`) on the phase result. Cannot-fire: re-derive per file from
779
814
  // `phaseResult.unprovenQueryReceivers` + `phaseResult.unresolvedImports`, keyed
@@ -781,12 +816,34 @@ async function applyPhaseModelSplit(inputs) {
781
816
  // receivers/imports never reached it). Manifest-stale: the ecosystem-list
782
817
  // self-check, unchanged from the deleted `buildPipelineResult` loop.
783
818
  receiverDiagnostics = await buildReceiverDiagnostics(phaseResult.unprovenQueryReceivers, phaseResult.unresolvedImports, phaseResult.unresolvedQuerySites, phaseResult.unparseableSql, root);
784
- // Strip the migrated rules' legacy emission from every analyzer result.
785
- let strippedCount = 0;
819
+ // Spec 68 §8 — `unreferenced-module`'s unproven modules: a module with no
820
+ // resolved importer while the graph carries a computed-specifier dynamic
821
+ // import. The rule withholds the dead-code finding for these; this surfaces
822
+ // the disposition (with its reason) as a coverage diagnostic instead.
823
+ for (const m of phaseResult.unprovenModules) {
824
+ receiverDiagnostics.push({
825
+ analyzerName: 'dependency-graph',
826
+ kind: 'unresolved-dynamic-import',
827
+ message: m.reason,
828
+ file: m.file,
829
+ line: m.line,
830
+ });
831
+ }
832
+ // §15 — a rule that could not run (fail-open, missing dependency) reported
833
+ // itself on the phase result's diagnostic channel. Merge those alongside the
834
+ // receiver-diagnostics the pre-pass re-derived, so the styles
835
+ // `undefined-class-disabled` diagnostic (re-homed from the deleted
836
+ // `initTailwindProbe`) reaches the same off-ladder surface.
837
+ receiverDiagnostics.push(...phaseResult.diagnostics);
838
+ // Strip the migrated rules' legacy emission from every analyzer result —
839
+ // the phase model is their single source of truth now (§15), and the legacy
840
+ // factories that keep running for their infra side-effect (styles reducer,
841
+ // schema-code visitor) still emit their migrated findings alongside. This is
842
+ // a direct projection of MIGRATED_RULES, not a route split: there is no
843
+ // "phase vs legacy" choice anymore, only discarding a stale duplicate.
844
+ const migratedRules = new Set(MIGRATED_RULES.map((r) => r.id));
786
845
  for (const result of Object.values(analyzerResults)) {
787
- const before = result.violations.length;
788
- result.violations = result.violations.filter((v) => !migrated.has(v.rule));
789
- strippedCount += before - result.violations.length;
846
+ result.violations = result.violations.filter((v) => !migratedRules.has(v.rule));
790
847
  }
791
848
  // Re-emit phase findings as violations, bucketed by the rule's analyzer.
792
849
  const byAnalyzer = new Map();
@@ -836,13 +893,8 @@ async function applyPhaseModelSplit(inputs) {
836
893
  analyzerResults[analyzer] = makeEmptyAnalyzerResult(analyzer, phaseFiles.length);
837
894
  }
838
895
  }
839
- logMcpInfo('analysis', 'phase model (both paths)', {
840
- migrated: migrated.size,
841
- stripped: strippedCount,
842
- reemitted: phaseFindings.length,
843
- });
844
896
  }
845
- return { phaseFindings, phaseIncompleteFacts, routeAttribution, receiverDiagnostics };
897
+ return { phaseFindings, phaseIncompleteFacts, phaseReachability, receiverDiagnostics, phaseCandidates, receiverResolutions };
846
898
  }
847
899
  /**
848
900
  * Spec 69 §10 + Part 2b — re-derive the pre-pass's coverage diagnostics from the
@@ -962,29 +1014,6 @@ export async function persistDryPairs(dryPairs, root) {
962
1014
  // dry_pair_history persistence is advisory — non-fatal
963
1015
  }
964
1016
  }
965
- /**
966
- * Run the React analyzer's cross-component finalization (require-error-boundary
967
- * / raw-element checks), appending extra violations into the `react` result.
968
- */
969
- async function finalizeReact(reactBundle, analyzerResults, pipelineAnalyzerConfig) {
970
- if (reactBundle) {
971
- try {
972
- const reactResult = analyzerResults['react'];
973
- if (reactResult) {
974
- // Pass the react namespace (not the whole namespaced config) so
975
- // finalize reads `requireErrorBoundaries`/`rawElementCheck` at the
976
- // same level the visitor's `context.config` does.
977
- const extraViolations = await reactBundle.finalizeCrossComponent(pipelineAnalyzerConfig['react'] ?? {});
978
- reactResult.violations.push(...extraViolations);
979
- }
980
- }
981
- catch (err) {
982
- logMcpInfo('analysis', 'react finalization failed (non-fatal)', {
983
- error: err instanceof Error ? err.message : String(err)
984
- });
985
- }
986
- }
987
- }
988
1017
  /**
989
1018
  * Build the pipeline, run it, and fold its outputs — plus the both-paths phase
990
1019
  * split and derived coverage — into a single result bundle for `run`.
@@ -995,7 +1024,7 @@ async function runPipelineStage(inputs) {
995
1024
  let indexFactsWritten = 0;
996
1025
  let writeIndexFactsMs = 0;
997
1026
  const { auditIndex, pipelineIndexHandle, indexSkipDiagnostic } = await initializeAuditIndex(root);
998
- const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers, reactBundle, solidBundle, } = buildPipelineAdapters(analyzers);
1027
+ const { pipelineVisitors, pipelineReducers, pipelineDerivedReducers, } = buildPipelineAdapters(analyzers);
999
1028
  // ── Safeguard warnings ────────────────────────────────────────────────
1000
1029
  if (auditIndex && analyzers.includes('cross-domain')) {
1001
1030
  const suCount = auditIndex.rawSql.count('schema_usage');
@@ -1011,6 +1040,9 @@ async function runPipelineStage(inputs) {
1011
1040
  // Outputs hoisted so derived coverage (after the split) can read them.
1012
1041
  let phaseFindings = [];
1013
1042
  let phaseIncompleteFacts = new Map();
1043
+ let phaseReachability;
1044
+ let phaseCandidates = [];
1045
+ let receiverResolutions = 0;
1014
1046
  let pipelineCoverage;
1015
1047
  let pipelineTableCatalog;
1016
1048
  let pipelineStageTiming;
@@ -1023,7 +1055,6 @@ async function runPipelineStage(inputs) {
1023
1055
  let pipelineTestCoverage;
1024
1056
  let pipelineDeadClusters;
1025
1057
  let pipelineSizeDistributions;
1026
- let routeAttribution;
1027
1058
  let receiverDiagnostics = [];
1028
1059
  if (hasPipelineAnalyzers) {
1029
1060
  const pipelineAnalyzerConfig = buildPipelineAnalyzerConfig(analyzers, mergedOptions, root, files, corpusFiles, provenanceTiming);
@@ -1052,21 +1083,22 @@ async function runPipelineStage(inputs) {
1052
1083
  status: ar.status?.status,
1053
1084
  });
1054
1085
  }
1055
- await finalizeReact(reactBundle, analyzerResults, pipelineAnalyzerConfig);
1056
- const split = await applyPhaseModelSplit({
1086
+ const split = await runPhaseModelStage({
1057
1087
  mergedOptions, root, files, isScoped, pipelineAnalyzerConfig, analyzerResults, pipelineIndexHandle,
1058
1088
  });
1059
1089
  phaseFindings = split.phaseFindings;
1060
1090
  phaseIncompleteFacts = split.phaseIncompleteFacts;
1061
- routeAttribution = split.routeAttribution;
1091
+ phaseReachability = split.phaseReachability;
1062
1092
  receiverDiagnostics = split.receiverDiagnostics;
1093
+ phaseCandidates = split.phaseCandidates;
1094
+ receiverResolutions = split.receiverResolutions;
1063
1095
  // Spec 68 §8 — derived coverage, computed AFTER the split so phaseFindings
1064
1096
  // holds the migrated rules' re-emitted findings.
1065
1097
  pipelineCoverage = deriveCoverage({
1066
1098
  rules: MIGRATED_RULES,
1067
1099
  findings: phaseFindings,
1068
1100
  presentFormats: presentFormatsOf(files),
1069
- enabledRules: enabledMigratedRules(),
1101
+ enabledRules: new Set(MIGRATED_RULES.map((r) => r.id)),
1070
1102
  incompleteFacts: phaseIncompleteFacts,
1071
1103
  groupOf: (ruleId) => RULE_ANALYZER.get(ruleId) ?? ruleId,
1072
1104
  });
@@ -1088,11 +1120,6 @@ async function runPipelineStage(inputs) {
1088
1120
  ];
1089
1121
  pipelineTestCoverage = pipelineResult.metadata?.testCoverage;
1090
1122
  pipelineDeadClusters = pipelineResult.metadata?.deadClusters;
1091
- // Spec 60 R2 — aggregate the SOLID analyzer's raw size readings.
1092
- if (solidBundle) {
1093
- const samples = await solidBundle.getSizeSamples();
1094
- pipelineSizeDistributions = computeSizeDistributions(samples);
1095
- }
1096
1123
  }
1097
1124
  catch (error) {
1098
1125
  if (error instanceof AuditAbortedError) {
@@ -1138,6 +1165,9 @@ async function runPipelineStage(inputs) {
1138
1165
  analyzerResults,
1139
1166
  phaseFindings,
1140
1167
  phaseIncompleteFacts,
1168
+ phaseReachability,
1169
+ phaseCandidates,
1170
+ receiverResolutions,
1141
1171
  pipelineCoverage,
1142
1172
  pipelineTableCatalog,
1143
1173
  pipelineStageTiming,
@@ -1150,7 +1180,6 @@ async function runPipelineStage(inputs) {
1150
1180
  pipelineTestCoverage,
1151
1181
  pipelineDeadClusters,
1152
1182
  pipelineSizeDistributions,
1153
- routeAttribution,
1154
1183
  indexFactsWritten,
1155
1184
  writeIndexFactsMs,
1156
1185
  };
@@ -1254,75 +1283,40 @@ function stampAnalyzerFields(ordered) {
1254
1283
  }
1255
1284
  }
1256
1285
  /**
1257
- * Spec 13 R2 — attach hotspot + reachability scores to every violation and
1258
- * reorder within severity tiers (live code first, then higher hotspot, original
1259
- * order as tiebreaker). Advisory — failure is non-fatal (no churn/hotspot data
1260
- * is common on a first run).
1286
+ * Spec 13 R2 — attach a reachability score to every violation and reorder within
1287
+ * severity tiers (live code first: entry point → imported → dead), original
1288
+ * order as tiebreaker. Derived from the phase `reachability` fact (reverse
1289
+ * import adjacency + package entry points); no DB read. Advisory — a missing
1290
+ * fact (a run where the phase model produced no reachability) leaves every
1291
+ * finding at the neutral 0.5 and preserves original order.
1261
1292
  */
1262
- async function applyHotspotScoring(ordered, root) {
1263
- try {
1264
- const indexDb = CodeIndexDB.getInstance(undefined, root);
1265
- await indexDb.initialize();
1266
- const hotspotRows = indexDb.rawDb
1267
- .prepare('SELECT target, type, score FROM hotspot_scores')
1268
- .all();
1269
- const hotspotByTarget = new Map();
1270
- const hotspotByFile = new Map();
1271
- for (const row of hotspotRows) {
1272
- hotspotByTarget.set(row.target, row.score);
1273
- if (row.type === 'file') {
1274
- hotspotByFile.set(row.target, row.score);
1275
- }
1276
- }
1277
- const reachabilityByFile = new Map();
1278
- const reachRows = indexDb.rawDb
1279
- .prepare("SELECT node_key, weight FROM graph_cache WHERE graph_type = 'reachability'")
1280
- .all();
1281
- for (const row of reachRows) {
1282
- reachabilityByFile.set(row.node_key, row.weight);
1283
- }
1284
- for (const analyzerName of Object.keys(ordered)) {
1285
- const result = ordered[analyzerName];
1286
- const violations = result.violations;
1287
- for (const v of violations) {
1288
- const funcName = v.symbol;
1289
- const file = v.file;
1290
- // Try function-level hotspot first: "filePath::symbol"
1291
- const funcTarget = funcName ? `${file}::${funcName}` : undefined;
1292
- v.hotspot = (funcTarget ? hotspotByTarget.get(funcTarget) : undefined)
1293
- ?? hotspotByFile.get(file)
1294
- ?? 0;
1295
- // Files with no cross-language entities (no reachability data) get a
1296
- // neutral 0.5 so we never demote a finding we know nothing about.
1297
- v.reachability = reachabilityByFile.get(file) ?? 0.5;
1298
- }
1299
- const severityOrder = {
1300
- critical: 0,
1301
- severe: 1,
1302
- high: 2,
1303
- info: 3,
1304
- };
1305
- const indexed = violations.map((v, i) => ({ v, i }));
1306
- indexed.sort((a, b) => {
1307
- const sevA = severityOrder[a.v.severity] ?? 99;
1308
- const sevB = severityOrder[b.v.severity] ?? 99;
1309
- if (sevA !== sevB)
1310
- return sevA - sevB;
1311
- const reachA = a.v.reachability ?? 0.5;
1312
- const reachB = b.v.reachability ?? 0.5;
1313
- if (reachA !== reachB)
1314
- return reachB - reachA;
1315
- const hsA = a.v.hotspot ?? 0;
1316
- const hsB = b.v.hotspot ?? 0;
1317
- if (hsA !== hsB)
1318
- return hsB - hsA;
1319
- return a.i - b.i;
1320
- });
1321
- result.violations = indexed.map((x) => x.v);
1293
+ function applyReachabilityRanking(ordered, reachabilityByFile) {
1294
+ const severityOrder = {
1295
+ critical: 0,
1296
+ severe: 1,
1297
+ high: 2,
1298
+ info: 3,
1299
+ };
1300
+ for (const result of Object.values(ordered)) {
1301
+ const violations = result.violations;
1302
+ for (const v of violations) {
1303
+ // A file absent from the corpus graph gets a neutral 0.5 so we never
1304
+ // demote a finding we know nothing about.
1305
+ v.reachability = reachabilityByFile?.get(v.file) ?? 0.5;
1322
1306
  }
1323
- }
1324
- catch {
1325
- // Hotspot/reachability reordering is advisory — failure is non-fatal
1307
+ const indexed = violations.map((v, i) => ({ v, i }));
1308
+ indexed.sort((a, b) => {
1309
+ const sevA = severityOrder[a.v.severity] ?? 99;
1310
+ const sevB = severityOrder[b.v.severity] ?? 99;
1311
+ if (sevA !== sevB)
1312
+ return sevA - sevB;
1313
+ const reachA = a.v.reachability ?? 0.5;
1314
+ const reachB = b.v.reachability ?? 0.5;
1315
+ if (reachA !== reachB)
1316
+ return reachB - reachA;
1317
+ return a.i - b.i;
1318
+ });
1319
+ result.violations = indexed.map((x) => x.v);
1326
1320
  }
1327
1321
  }
1328
1322
  /**
@@ -1369,18 +1363,54 @@ function classifyBaselineFindings(ordered, mergedOptions, files, isScoped) {
1369
1363
  * Spec 60 size/test/distribution reports), and hook-contract-clean violations.
1370
1364
  */
1371
1365
  function buildAuditResult(inputs) {
1372
- 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;
1366
+ 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, phaseCandidates, receiverResolutions, cacheGc, } = inputs;
1373
1367
  // Spec 69 §10 R4 — count query-shaped call sites whose DB receiver is
1374
1368
  // unproven, keyed by the analyzer that owns the receiver-resolution
1375
1369
  // instrument (`schema`). This is the site-level `cannot-fire` surface that
1376
1370
  // `summary.byAnalyzer[].unprovenSites` carries per analyzer.
1377
1371
  const unprovenSitesByAnalyzer = new Map();
1372
+ // K9 — the same site-level `cannot-fire` surface, keyed by the *rule* each
1373
+ // unproven site renders unreadable (from `details.affectedRules`) and the
1374
+ // *cause* that produced it (`details.cause`). `byAnalyzer[].unprovenSites`
1375
+ // answers "which analyzer produced these?"; this answers "which rule may have
1376
+ // under-reported?" — the discriminant the raw 465-site total hid. One site
1377
+ // affects every rule in its `affectedRules` list, so the per-rule `count` is
1378
+ // the number of unproven sites that could have produced a finding under it.
1379
+ const unprovenSitesByRule = new Map();
1378
1380
  for (const d of pipelineDiagnostics ?? []) {
1379
1381
  if (d.kind !== 'cannot-fire' || d.analyzerName !== 'schema')
1380
1382
  continue;
1381
1383
  unprovenSitesByAnalyzer.set(d.analyzerName, (unprovenSitesByAnalyzer.get(d.analyzerName) ?? 0) + 1);
1384
+ const affectedRules = d.details?.affectedRules;
1385
+ if (!Array.isArray(affectedRules))
1386
+ continue;
1387
+ const cause = typeof d.details?.cause === 'string' ? d.details.cause : 'unknown';
1388
+ for (const rule of affectedRules) {
1389
+ if (typeof rule !== 'string')
1390
+ continue;
1391
+ let entry = unprovenSitesByRule.get(rule);
1392
+ if (!entry) {
1393
+ entry = { count: 0, causes: new Map() };
1394
+ unprovenSitesByRule.set(rule, entry);
1395
+ }
1396
+ entry.count++;
1397
+ entry.causes.set(cause, (entry.causes.get(cause) ?? 0) + 1);
1398
+ }
1382
1399
  }
1383
- const summary = generateSummary(ordered, files.length, unprovenSitesByAnalyzer);
1400
+ // A bare `changed` scope that found no baseline to diff against (an empty
1401
+ // `functions` table) is reported as an uncomputable diff, never as a clean
1402
+ // run. The diagnostic names both possible causes: the index was never
1403
+ // populated, or it was discarded (e.g. after corruption).
1404
+ const noBaselineDiagnostics = changedScopeNoBaseline
1405
+ ? [{
1406
+ analyzerName: 'code-index',
1407
+ kind: 'no-baseline',
1408
+ message: 'Cannot determine what changed: the code index holds no baseline to diff against ' +
1409
+ '(no previously indexed files). No index has been built yet, or a prior index was ' +
1410
+ 'discarded. Run a full audit to build the baseline, then re-run `changed`.',
1411
+ }]
1412
+ : [];
1413
+ const summary = generateSummary(ordered, files.length, unprovenSitesByAnalyzer, unprovenSitesByRule);
1384
1414
  return {
1385
1415
  timestamp: new Date(),
1386
1416
  summary,
@@ -1389,6 +1419,7 @@ function buildAuditResult(inputs) {
1389
1419
  metadata: {
1390
1420
  auditDuration: Date.now() - startTime,
1391
1421
  auditCpuMs: cpuDurationMs(startCpu),
1422
+ receiverResolutions,
1392
1423
  filesAnalyzed: files.length,
1393
1424
  ...(indexFactsWritten > 0 && { indexFactsWritten, writeIndexFactsMs }),
1394
1425
  analyzersRun: analyzers,
@@ -1397,8 +1428,9 @@ function buildAuditResult(inputs) {
1397
1428
  scope: scopeResultType,
1398
1429
  provenanceResolutionMs: provenanceTiming.totalMs,
1399
1430
  ...(blastRadius && { blastRadius }),
1400
- ...((zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
1431
+ ...((noBaselineDiagnostics.length > 0 || zeroFilesDiagnostics.length > 0 || (pipelineDiagnostics?.length ?? 0) > 0 || rejected.length > 0 || lintConfigDiagnostics.length > 0) && {
1401
1432
  diagnostics: [
1433
+ ...noBaselineDiagnostics,
1402
1434
  ...zeroFilesDiagnostics,
1403
1435
  ...(pipelineDiagnostics ?? []),
1404
1436
  ...lintConfigDiagnostics,
@@ -1413,7 +1445,6 @@ function buildAuditResult(inputs) {
1413
1445
  }),
1414
1446
  ...(baselineMetadata && { baseline: baselineMetadata }),
1415
1447
  ...(pipelineCoverage && { coverage: pipelineCoverage }),
1416
- ...(routeAttribution && { routeAttribution }),
1417
1448
  ...(pipelineTableCatalog && { tableCatalog: pipelineTableCatalog }),
1418
1449
  ...(pipelineStageTiming && { stageTiming: pipelineStageTiming }),
1419
1450
  ...(thresholdChanges.length > 0 && { thresholdChanges }),
@@ -1427,6 +1458,8 @@ function buildAuditResult(inputs) {
1427
1458
  ...(pipelineTestCoverage && { testCoverage: pipelineTestCoverage }),
1428
1459
  ...(pipelineDeadClusters && pipelineDeadClusters.length > 0 && { deadClusters: pipelineDeadClusters }),
1429
1460
  ...(pipelineSizeDistributions && pipelineSizeDistributions.length > 0 && { sizeDistributions: pipelineSizeDistributions }),
1461
+ ...(phaseCandidates.length > 0 && { candidates: phaseCandidates }),
1462
+ ...((cacheGc.removedCliVersions.length > 0 || cacheGc.removedProjects.length > 0) && { cacheGc }),
1430
1463
  ...(collectedFunctions.length > 0 && {
1431
1464
  collectedFunctions,
1432
1465
  fileToFunctionsMap: Object.fromEntries(fileToFunctionsMap)
@@ -1488,11 +1521,17 @@ export function createAuditRunner(options = {}) {
1488
1521
  // `node_modules/.cache/code-auditor`) left by a pre-move build — reported once,
1489
1522
  // never deleted (see legacyIndexNotice.ts).
1490
1523
  notifyLegacyIndexLocation(root);
1524
+ // Bound the growth of the tool's own fallback cache: superseded pinned-CLI
1525
+ // copies and abandoned project indexes. Process-scoped, never touches a user
1526
+ // CODE_AUDITOR_DATA_DIR or the consuming repo (see cacheGc.ts). The result is
1527
+ // captured (not fire-and-forget) so the audit output states what was collected
1528
+ // — a silent delete of a 132 MB index is exactly the defect K10 forbids.
1529
+ const cacheGc = gcStaleCacheDirsOnce();
1491
1530
  // Spec 44 — file accounting accumulator, owned by the run. Threaded through
1492
1531
  // discovery (full `all` scope) and the pipeline (stage 1/2) so every touched
1493
1532
  // file lands in exactly one terminal state and the balance can be asserted.
1494
1533
  const fileAccounting = new FileAccounting();
1495
- const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
1534
+ const { files, changedFunctions, skippedExtensions, blastRadius, corpusFiles, scope, isScoped, scopeResultType, changedScopeNoBaseline, } = await discoverAuditFiles(mergedOptions, root, fileAccounting);
1496
1535
  const { collectedFunctions, fileToFunctionsMap } = await collectIndexedFunctions(mergedOptions, files);
1497
1536
  // Run analyzers
1498
1537
  // Spec 68 §15 — no selection model: the full pipeline always runs. `indexOnly`
@@ -1510,7 +1549,7 @@ export function createAuditRunner(options = {}) {
1510
1549
  mergedOptions, root, files, corpusFiles, isScoped, fileAccounting,
1511
1550
  styleConsumedFiles, styleContributingFiles, analyzers, provenanceTiming,
1512
1551
  });
1513
- const { analyzerResults, phaseFindings, phaseIncompleteFacts, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, routeAttribution, indexFactsWritten, writeIndexFactsMs, } = pipeline;
1552
+ const { analyzerResults, phaseFindings, phaseIncompleteFacts, phaseReachability, phaseCandidates, receiverResolutions, pipelineCoverage, pipelineTableCatalog, pipelineStageTiming, pipelineSkippedFiles, pipelineUnparsedFiles, pipelineInputPresence, pipelineRuleTiming, pipelineFileAccounting, pipelineDiagnostics, pipelineTestCoverage, pipelineDeadClusters, pipelineSizeDistributions, indexFactsWritten, writeIndexFactsMs, } = pipeline;
1514
1553
  // ── Zero-files diagnostic ─────────────────────────────────────────────
1515
1554
  // Extracted to runZeroFilesDiagnostics() for testability. Runs against the
1516
1555
  // raw analyzerResults before truthiness filtering so the "no result" pass
@@ -1526,8 +1565,8 @@ export function createAuditRunner(options = {}) {
1526
1565
  // Normalize the analyzer field on every violation to its result key (the
1527
1566
  // single source of truth — see stampAnalyzerFields).
1528
1567
  stampAnalyzerFields(orderedAnalyzerResults);
1529
- // Spec 13 R2 — hotspot scoring & finding reordering (advisory).
1530
- await applyHotspotScoring(orderedAnalyzerResults, root);
1568
+ // Spec 13 R2 — reachability scoring & finding reordering (advisory).
1569
+ applyReachabilityRanking(orderedAnalyzerResults, phaseReachability);
1531
1570
  // Hook-contract guard: no violation may carry an empty file path, line 0, or
1532
1571
  // missing line. A sentinel violation with file:'' broke the Claude Code hook's
1533
1572
  // JSON consumer (Spec 15 regression); line:0 from CrossDomainAnalyzer broke it
@@ -1552,6 +1591,7 @@ export function createAuditRunner(options = {}) {
1552
1591
  startCpu,
1553
1592
  provenanceTiming,
1554
1593
  blastRadius,
1594
+ changedScopeNoBaseline,
1555
1595
  zeroFilesDiagnostics,
1556
1596
  pipelineDiagnostics,
1557
1597
  rejected,
@@ -1559,7 +1599,6 @@ export function createAuditRunner(options = {}) {
1559
1599
  configPath,
1560
1600
  baselineMetadata,
1561
1601
  pipelineCoverage,
1562
- routeAttribution,
1563
1602
  pipelineTableCatalog,
1564
1603
  pipelineStageTiming,
1565
1604
  thresholdChanges,
@@ -1575,6 +1614,9 @@ export function createAuditRunner(options = {}) {
1575
1614
  pipelineSizeDistributions,
1576
1615
  collectedFunctions,
1577
1616
  fileToFunctionsMap,
1617
+ phaseCandidates,
1618
+ receiverResolutions,
1619
+ cacheGc,
1578
1620
  });
1579
1621
  // Report completion
1580
1622
  reportProgress(mergedOptions, {
@@ -1753,8 +1795,9 @@ export async function resolveFilesScope(options, scopeFiles) {
1753
1795
  * @param analyzerResults per-analyzer results, keyed by analyzer name
1754
1796
  * @param filesAnalyzed total number of files processed this run
1755
1797
  * @param unprovenSitesByAnalyzer per-analyzer count of unproven (cannot-fire) sites
1798
+ * @param unprovenSitesByRule per-rule count + cause of unproven (cannot-fire) sites (K9)
1756
1799
  */
1757
- export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByAnalyzer = new Map()) {
1800
+ export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByAnalyzer = new Map(), unprovenSitesByRule = new Map()) {
1758
1801
  let totalViolations = 0;
1759
1802
  let criticalIssues = 0;
1760
1803
  let severe = 0;
@@ -1801,6 +1844,14 @@ export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByA
1801
1844
  let unprovenSites = 0;
1802
1845
  for (const n of unprovenSitesByAnalyzer.values())
1803
1846
  unprovenSites += n;
1847
+ // K9 — materialize the per-rule cannot-fire breakdown as a plain object so
1848
+ // the summary carries it through the JSON/HTML/report surfaces. Keys are the
1849
+ // affected rule ids; `count` is the number of unproven sites that could have
1850
+ // produced a finding under that rule, and `causes` names the emission path.
1851
+ const unprovenSitesByRuleOut = {};
1852
+ for (const [rule, entry] of unprovenSitesByRule) {
1853
+ unprovenSitesByRuleOut[rule] = { count: entry.count, causes: Object.fromEntries(entry.causes) };
1854
+ }
1804
1855
  return {
1805
1856
  totalFiles: filesAnalyzed,
1806
1857
  totalViolations,
@@ -1811,6 +1862,7 @@ export function generateSummary(analyzerResults, filesAnalyzed, unprovenSitesByA
1811
1862
  byAnalyzer,
1812
1863
  topIssues,
1813
1864
  unprovenSites,
1865
+ unprovenSitesByRule: unprovenSitesByRuleOut,
1814
1866
  heldOut
1815
1867
  };
1816
1868
  }