code-auditor-mcp 3.9.0 → 3.9.2

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 (252) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +95 -0
  3. package/README.md +1 -0
  4. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts +14 -0
  5. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.d.ts.map +1 -0
  6. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js +16 -0
  7. package/dist/__tests__/fixtures/spec-52/item-01-prepare-in-loop.js.map +1 -0
  8. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts +15 -0
  9. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js +17 -0
  11. package/dist/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.js.map +1 -0
  12. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +14 -0
  13. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +1 -0
  14. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +13 -0
  15. package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +1 -0
  16. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +24 -0
  17. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +1 -0
  18. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +19 -0
  19. package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +1 -0
  20. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts +19 -0
  21. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.d.ts.map +1 -0
  22. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js +19 -0
  23. package/dist/__tests__/fixtures/spec-52/item-05-upsert-forms.js.map +1 -0
  24. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +17 -0
  25. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +1 -0
  26. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js +19 -0
  27. package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +1 -0
  28. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +25 -0
  29. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +1 -0
  30. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js +21 -0
  31. package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +1 -0
  32. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts +15 -0
  33. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.d.ts.map +1 -0
  34. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js +19 -0
  35. package/dist/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.js.map +1 -0
  36. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts +14 -0
  37. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.d.ts.map +1 -0
  38. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js +14 -0
  39. package/dist/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.js.map +1 -0
  40. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +15 -0
  41. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +1 -0
  42. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js +16 -0
  43. package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +1 -0
  44. package/dist/analyzers/analyzerUtils.js +2 -2
  45. package/dist/analyzers/analyzerUtils.js.map +1 -1
  46. package/dist/analyzers/applicability.d.ts +38 -0
  47. package/dist/analyzers/applicability.d.ts.map +1 -1
  48. package/dist/analyzers/applicability.js +55 -0
  49. package/dist/analyzers/applicability.js.map +1 -1
  50. package/dist/analyzers/cross-language/APIContractAnalyzer.js +5 -5
  51. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  52. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +1 -1
  53. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  54. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +7 -7
  55. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  56. package/dist/analyzers/cross-language/SchemaValidator.d.ts +11 -0
  57. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  58. package/dist/analyzers/cross-language/SchemaValidator.js +35 -59
  59. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  60. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +3 -2
  61. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  62. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +9 -8
  63. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  64. package/dist/analyzers/documentationAnalyzer.js +5 -5
  65. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  66. package/dist/analyzers/invariantsAnalyzer.js +1 -1
  67. package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
  68. package/dist/analyzers/reactAnalyzer.d.ts +1 -1
  69. package/dist/analyzers/reactAnalyzer.js +13 -13
  70. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  71. package/dist/analyzers/ruleRegistry.d.ts +3 -4
  72. package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
  73. package/dist/analyzers/ruleRegistry.js.map +1 -1
  74. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +2 -2
  75. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  76. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +7 -7
  77. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  78. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +6 -6
  79. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +12 -12
  80. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  81. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +7 -0
  82. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  83. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +118 -12
  84. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  85. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +2 -2
  86. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +8 -8
  87. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  88. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +9 -2
  89. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  90. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +77 -15
  91. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  92. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +1 -1
  93. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +2 -2
  94. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  95. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +1 -1
  96. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  97. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +12 -24
  98. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  99. package/dist/analyzers/universal/schema/codeAnalysis.d.ts +19 -5
  100. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
  101. package/dist/analyzers/universal/schema/codeAnalysis.js +42 -19
  102. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
  103. package/dist/analyzers/universal/schema/discovery.js +1 -1
  104. package/dist/analyzers/universal/schema/discovery.js.map +1 -1
  105. package/dist/analyzers/universal/schema/jsonSchema.js +19 -19
  106. package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -1
  107. package/dist/analyzers/universal/schema/violations.d.ts +1 -1
  108. package/dist/analyzers/universal/schema/violations.d.ts.map +1 -1
  109. package/dist/auditRouter.d.ts.map +1 -1
  110. package/dist/auditRouter.js +4 -6
  111. package/dist/auditRouter.js.map +1 -1
  112. package/dist/auditRunner.d.ts.map +1 -1
  113. package/dist/auditRunner.js +11 -12
  114. package/dist/auditRunner.js.map +1 -1
  115. package/dist/cli.js +42 -80
  116. package/dist/cli.js.map +1 -1
  117. package/dist/config/configLoader.d.ts.map +1 -1
  118. package/dist/config/configLoader.js +1 -16
  119. package/dist/config/configLoader.js.map +1 -1
  120. package/dist/config/defaults.d.ts +1 -8
  121. package/dist/config/defaults.d.ts.map +1 -1
  122. package/dist/config/defaults.js +8 -31
  123. package/dist/config/defaults.js.map +1 -1
  124. package/dist/config/effectiveConfig.d.ts +4 -4
  125. package/dist/config/effectiveConfig.js +4 -4
  126. package/dist/daemon/lspServer.js +2 -2
  127. package/dist/daemon/lspServer.js.map +1 -1
  128. package/dist/enforcement/gate.d.ts +1 -1
  129. package/dist/enforcement/gate.d.ts.map +1 -1
  130. package/dist/enforcement/gate.js +10 -9
  131. package/dist/enforcement/gate.js.map +1 -1
  132. package/dist/generators/AWSQConfigGenerator.js +1 -1
  133. package/dist/generators/AWSQConfigGenerator.js.map +1 -1
  134. package/dist/generators/AiderConfigGenerator.js +1 -1
  135. package/dist/generators/AiderConfigGenerator.js.map +1 -1
  136. package/dist/generators/ClaudeConfigGenerator.js +1 -1
  137. package/dist/generators/ClaudeConfigGenerator.js.map +1 -1
  138. package/dist/generators/ClineConfigGenerator.js +1 -1
  139. package/dist/generators/ClineConfigGenerator.js.map +1 -1
  140. package/dist/generators/CodeiumConfigGenerator.js +1 -1
  141. package/dist/generators/CodeiumConfigGenerator.js.map +1 -1
  142. package/dist/generators/CodexConfigGenerator.js +1 -1
  143. package/dist/generators/CodexConfigGenerator.js.map +1 -1
  144. package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
  145. package/dist/generators/ConfigGeneratorFactory.js +2 -0
  146. package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
  147. package/dist/generators/ContinueConfigGenerator.js +1 -1
  148. package/dist/generators/CopilotConfigGenerator.js +1 -1
  149. package/dist/generators/CopilotConfigGenerator.js.map +1 -1
  150. package/dist/generators/CursorConfigGenerator.js +1 -1
  151. package/dist/generators/CursorConfigGenerator.js.map +1 -1
  152. package/dist/generators/GeminiConfigGenerator.js +2 -2
  153. package/dist/generators/GeminiConfigGenerator.js.map +1 -1
  154. package/dist/generators/JetBrainsConfigGenerator.js +1 -1
  155. package/dist/generators/JetBrainsConfigGenerator.js.map +1 -1
  156. package/dist/generators/VSCodeConfigGenerator.js +1 -1
  157. package/dist/generators/VSCodeConfigGenerator.js.map +1 -1
  158. package/dist/generators/ZCodeConfigGenerator.d.ts +21 -0
  159. package/dist/generators/ZCodeConfigGenerator.d.ts.map +1 -0
  160. package/dist/generators/ZCodeConfigGenerator.js +64 -0
  161. package/dist/generators/ZCodeConfigGenerator.js.map +1 -0
  162. package/dist/generators/index.d.ts +3 -2
  163. package/dist/generators/index.d.ts.map +1 -1
  164. package/dist/generators/index.js +3 -2
  165. package/dist/generators/index.js.map +1 -1
  166. package/dist/hooks/codex.d.ts +2 -2
  167. package/dist/hooks/codex.d.ts.map +1 -1
  168. package/dist/hooks/codex.js +22 -37
  169. package/dist/hooks/codex.js.map +1 -1
  170. package/dist/hooks/core.d.ts +2 -2
  171. package/dist/hooks/core.d.ts.map +1 -1
  172. package/dist/hooks/core.js +6 -6
  173. package/dist/hooks/core.js.map +1 -1
  174. package/dist/hooks/cursor.d.ts +1 -1
  175. package/dist/hooks/cursor.d.ts.map +1 -1
  176. package/dist/hooks/cursor.js +18 -37
  177. package/dist/hooks/cursor.js.map +1 -1
  178. package/dist/installer.d.ts.map +1 -1
  179. package/dist/installer.js +8 -0
  180. package/dist/installer.js.map +1 -1
  181. package/dist/invariants/invariant-rules.schema.json +1 -1
  182. package/dist/invariants/ruleValidator.js +1 -1
  183. package/dist/invariants/ruleValidator.js.map +1 -1
  184. package/dist/invariants/types.d.ts +1 -1
  185. package/dist/invariants/types.d.ts.map +1 -1
  186. package/dist/languages/LanguageOrchestrator.d.ts +2 -19
  187. package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
  188. package/dist/languages/LanguageOrchestrator.js +23 -50
  189. package/dist/languages/LanguageOrchestrator.js.map +1 -1
  190. package/dist/languages/RuntimeManager.js +2 -2
  191. package/dist/languages/RuntimeManager.js.map +1 -1
  192. package/dist/languages/UniversalAnalyzer.d.ts +2 -13
  193. package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
  194. package/dist/languages/UniversalAnalyzer.js +2 -23
  195. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  196. package/dist/languages/go/analyzer +0 -0
  197. package/dist/languages/go/analyzer-src/analyzer.go +8 -8
  198. package/dist/languages/go/analyzer-src/solid.go +6 -6
  199. package/dist/ledger.js +1 -1
  200. package/dist/ledger.js.map +1 -1
  201. package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
  202. package/dist/mcp-tools/projectTasks.js +4 -5
  203. package/dist/mcp-tools/projectTasks.js.map +1 -1
  204. package/dist/mcp-tools/whitelistTools.d.ts +1 -1
  205. package/dist/mcp-tools/workflowGuide.js +3 -3
  206. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  207. package/dist/mcp-tools-shared.d.ts.map +1 -1
  208. package/dist/mcp-tools-shared.js +22 -21
  209. package/dist/mcp-tools-shared.js.map +1 -1
  210. package/dist/mcp-ui-simple.js +15 -15
  211. package/dist/mcp-ui-simple.js.map +1 -1
  212. package/dist/mcp.js +24 -22
  213. package/dist/mcp.js.map +1 -1
  214. package/dist/mcpAuditJobs.js +14 -14
  215. package/dist/mcpAuditJobs.js.map +1 -1
  216. package/dist/mcpAutoIndex.js +1 -1
  217. package/dist/mcpAutoIndex.js.map +1 -1
  218. package/dist/nextFile.d.ts +2 -2
  219. package/dist/nextFile.d.ts.map +1 -1
  220. package/dist/nextFile.js +4 -5
  221. package/dist/nextFile.js.map +1 -1
  222. package/dist/nextFileIncremental.d.ts +2 -2
  223. package/dist/nextFileIncremental.d.ts.map +1 -1
  224. package/dist/nextFileIncremental.js +7 -7
  225. package/dist/nextFileIncremental.js.map +1 -1
  226. package/dist/pipeline.d.ts.map +1 -1
  227. package/dist/pipeline.js +16 -9
  228. package/dist/pipeline.js.map +1 -1
  229. package/dist/pipelineAdapters.d.ts.map +1 -1
  230. package/dist/pipelineAdapters.js +25 -11
  231. package/dist/pipelineAdapters.js.map +1 -1
  232. package/dist/reporting/csvReportGenerator.js +7 -7
  233. package/dist/reporting/csvReportGenerator.js.map +1 -1
  234. package/dist/reporting/htmlReportGenerator.js +18 -18
  235. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  236. package/dist/reporting/jsonReportGenerator.js +3 -3
  237. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  238. package/dist/reporting/sarifReportGenerator.js +3 -3
  239. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  240. package/dist/scripts/benchmarkWorkers.js +1 -1
  241. package/dist/scripts/benchmarkWorkers.js.map +1 -1
  242. package/dist/types/crossLanguage.d.ts +1 -1
  243. package/dist/types/crossLanguage.d.ts.map +1 -1
  244. package/dist/types.d.ts +26 -24
  245. package/dist/types.d.ts.map +1 -1
  246. package/dist/types.js +11 -0
  247. package/dist/types.js.map +1 -1
  248. package/package.json +6 -3
  249. package/plugin/.claude-plugin/plugin.json +1 -1
  250. package/plugin/skills/code-auditor/SKILL-RULE-KINDS.md +25 -23
  251. package/plugin/skills/code-auditor/SKILL.md +9 -9
  252. package/server.json +2 -2
@@ -10,7 +10,7 @@
10
10
  "name": "code-auditor",
11
11
  "source": "./plugin",
12
12
  "description": "Diff-scoped code quality auditing on every edit. Indexes your codebase, enforces invariants, and feeds violations back to the agent so fixes happen inline.",
13
- "version": "3.9.0",
13
+ "version": "3.9.2",
14
14
  "author": {
15
15
  "name": "Ben Hammond"
16
16
  },
package/CHANGELOG.md CHANGED
@@ -2,6 +2,101 @@
2
2
 
3
3
  All notable changes to the Code Auditor MCP project.
4
4
 
5
+ ## [3.9.2] — 2026-09-11
6
+
7
+ ### Severity as urgency, not permission (Spec 54)
8
+
9
+ The severity vocabulary is relabeled from `critical` / `warning` / `suggestion`
10
+ to **`critical`** / **`severe`** / **`high`**. Every level is now a defect; the
11
+ axis is *how fast it bites you*, not how optional the fix is:
12
+
13
+ - **`critical`** — exploitable or broken now.
14
+ - **`severe`** — wrong, and it will surface.
15
+ - **`high`** — wrong, and it has not bitten yet.
16
+
17
+ There is no fourth rung below `high`; the advisory `suggestion` tier is gone,
18
+ and a `high` finding now blocks the gate with exit 2. Every reading is a defect
19
+ and the severity is only the order to act.
20
+
21
+ - **Old-scheme machinery removed** — `gateSeverities`, `severityOverrides`, and
22
+ path-profile severity capping are gone; `excludeFromGate` remains.
23
+ - **SARIF** maps all three levels to `error` (the lower two no longer downgrade).
24
+ - **Every surface updated** — CLI, JSON/HTML/CSV/SARIF reports, SKILL docs,
25
+ the rule registry, message templates, and the six-locale docs site.
26
+ - **Full ledger** — every rule reassigned from scratch in
27
+ `specs/severity-assignment-ledger.md`: 111 rules at 8 `critical` / 37 `severe` /
28
+ 56 `high`.
29
+ - **No detection change** — corpus baselines re-measured and unchanged; this is
30
+ a relabel only, not a behavior change.
31
+
32
+ ## [3.9.1] — 2026-09-11
33
+
34
+ ### Findings from a real D1/Workers audit (Spec 52)
35
+
36
+ Three defects surfaced by auditing an unseen Cloudflare D1 project, each fixed
37
+ with a false-positive / true-positive two-direction proof:
38
+
39
+ - **`loop-query` treated statement construction as execution (R1).** `.prepare()` /
40
+ `.bind()` build a statement object with no I/O; execution happens only at
41
+ `.run()` / `.all()` / `.first()` / `.raw()` / `.exec()` / `.batch()`. The
42
+ accumulate-then-batch shape (`stmts.push(db.prepare(sql).bind(x))` consumed by
43
+ a single `.batch()` after the loop) is no longer flagged, while a genuine N+1 —
44
+ an eager call in a loop, *including* `db.prepare(sql).bind(x).run()` — still
45
+ fires. `too-many-queries` no longer counts `.prepare()` SQL, but now counts the
46
+ eager execution methods (`.run()` / `.all()` / `.first()` / `.raw()` /
47
+ `.batch()`, including the typed D1 forms `.all<T>()` / `.first<T>()`), so a
48
+ genuine `db.prepare(sql).bind(x).all<Row>()` chain is still counted while a
49
+ pure statement factory is not. `Promise.all(...)` is excluded and `.exec()` is
50
+ deliberately not counted (indistinguishable from `regex.exec()` in a text
51
+ heuristic).
52
+ - **`INSERT OR IGNORE` and friends were not recognized as writes (R2).** The write
53
+ classifier now matches `INSERT OR IGNORE INTO`, `INSERT OR REPLACE INTO`,
54
+ `REPLACE INTO`, and `INSERT … ON CONFLICT … DO UPDATE`, so the write set that
55
+ `written-never-read` / `read-never-written` / `multi-table-write` /
56
+ `unknown-table` read is correct.
57
+ - **Whole-program rules were unsound under a scoped run (R3).**
58
+ `written-never-read`, `read-never-written`, `no-validator-reachable`, and
59
+ `unknown-table` draw a global conclusion that a partial file set cannot
60
+ support, so a scoped/diff run now reports them `notApplicable` ("requires
61
+ whole-project analysis; this run was scoped to N file(s)") rather than firing
62
+ an unqualified global claim. Full runs are unchanged.
63
+
64
+ Edge-case follow-up (same pass): `loop-query`'s `Promise.all` guard now also
65
+ crosses an arrow function — `Promise.all(rows.map(r => db.prepare(sql).bind(r)))`
66
+ in a loop is statement construction (not N+1), while
67
+ `Promise.all(rows.map(r => db.prepare(sql).bind(r).all()))` still fires. And
68
+ `countQueries` no longer double-counts the `DO UPDATE` / `KEY UPDATE` clause of
69
+ an upsert as a second query (knex `too-many-queries` 196 → 193).
70
+
71
+ ### ZCode installation support
72
+ Added first-class ZCode (Z.AI's agentic AI coding IDE) support:
73
+ - `code-audit install --agent zcode` installs the skill (ZCode reads the `.agents`
74
+ skills convention).
75
+ - The `generate` MCP tool now emits ZCode MCP config — `.zcode/config.json`
76
+ (project scope) or `~/.zcode/cli/config.json` (user scope), using ZCode's
77
+ native `mcp.servers` key.
78
+
79
+ ### `solid/dependency-inversion` — escapes-vs-held false positive
80
+ An external audit found the rule firing on `new AppError(...)` thrown from a
81
+ Durable Object method — a *value type*, not a collaborator the class holds. The
82
+ predicate now distinguishes **escapes** from **held**: a construction whose value
83
+ is thrown or returned is a value type (not a dependency); one assigned to a field
84
+ or captured as state is a dependency. The escape check walks through transparent
85
+ wrappers (`parenthesized_expression`, `as_expression`, `type_assertion`,
86
+ `satisfies_expression`, `non_null_expression`), so `return new Foo() as Bar` and
87
+ `throw (new AppError())` are also cleared. This clears `throw new AppError(...)`
88
+ and `return new Foo(...)` factory values while every held-collaborator positive
89
+ still fires. Corpus deltas (only this rule moved): knex 20 → 12 (−8, dialect/Client
90
+ factory accessors that `return new QueryBuilder(this)` etc.), blitz 12 → 11
91
+ (−1, a `return new Response(...)` value). recall-protocol (3), hhra-org (8),
92
+ primer-css (0) unchanged — no weakening to zero. Survivors sampled and confirmed
93
+ field-held collaborators: `GuildAgent` (`this.#migrations = new SQLSchemaMigrations(...)`),
94
+ `StrategistManager` (`this.reconnect = new ReconnectController(...)`),
95
+ `OrgTrackerDatabase` (`this.pool = new Pool(...)`), `Client`
96
+ (`this.logger = new Logger(...)` / `this.pool = new KnexPool(...)` — fires despite
97
+ its many `return new X()` factory accessors), `Generator`
98
+ (`this.enquirer = new Enquirer()`).
99
+
5
100
  ## [3.9.0] — 2026-09-09
6
101
 
7
102
  ### SQLite without a native install (Spec 51)
package/README.md CHANGED
@@ -79,6 +79,7 @@ One skill, one CLI, one MCP server. Every agent gets the same audit engine — t
79
79
  | Gemini CLI | `code-audit install --agent gemini` | No | Yes | 2026-07-19 |
80
80
  | VS Code / Copilot | `code-audit install --agent agents` | No | Yes | 2026-07-19 |
81
81
  | Other SKILL.md tools | `code-audit install --agent agents` | No | Yes | 2026-07-19 |
82
+ | ZCode | `code-audit install --agent zcode` | No (plugin-scoped) | Yes | 2026-09-10 |
82
83
 
83
84
  Hook behavior: **Blocking** means the diff-scoped `changed` gate is tripped — an invariant rule that declares `gating: true` (a binary, per-rule flag, independent of severity) blocks the edit from landing (the agent sees the violation and fixes inline). **Advisory** means violations are reported through the strongest available feedback channel but the edit has already occurred. Cursor's `afterFileEdit` hook is fire-and-forget with no output consumption. MCP is available everywhere for shell-less use.
84
85
 
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Spec-52 R1 item 1 — loop-query FALSE positive (oracle: MUST NOT fire).
3
+ *
4
+ * `db.prepare(sql)` inside a loop builds a statement object; it performs no I/O.
5
+ * The statements are accumulated and executed once by a single `.batch()` after
6
+ * the loop. This is the `mod.ts:213/218/223/266` shape — not an N+1.
7
+ */
8
+ interface Row {
9
+ id: number;
10
+ name: string;
11
+ }
12
+ export declare function insertMany(db: any, rows: Row[]): Promise<void>;
13
+ export {};
14
+ //# sourceMappingURL=item-01-prepare-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-01-prepare-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-01-prepare-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Spec-52 R1 item 1 — loop-query FALSE positive (oracle: MUST NOT fire).
3
+ *
4
+ * `db.prepare(sql)` inside a loop builds a statement object; it performs no I/O.
5
+ * The statements are accumulated and executed once by a single `.batch()` after
6
+ * the loop. This is the `mod.ts:213/218/223/266` shape — not an N+1.
7
+ */
8
+ export async function insertMany(db, rows) {
9
+ const stmts = [];
10
+ for (const row of rows) {
11
+ const stmt = db.prepare('INSERT INTO items (id, name) VALUES (?, ?)');
12
+ stmts.push(stmt);
13
+ }
14
+ await db.batch(stmts);
15
+ }
16
+ //# sourceMappingURL=item-01-prepare-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-01-prepare-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-01-prepare-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAO,EAAE,IAAW;IACnD,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC;QACtE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Spec-52 R1 item 2 — accumulate-then-batch (oracle: MUST NOT fire).
3
+ *
4
+ * The explicit accumulate-then-batch shape: each loop iteration prepares and
5
+ * binds a statement (statement construction — no I/O), pushes it to an array,
6
+ * and a single `.batch()` after the loop executes them all. The eager call sits
7
+ * outside the loop, so this is not an N+1.
8
+ */
9
+ interface ScoreRow {
10
+ id: number;
11
+ score: number;
12
+ }
13
+ export declare function rescoreMany(db: any, items: ScoreRow[]): Promise<void>;
14
+ export {};
15
+ //# sourceMappingURL=item-02-prepare-bind-accumulate-batch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-02-prepare-bind-accumulate-batch.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAO3E"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Spec-52 R1 item 2 — accumulate-then-batch (oracle: MUST NOT fire).
3
+ *
4
+ * The explicit accumulate-then-batch shape: each loop iteration prepares and
5
+ * binds a statement (statement construction — no I/O), pushes it to an array,
6
+ * and a single `.batch()` after the loop executes them all. The eager call sits
7
+ * outside the loop, so this is not an N+1.
8
+ */
9
+ export async function rescoreMany(db, items) {
10
+ const stmts = [];
11
+ for (const item of items) {
12
+ const stmt = db.prepare('UPDATE scores SET score = ? WHERE id = ?').bind(item.score, item.id);
13
+ stmts.push(stmt);
14
+ }
15
+ await db.batch(stmts);
16
+ }
17
+ //# sourceMappingURL=item-02-prepare-bind-accumulate-batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-02-prepare-bind-accumulate-batch.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-02-prepare-bind-accumulate-batch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAO,EAAE,KAAiB;IAC1D,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9F,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Spec-52 R1 item 3 — loop-query TRUE positive (oracle: MUST fire).
3
+ *
4
+ * `db.exec(sql)` inside a loop is an eager member call — immediate I/O on every
5
+ * iteration. This is the simplest genuine-N+1 positive arm: the prepare/bind
6
+ * skip must be surgical and leave eager member methods firing.
7
+ */
8
+ interface ItemRow {
9
+ id: number;
10
+ name: string;
11
+ }
12
+ export declare function renameAll(db: any, items: ItemRow[]): Promise<void>;
13
+ export {};
14
+ //# sourceMappingURL=item-03-exec-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-03-exec-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-03-exec-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIxE"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Spec-52 R1 item 3 — loop-query TRUE positive (oracle: MUST fire).
3
+ *
4
+ * `db.exec(sql)` inside a loop is an eager member call — immediate I/O on every
5
+ * iteration. This is the simplest genuine-N+1 positive arm: the prepare/bind
6
+ * skip must be surgical and leave eager member methods firing.
7
+ */
8
+ export async function renameAll(db, items) {
9
+ for (const item of items) {
10
+ db.exec(`UPDATE items SET name = 'renamed' WHERE id = ${item.id}`);
11
+ }
12
+ }
13
+ //# sourceMappingURL=item-03-exec-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-03-exec-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-03-exec-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAO,EAAE,KAAgB;IACvD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,EAAE,CAAC,IAAI,CAAC,gDAAgD,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Spec-52 R1 item 4 — loop-query TRUE positive (oracle: MUST fire).
3
+ *
4
+ * The `Leaderboard.ts:113` discriminating positive: `this.ctx.storage.sql.exec(...)`
5
+ * inside a loop. `.exec()` is an eager method (immediate I/O), so this is a real
6
+ * N+1 that must keep firing — the two-direction proof's positive arm.
7
+ */
8
+ interface RankRow {
9
+ id: number;
10
+ rank: number;
11
+ }
12
+ export declare class Leaderboard {
13
+ private ctx;
14
+ constructor(ctx: {
15
+ storage: {
16
+ sql: {
17
+ exec: (query: string, ...bind: unknown[]) => void;
18
+ };
19
+ };
20
+ });
21
+ applyRanks(rows: RankRow[]): Promise<void>;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=item-04-storage-sql-exec-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-04-storage-sql-exec-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAA8E;IAEzF,YAAY,GAAG,EAAE;QAAE,OAAO,EAAE;YAAE,GAAG,EAAE;gBAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,EAE3F;IAEK,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/C;CACF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Spec-52 R1 item 4 — loop-query TRUE positive (oracle: MUST fire).
3
+ *
4
+ * The `Leaderboard.ts:113` discriminating positive: `this.ctx.storage.sql.exec(...)`
5
+ * inside a loop. `.exec()` is an eager method (immediate I/O), so this is a real
6
+ * N+1 that must keep firing — the two-direction proof's positive arm.
7
+ */
8
+ export class Leaderboard {
9
+ ctx;
10
+ constructor(ctx) {
11
+ this.ctx = ctx;
12
+ }
13
+ async applyRanks(rows) {
14
+ for (const row of rows) {
15
+ this.ctx.storage.sql.exec('UPDATE leaderboard SET rank = ? WHERE id = ?', row.rank, row.id);
16
+ }
17
+ }
18
+ }
19
+ //# sourceMappingURL=item-04-storage-sql-exec-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-04-storage-sql-exec-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,MAAM,OAAO,WAAW;IACd,GAAG,CAA8E;IAEzF,YAAY,GAAgF;QAC1F,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAe;QAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,8CAA8C,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Spec-52 R2 — the four SQLite/Postgres upsert write forms (oracle: all MUST be
3
+ * classified as `insert` writes).
4
+ *
5
+ * `INSERT OR IGNORE INTO`, `INSERT OR REPLACE INTO`, `REPLACE INTO`, and
6
+ * `INSERT INTO ... ON CONFLICT ... DO UPDATE` all mutate the target table; the
7
+ * write classifier previously only matched `INSERT INTO`, so every rule reading
8
+ * the write set (written-never-read, multi-table-write) missed them.
9
+ *
10
+ * This fixture is exercised by the spec-52 test through `parseSqlTables`, which
11
+ * is the table-extraction path that feeds `schema_usage.usage_type`.
12
+ */
13
+ export declare const UPSERT_QUERIES: {
14
+ readonly insertOrIgnore: 'INSERT OR IGNORE INTO feature_flags (key, enabled) VALUES (?, ?)';
15
+ readonly insertOrReplace: 'INSERT OR REPLACE INTO feature_flags (key, enabled) VALUES (?, ?)';
16
+ readonly replaceInto: 'REPLACE INTO feature_flags (key, enabled) VALUES (?, ?)';
17
+ readonly onConflictDoUpdate: 'INSERT INTO feature_flags (key, enabled) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET enabled = excluded.enabled';
18
+ };
19
+ //# sourceMappingURL=item-05-upsert-forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-05-upsert-forms.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-05-upsert-forms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,cAAc;aACzB,cAAc,EACZ,kEAAkE;aACpE,eAAe,EACb,mEAAmE;aACrE,WAAW,EACT,yDAAyD;aAC3D,kBAAkB,EAChB,mHAAmH;CAC7G,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Spec-52 R2 — the four SQLite/Postgres upsert write forms (oracle: all MUST be
3
+ * classified as `insert` writes).
4
+ *
5
+ * `INSERT OR IGNORE INTO`, `INSERT OR REPLACE INTO`, `REPLACE INTO`, and
6
+ * `INSERT INTO ... ON CONFLICT ... DO UPDATE` all mutate the target table; the
7
+ * write classifier previously only matched `INSERT INTO`, so every rule reading
8
+ * the write set (written-never-read, multi-table-write) missed them.
9
+ *
10
+ * This fixture is exercised by the spec-52 test through `parseSqlTables`, which
11
+ * is the table-extraction path that feeds `schema_usage.usage_type`.
12
+ */
13
+ export const UPSERT_QUERIES = {
14
+ insertOrIgnore: 'INSERT OR IGNORE INTO feature_flags (key, enabled) VALUES (?, ?)',
15
+ insertOrReplace: 'INSERT OR REPLACE INTO feature_flags (key, enabled) VALUES (?, ?)',
16
+ replaceInto: 'REPLACE INTO feature_flags (key, enabled) VALUES (?, ?)',
17
+ onConflictDoUpdate: 'INSERT INTO feature_flags (key, enabled) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET enabled = excluded.enabled',
18
+ };
19
+ //# sourceMappingURL=item-05-upsert-forms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-05-upsert-forms.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-05-upsert-forms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,cAAc,EACZ,kEAAkE;IACpE,eAAe,EACb,mEAAmE;IACrE,WAAW,EACT,yDAAyD;IAC3D,kBAAkB,EAChB,mHAAmH;CAC7G,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Spec-52 R1 item 6 — chained prepare→bind→run in a loop (oracle: MUST fire).
3
+ *
4
+ * `db.prepare(sql).bind(x).run()` executes I/O on every iteration. Unlike the
5
+ * accumulate-then-batch shape (where the statement is pushed to an array and a
6
+ * single `.batch()` runs after the loop), the eager `.run()` sits *inside* the
7
+ * loop here, so this is a genuine N+1. The prepare/bind skip must be surgical:
8
+ * it skips standalone construction but leaves a prepare/bind that is chained
9
+ * into an eager call firing.
10
+ */
11
+ interface SyncJob {
12
+ id: number;
13
+ slug: string;
14
+ }
15
+ export declare function syncBuildsForHeroes(db: any, jobs: SyncJob[]): Promise<void>;
16
+ export {};
17
+ //# sourceMappingURL=item-06-chained-prepare-run-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-06-chained-prepare-run-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CASjF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Spec-52 R1 item 6 — chained prepare→bind→run in a loop (oracle: MUST fire).
3
+ *
4
+ * `db.prepare(sql).bind(x).run()` executes I/O on every iteration. Unlike the
5
+ * accumulate-then-batch shape (where the statement is pushed to an array and a
6
+ * single `.batch()` runs after the loop), the eager `.run()` sits *inside* the
7
+ * loop here, so this is a genuine N+1. The prepare/bind skip must be surgical:
8
+ * it skips standalone construction but leaves a prepare/bind that is chained
9
+ * into an eager call firing.
10
+ */
11
+ export async function syncBuildsForHeroes(db, jobs) {
12
+ for (const job of jobs) {
13
+ await db
14
+ .prepare(`UPDATE sync_jobs SET status = 'running', started_at = datetime('now') WHERE id = ?`)
15
+ .bind(job.id)
16
+ .run();
17
+ }
18
+ }
19
+ //# sourceMappingURL=item-06-chained-prepare-run-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-06-chained-prepare-run-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EAAO,EAAE,IAAe;IAChE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,EAAE;aACL,OAAO,CACN,oFAAoF,CACrF;aACA,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;aACZ,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Spec-52 R1 item 7 — chained prepare→bind→first<Row>() in a loop (oracle: MUST fire).
3
+ *
4
+ * The typed-read N+1: `.first<Row>()` (and `.all<Row>()`) is the D1 read idiom.
5
+ * The generic type argument must not hide the eager method from detection — the
6
+ * same gap that bit `too-many-queries`'s text regex. The AST path reads the
7
+ * property name directly, so `first` is visible regardless of `<Row>`.
8
+ */
9
+ interface HeroRow {
10
+ id: number;
11
+ name: string;
12
+ }
13
+ interface Statement {
14
+ bind(...args: unknown[]): Statement;
15
+ first<T>(): T | null;
16
+ all<T>(): T[];
17
+ run(): unknown;
18
+ }
19
+ interface Db {
20
+ prepare(sql: string): Statement;
21
+ batch(stmts: unknown[]): unknown;
22
+ }
23
+ export declare function fetchHeroById(db: Db, ids: number[]): Promise<HeroRow[]>;
24
+ export {};
25
+ //# sourceMappingURL=item-07-chained-prepare-bind-first-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-07-chained-prepare-bind-first-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAID,UAAU,SAAS;IACjB,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IACpC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACrB,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;IACd,GAAG,IAAI,OAAO,CAAC;CAChB;AACD,UAAU,EAAE;IACV,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;CAClC;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAU7E"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Spec-52 R1 item 7 — chained prepare→bind→first<Row>() in a loop (oracle: MUST fire).
3
+ *
4
+ * The typed-read N+1: `.first<Row>()` (and `.all<Row>()`) is the D1 read idiom.
5
+ * The generic type argument must not hide the eager method from detection — the
6
+ * same gap that bit `too-many-queries`'s text regex. The AST path reads the
7
+ * property name directly, so `first` is visible regardless of `<Row>`.
8
+ */
9
+ export async function fetchHeroById(db, ids) {
10
+ const out = [];
11
+ for (const id of ids) {
12
+ const row = await db
13
+ .prepare('SELECT id, name FROM heroes WHERE id = ?')
14
+ .bind(id)
15
+ .first();
16
+ if (row)
17
+ out.push(row);
18
+ }
19
+ return out;
20
+ }
21
+ //# sourceMappingURL=item-07-chained-prepare-bind-first-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-07-chained-prepare-bind-first-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAoBH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAM,EAAE,GAAa;IACvD,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,MAAM,EAAE;aACjB,OAAO,CAAC,0CAA0C,CAAC;aACnD,IAAI,CAAC,EAAE,CAAC;aACR,KAAK,EAAW,CAAC;QACpB,IAAI,GAAG;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Spec-52 R1 item 8 — Promise.all of plain statements in a loop (oracle: MUST NOT fire).
3
+ *
4
+ * `Promise.all([db.prepare(sql).bind(x), …])` wraps *statement objects* (no
5
+ * `.run()`/`.all()`/`.first()`), so nothing executes inside the loop. This is
6
+ * construction, not an N+1. `Promise.all` must not be misread as the eager D1
7
+ * `.all()` method just because its property name is "all".
8
+ */
9
+ interface ItemRow {
10
+ id: number;
11
+ name: string;
12
+ }
13
+ export declare function stageMany(db: any, items: ItemRow[]): Promise<unknown[]>;
14
+ export {};
15
+ //# sourceMappingURL=item-08-promise-all-of-statements-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-08-promise-all-of-statements-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAW7E"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Spec-52 R1 item 8 — Promise.all of plain statements in a loop (oracle: MUST NOT fire).
3
+ *
4
+ * `Promise.all([db.prepare(sql).bind(x), …])` wraps *statement objects* (no
5
+ * `.run()`/`.all()`/`.first()`), so nothing executes inside the loop. This is
6
+ * construction, not an N+1. `Promise.all` must not be misread as the eager D1
7
+ * `.all()` method just because its property name is "all".
8
+ */
9
+ export async function stageMany(db, items) {
10
+ const staged = [];
11
+ for (const item of items) {
12
+ staged.push(await Promise.all([
13
+ db.prepare('SELECT id FROM items WHERE id = ?').bind(item.id),
14
+ db.prepare('SELECT name FROM items WHERE id = ?').bind(item.id),
15
+ ]));
16
+ }
17
+ return staged;
18
+ }
19
+ //# sourceMappingURL=item-08-promise-all-of-statements-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-08-promise-all-of-statements-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-08-promise-all-of-statements-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAO,EAAE,KAAgB;IACvD,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CACT,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7D,EAAE,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;SAChE,CAAC,CACH,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Spec-52 R1 item 9 — Promise.all of a .map() of plain statements (oracle: MUST NOT fire).
3
+ *
4
+ * `Promise.all(rows.map(r => db.prepare(sql).bind(r)))` wraps *statement
5
+ * objects* (no `.run()`/`.all()`/`.first()`), so nothing executes inside the
6
+ * loop. The walk-up must cross the arrow function boundary and still recognise
7
+ * `Promise.all` as the promise combinator, not the eager D1 `.all()`.
8
+ */
9
+ interface ItemRow {
10
+ id: number;
11
+ }
12
+ export declare function stageMapped(db: any, rows: ItemRow[]): Promise<void>;
13
+ export {};
14
+ //# sourceMappingURL=item-09-promise-all-map-of-statements-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-09-promise-all-map-of-statements-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzE"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Spec-52 R1 item 9 — Promise.all of a .map() of plain statements (oracle: MUST NOT fire).
3
+ *
4
+ * `Promise.all(rows.map(r => db.prepare(sql).bind(r)))` wraps *statement
5
+ * objects* (no `.run()`/`.all()`/`.first()`), so nothing executes inside the
6
+ * loop. The walk-up must cross the arrow function boundary and still recognise
7
+ * `Promise.all` as the promise combinator, not the eager D1 `.all()`.
8
+ */
9
+ export async function stageMapped(db, rows) {
10
+ for (const row of rows) {
11
+ await Promise.all(rows.map((r) => db.prepare('SELECT id FROM items WHERE id = ?').bind(r.id)));
12
+ }
13
+ }
14
+ //# sourceMappingURL=item-09-promise-all-map-of-statements-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-09-promise-all-map-of-statements-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-09-promise-all-map-of-statements-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAO,EAAE,IAAe;IACxD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,CAAC,GAAG,CACf,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAC5E,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Spec-52 R1 item 10 — Promise.all of a .map() of eager `.all()` calls (oracle: MUST fire).
3
+ *
4
+ * The discriminating positive for the Promise.all guard: `Promise.all` is a
5
+ * promise combinator and must be *skipped*, but the eager D1 `.all()` on each
6
+ * mapped statement still executes per row — a genuine N+1. Crossing the arrow
7
+ * function to reach the eager method (and skip the combinator on the way) is
8
+ * exactly what the guard must get right.
9
+ */
10
+ interface ItemRow {
11
+ id: number;
12
+ }
13
+ export declare function fetchMapped(db: any, rows: ItemRow[]): Promise<unknown[]>;
14
+ export {};
15
+ //# sourceMappingURL=item-10-promise-all-map-of-eager-all-in-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-10-promise-all-map-of-eager-all-in-loop.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAO9E"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Spec-52 R1 item 10 — Promise.all of a .map() of eager `.all()` calls (oracle: MUST fire).
3
+ *
4
+ * The discriminating positive for the Promise.all guard: `Promise.all` is a
5
+ * promise combinator and must be *skipped*, but the eager D1 `.all()` on each
6
+ * mapped statement still executes per row — a genuine N+1. Crossing the arrow
7
+ * function to reach the eager method (and skip the combinator on the way) is
8
+ * exactly what the guard must get right.
9
+ */
10
+ export async function fetchMapped(db, rows) {
11
+ for (const row of rows) {
12
+ await Promise.all(rows.map((r) => db.prepare('SELECT id FROM items WHERE id = ?').bind(r.id).all()));
13
+ }
14
+ return [];
15
+ }
16
+ //# sourceMappingURL=item-10-promise-all-map-of-eager-all-in-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-10-promise-all-map-of-eager-all-in-loop.js","sourceRoot":"","sources":["../../../../src/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAO,EAAE,IAAe;IACxD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,CAAC,GAAG,CACf,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -146,7 +146,7 @@ export function filterViolationsBySeverity(violations, minSeverity) {
146
146
  if (!minSeverity) {
147
147
  return violations;
148
148
  }
149
- const severityOrder = { critical: 3, warning: 2, suggestion: 1, off: 0 };
149
+ const severityOrder = { critical: 3, severe: 2, high: 1 };
150
150
  const minLevel = severityOrder[minSeverity] || 0;
151
151
  return violations.filter(v => severityOrder[v.severity] >= minLevel);
152
152
  }
@@ -158,7 +158,7 @@ export function filterViolationsBySeverity(violations, minSeverity) {
158
158
  export function sortViolations(violations) {
159
159
  return violations.sort((a, b) => {
160
160
  // Sort by severity first
161
- const severityOrder = { critical: 3, warning: 2, suggestion: 1, off: 0 };
161
+ const severityOrder = { critical: 3, severe: 2, high: 1 };
162
162
  const severityDiff = severityOrder[b.severity] - severityOrder[a.severity];
163
163
  if (severityDiff !== 0)
164
164
  return severityDiff;