@intentius/chant 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (558) hide show
  1. package/dist/audit/catalog.d.ts +51 -1
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +36 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +26 -9
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/lineage.d.ts +10 -0
  8. package/dist/audit/lineage.d.ts.map +1 -0
  9. package/dist/audit/prior-art.d.ts +385 -0
  10. package/dist/audit/prior-art.d.ts.map +1 -0
  11. package/dist/audit/proof.d.ts +16 -0
  12. package/dist/audit/proof.d.ts.map +1 -1
  13. package/dist/audit/report-html.d.ts.map +1 -1
  14. package/dist/audit/report-model.d.ts +8 -1
  15. package/dist/audit/report-model.d.ts.map +1 -1
  16. package/dist/audit/report.d.ts.map +1 -1
  17. package/dist/audit/rules-doc.d.ts +3 -0
  18. package/dist/audit/rules-doc.d.ts.map +1 -1
  19. package/dist/audit/secrets.d.ts +15 -0
  20. package/dist/audit/secrets.d.ts.map +1 -1
  21. package/dist/audit/terraform-state.d.ts +46 -0
  22. package/dist/audit/terraform-state.d.ts.map +1 -0
  23. package/dist/build.d.ts +17 -1
  24. package/dist/build.d.ts.map +1 -1
  25. package/dist/claimed-fields.d.ts +161 -0
  26. package/dist/claimed-fields.d.ts.map +1 -0
  27. package/dist/cli/build-params-cli.d.ts +2 -2
  28. package/dist/cli/command-group.d.ts +14 -0
  29. package/dist/cli/command-group.d.ts.map +1 -1
  30. package/dist/cli/commands/audit.d.ts +17 -1
  31. package/dist/cli/commands/audit.d.ts.map +1 -1
  32. package/dist/cli/commands/build.d.ts.map +1 -1
  33. package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
  34. package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
  35. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  36. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
  38. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
  40. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  41. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  42. package/dist/cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/commands/lint.d.ts +13 -0
  44. package/dist/cli/commands/lint.d.ts.map +1 -1
  45. package/dist/cli/commands/onboard.d.ts +2 -0
  46. package/dist/cli/commands/onboard.d.ts.map +1 -1
  47. package/dist/cli/handlers/build.d.ts.map +1 -1
  48. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  49. package/dist/cli/handlers/operator.d.ts +82 -16
  50. package/dist/cli/handlers/operator.d.ts.map +1 -1
  51. package/dist/cli/handlers/run.d.ts +49 -42
  52. package/dist/cli/handlers/run.d.ts.map +1 -1
  53. package/dist/cli/main.d.ts +12 -1
  54. package/dist/cli/main.d.ts.map +1 -1
  55. package/dist/cli/mcp/op-tools.d.ts +16 -1
  56. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  57. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  58. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  59. package/dist/cli/plugins.d.ts +22 -0
  60. package/dist/cli/plugins.d.ts.map +1 -1
  61. package/dist/cli/registry.d.ts +40 -9
  62. package/dist/cli/registry.d.ts.map +1 -1
  63. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  64. package/dist/components/auto-release.d.ts +10 -20
  65. package/dist/components/auto-release.d.ts.map +1 -1
  66. package/dist/components/capability.d.ts +4 -4
  67. package/dist/components/cli-support.d.ts +24 -22
  68. package/dist/components/cli-support.d.ts.map +1 -1
  69. package/dist/components/component.d.ts +21 -6
  70. package/dist/components/component.d.ts.map +1 -1
  71. package/dist/components/driver-output.d.ts.map +1 -1
  72. package/dist/components/driver.d.ts +73 -37
  73. package/dist/components/driver.d.ts.map +1 -1
  74. package/dist/components/index.d.ts +1 -2
  75. package/dist/components/index.d.ts.map +1 -1
  76. package/dist/components/manifest-persistence.d.ts +1 -9
  77. package/dist/components/manifest-persistence.d.ts.map +1 -1
  78. package/dist/components/run-progress.d.ts +6 -6
  79. package/dist/components/run-progress.d.ts.map +1 -1
  80. package/dist/components/verbs/run-agent.d.ts +10 -22
  81. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  82. package/dist/components/verbs/verify.d.ts +2 -2
  83. package/dist/deep-observation.d.ts +17 -0
  84. package/dist/deep-observation.d.ts.map +1 -1
  85. package/dist/detectLexicon.d.ts +13 -0
  86. package/dist/detectLexicon.d.ts.map +1 -1
  87. package/dist/discovery/files.d.ts +4 -4
  88. package/dist/discovery/fold-import.d.ts.map +1 -1
  89. package/dist/discovery/param-deps.d.ts +33 -0
  90. package/dist/discovery/param-deps.d.ts.map +1 -1
  91. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  92. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  93. package/dist/fold/foldable-helpers.d.ts +5 -1
  94. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  95. package/dist/fold-provenance.d.ts +236 -0
  96. package/dist/fold-provenance.d.ts.map +1 -0
  97. package/dist/graph-ops.d.ts +1 -1
  98. package/dist/held-elsewhere.d.ts +85 -0
  99. package/dist/held-elsewhere.d.ts.map +1 -0
  100. package/dist/index.d.ts +3 -0
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/kubectl-context.d.ts +3 -3
  103. package/dist/lexicon-config.d.ts +4 -4
  104. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  105. package/dist/lexicon.d.ts +273 -16
  106. package/dist/lexicon.d.ts.map +1 -1
  107. package/dist/lifecycle/change-set.d.ts +13 -0
  108. package/dist/lifecycle/change-set.d.ts.map +1 -1
  109. package/dist/lifecycle/deep-diff.d.ts +133 -6
  110. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  111. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  112. package/dist/lifecycle/gate-ledger.d.ts +79 -3
  113. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  114. package/dist/lifecycle/index.d.ts +1 -0
  115. package/dist/lifecycle/index.d.ts.map +1 -1
  116. package/dist/lifecycle/release-ledger.d.ts +2 -2
  117. package/dist/lifecycle/run-ledger.d.ts +53 -0
  118. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  119. package/dist/lifecycle/symptoms.d.ts +3 -3
  120. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  121. package/dist/lint/config.d.ts +81 -2
  122. package/dist/lint/config.d.ts.map +1 -1
  123. package/dist/lint/post-synth.d.ts +55 -2
  124. package/dist/lint/post-synth.d.ts.map +1 -1
  125. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  126. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  127. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  128. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  129. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  130. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  131. package/dist/lint/rules/comp/index.d.ts +1 -1
  132. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  133. package/dist/lint/rules/op/index.d.ts +16 -0
  134. package/dist/lint/rules/op/index.d.ts.map +1 -0
  135. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  136. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  137. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  138. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  139. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  140. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  141. package/dist/lint/rules/op/support.d.ts +12 -0
  142. package/dist/lint/rules/op/support.d.ts.map +1 -0
  143. package/dist/lint/suppressions.d.ts +108 -0
  144. package/dist/lint/suppressions.d.ts.map +1 -0
  145. package/dist/live-endpoint.d.ts +1 -1
  146. package/dist/op/activities/activity-contracts.d.ts +54 -0
  147. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  148. package/dist/op/activities/apply.d.ts +262 -0
  149. package/dist/op/activities/apply.d.ts.map +1 -0
  150. package/dist/op/activities/build.d.ts +13 -0
  151. package/dist/op/activities/build.d.ts.map +1 -0
  152. package/dist/op/activities/converge.d.ts +110 -0
  153. package/dist/op/activities/converge.d.ts.map +1 -0
  154. package/dist/op/activities/env-teardown.d.ts +76 -0
  155. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  156. package/dist/op/activities/guard-validate.d.ts +64 -0
  157. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  158. package/dist/op/activities/http-check.d.ts +34 -0
  159. package/dist/op/activities/http-check.d.ts.map +1 -0
  160. package/dist/op/activities/index.d.ts +31 -0
  161. package/dist/op/activities/index.d.ts.map +1 -0
  162. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  163. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  164. package/dist/op/activities/lifecycle.d.ts +42 -0
  165. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  166. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  167. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  168. package/dist/op/activities/policy.d.ts +17 -0
  169. package/dist/op/activities/policy.d.ts.map +1 -0
  170. package/dist/op/activities/reconcile.d.ts +149 -0
  171. package/dist/op/activities/reconcile.d.ts.map +1 -0
  172. package/dist/op/activities/shell.d.ts +13 -0
  173. package/dist/op/activities/shell.d.ts.map +1 -0
  174. package/dist/op/activities/teardown.d.ts +10 -0
  175. package/dist/op/activities/teardown.d.ts.map +1 -0
  176. package/dist/op/activities/util.d.ts +7 -0
  177. package/dist/op/activities/util.d.ts.map +1 -0
  178. package/dist/op/activities/wait.d.ts +28 -0
  179. package/dist/op/activities/wait.d.ts.map +1 -0
  180. package/dist/op/activities/workflow-audit.d.ts +92 -0
  181. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  182. package/dist/op/activity-contract-registry.d.ts +76 -0
  183. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  184. package/dist/op/activity-contract.d.ts +25 -18
  185. package/dist/op/activity-contract.d.ts.map +1 -1
  186. package/dist/op/activity-failure.d.ts +28 -0
  187. package/dist/op/activity-failure.d.ts.map +1 -0
  188. package/dist/op/activity-profiles.d.ts +118 -0
  189. package/dist/op/activity-profiles.d.ts.map +1 -0
  190. package/dist/op/activity-registry.d.ts +22 -38
  191. package/dist/op/activity-registry.d.ts.map +1 -1
  192. package/dist/op/activity-runtime.d.ts +3 -14
  193. package/dist/op/activity-runtime.d.ts.map +1 -1
  194. package/dist/op/builders.d.ts +85 -30
  195. package/dist/op/builders.d.ts.map +1 -1
  196. package/dist/op/change-signal.d.ts +91 -0
  197. package/dist/op/change-signal.d.ts.map +1 -0
  198. package/dist/op/composites/apply-op.d.ts +118 -0
  199. package/dist/op/composites/apply-op.d.ts.map +1 -0
  200. package/dist/op/composites/converge-op.d.ts +120 -0
  201. package/dist/op/composites/converge-op.d.ts.map +1 -0
  202. package/dist/op/composites/index.d.ts +26 -0
  203. package/dist/op/composites/index.d.ts.map +1 -0
  204. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  205. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  206. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  207. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  208. package/dist/op/composites/reconcile-op.d.ts +57 -0
  209. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  210. package/dist/op/composites/watch-op.d.ts +60 -0
  211. package/dist/op/composites/watch-op.d.ts.map +1 -0
  212. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  213. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  214. package/dist/op/converge-rule.d.ts +12 -10
  215. package/dist/op/converge-rule.d.ts.map +1 -1
  216. package/dist/op/cron.d.ts +51 -0
  217. package/dist/op/cron.d.ts.map +1 -0
  218. package/dist/op/discover.d.ts +9 -0
  219. package/dist/op/discover.d.ts.map +1 -1
  220. package/dist/op/duration.d.ts +11 -0
  221. package/dist/op/duration.d.ts.map +1 -0
  222. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  223. package/dist/op/gate-name.d.ts +40 -0
  224. package/dist/op/gate-name.d.ts.map +1 -0
  225. package/dist/op/gate-summary.d.ts +43 -0
  226. package/dist/op/gate-summary.d.ts.map +1 -0
  227. package/dist/op/gate.d.ts +90 -0
  228. package/dist/op/gate.d.ts.map +1 -0
  229. package/dist/op/generate-pipeline.d.ts +9 -0
  230. package/dist/op/generate-pipeline.d.ts.map +1 -1
  231. package/dist/op/index.d.ts +29 -9
  232. package/dist/op/index.d.ts.map +1 -1
  233. package/dist/op/local-executor.d.ts +114 -28
  234. package/dist/op/local-executor.d.ts.map +1 -1
  235. package/dist/op/local-output.d.ts +11 -1
  236. package/dist/op/local-output.d.ts.map +1 -1
  237. package/dist/op/op-ir.d.ts +185 -0
  238. package/dist/op/op-ir.d.ts.map +1 -0
  239. package/dist/op/op-verb-class.d.ts +10 -2
  240. package/dist/op/op-verb-class.d.ts.map +1 -1
  241. package/dist/op/operator.d.ts +93 -5
  242. package/dist/op/operator.d.ts.map +1 -1
  243. package/dist/op/receipt-store.d.ts +1 -1
  244. package/dist/op/receipt-store.d.ts.map +1 -1
  245. package/dist/op/resource.d.ts +15 -1
  246. package/dist/op/resource.d.ts.map +1 -1
  247. package/dist/op/runtime.d.ts +200 -0
  248. package/dist/op/runtime.d.ts.map +1 -0
  249. package/dist/op/runtimes/local.d.ts +29 -0
  250. package/dist/op/runtimes/local.d.ts.map +1 -0
  251. package/dist/op/step-output-ref.d.ts +15 -13
  252. package/dist/op/step-output-ref.d.ts.map +1 -1
  253. package/dist/op/types.d.ts +96 -29
  254. package/dist/op/types.d.ts.map +1 -1
  255. package/dist/ownership.d.ts +1 -1
  256. package/dist/provenance.d.ts +21 -0
  257. package/dist/provenance.d.ts.map +1 -1
  258. package/dist/serializer-walker.d.ts.map +1 -1
  259. package/dist/terraform/bridge.d.ts +26 -7
  260. package/dist/terraform/bridge.d.ts.map +1 -1
  261. package/dist/terraform/carve-provider.d.ts +28 -2
  262. package/dist/terraform/carve-provider.d.ts.map +1 -1
  263. package/dist/terraform/data-source-shape.d.ts +77 -0
  264. package/dist/terraform/data-source-shape.d.ts.map +1 -0
  265. package/dist/terraform/graph.d.ts.map +1 -1
  266. package/dist/terraform/providers/kubernetes.d.ts +5 -0
  267. package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
  268. package/dist/terraform/tier-map.d.ts +16 -4
  269. package/dist/terraform/tier-map.d.ts.map +1 -1
  270. package/dist/terraform/types.d.ts +8 -0
  271. package/dist/terraform/types.d.ts.map +1 -1
  272. package/package.json +6 -1
  273. package/src/audit/catalog.test.ts +79 -3
  274. package/src/audit/catalog.ts +111 -13
  275. package/src/audit/core.test.ts +26 -0
  276. package/src/audit/core.ts +84 -18
  277. package/src/audit/discover.test.ts +39 -0
  278. package/src/audit/discover.ts +202 -18
  279. package/src/audit/lineage.ts +100 -0
  280. package/src/audit/prior-art.ts +153 -0
  281. package/src/audit/proof.test.ts +67 -0
  282. package/src/audit/proof.ts +70 -0
  283. package/src/audit/report-html.ts +9 -4
  284. package/src/audit/report-model.ts +16 -2
  285. package/src/audit/report.test.ts +33 -0
  286. package/src/audit/report.ts +5 -3
  287. package/src/audit/rules-doc.test.ts +34 -2
  288. package/src/audit/rules-doc.ts +82 -9
  289. package/src/audit/secrets.ts +15 -3
  290. package/src/audit/terraform-state.test.ts +144 -0
  291. package/src/audit/terraform-state.ts +131 -0
  292. package/src/build.ts +31 -1
  293. package/src/claimed-fields.test.ts +145 -0
  294. package/src/claimed-fields.ts +208 -0
  295. package/src/cli/build-params-cli.ts +2 -2
  296. package/src/cli/command-group.ts +14 -0
  297. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  298. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  299. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  300. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  301. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  302. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  303. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  304. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  305. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  306. package/src/cli/commands/audit.test.ts +191 -11
  307. package/src/cli/commands/audit.ts +78 -28
  308. package/src/cli/commands/build.test.ts +330 -9
  309. package/src/cli/commands/build.ts +170 -24
  310. package/src/cli/commands/carve-bridge.test.ts +237 -14
  311. package/src/cli/commands/carve-bridge.ts +21 -17
  312. package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
  313. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  314. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  315. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  316. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  317. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  318. package/src/cli/commands/init-lexicon.test.ts +97 -2
  319. package/src/cli/commands/init-lexicon.ts +4 -2
  320. package/src/cli/commands/init.test.ts +35 -1
  321. package/src/cli/commands/init.ts +14 -3
  322. package/src/cli/commands/lint.test.ts +194 -1
  323. package/src/cli/commands/lint.ts +205 -7
  324. package/src/cli/commands/onboard.test.ts +75 -0
  325. package/src/cli/commands/onboard.ts +54 -23
  326. package/src/cli/handlers/build.test.ts +81 -1
  327. package/src/cli/handlers/build.ts +15 -1
  328. package/src/cli/handlers/graph.test.ts +3 -3
  329. package/src/cli/handlers/lifecycle.test.ts +195 -12
  330. package/src/cli/handlers/lifecycle.ts +123 -6
  331. package/src/cli/handlers/misc.ts +1 -1
  332. package/src/cli/handlers/operator.test.ts +183 -11
  333. package/src/cli/handlers/operator.ts +341 -55
  334. package/src/cli/handlers/run.test.ts +568 -1295
  335. package/src/cli/handlers/run.ts +446 -912
  336. package/src/cli/main.test.ts +22 -16
  337. package/src/cli/main.ts +66 -33
  338. package/src/cli/mcp/op-tools.ts +147 -88
  339. package/src/cli/mcp/resource-handlers.ts +10 -37
  340. package/src/cli/mcp/resources/context.ts +11 -12
  341. package/src/cli/mcp/server.test.ts +25 -27
  342. package/src/cli/mcp/server.ts +2 -2
  343. package/src/cli/plugins.test.ts +68 -2
  344. package/src/cli/plugins.ts +39 -0
  345. package/src/cli/registry.ts +40 -9
  346. package/src/codegen/docs.ts +1 -1
  347. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  348. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  349. package/src/components/README.md +4 -3
  350. package/src/components/__fixtures__/neo4j-fanout.json +1 -1
  351. package/src/components/auto-release.test.ts +4 -20
  352. package/src/components/auto-release.ts +17 -42
  353. package/src/components/capability.ts +4 -4
  354. package/src/components/cli-support.test.ts +28 -12
  355. package/src/components/cli-support.ts +60 -47
  356. package/src/components/component-schema.test.ts +18 -2
  357. package/src/components/component.schema.json +18 -5
  358. package/src/components/component.test.ts +2 -2
  359. package/src/components/component.ts +27 -7
  360. package/src/components/config-defaults.test.ts +2 -2
  361. package/src/components/driver-output.ts +14 -2
  362. package/src/components/driver.test.ts +109 -20
  363. package/src/components/driver.ts +201 -84
  364. package/src/components/index.ts +1 -6
  365. package/src/components/manifest-persistence.test.ts +2 -20
  366. package/src/components/manifest-persistence.ts +4 -24
  367. package/src/components/pilots/neo4j-fanout.pilot.ts +3 -3
  368. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  369. package/src/components/run-progress.ts +6 -6
  370. package/src/components/verbs/run-agent.test.ts +19 -0
  371. package/src/components/verbs/run-agent.ts +12 -24
  372. package/src/components/verbs/verify.ts +2 -2
  373. package/src/deep-observation.test.ts +75 -0
  374. package/src/deep-observation.ts +48 -0
  375. package/src/detectLexicon.ts +18 -1
  376. package/src/discovery/collect.test.ts +3 -3
  377. package/src/discovery/files.ts +4 -4
  378. package/src/discovery/fold-import.test.ts +1 -1
  379. package/src/discovery/fold-import.ts +105 -2
  380. package/src/discovery/param-deps.test.ts +112 -1
  381. package/src/discovery/param-deps.ts +193 -0
  382. package/src/discovery/sandbox/bundle.ts +15 -1
  383. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  384. package/src/discovery/sandbox/config-wire.ts +2 -2
  385. package/src/fold/fold.test.ts +56 -0
  386. package/src/fold/foldable-helpers.ts +82 -2
  387. package/src/fold-provenance-exclusion.test.ts +186 -0
  388. package/src/fold-provenance.test.ts +495 -0
  389. package/src/fold-provenance.ts +441 -0
  390. package/src/graph-ops.test.ts +3 -3
  391. package/src/graph-ops.ts +1 -1
  392. package/src/held-elsewhere.test.ts +102 -0
  393. package/src/held-elsewhere.ts +114 -0
  394. package/src/index.ts +3 -0
  395. package/src/kubectl-context.ts +3 -3
  396. package/src/lexicon-config.test.ts +5 -5
  397. package/src/lexicon-config.ts +4 -4
  398. package/src/lexicon-plugin-helpers.ts +1 -1
  399. package/src/lexicon.ts +296 -16
  400. package/src/lifecycle/change-set.test.ts +49 -0
  401. package/src/lifecycle/change-set.ts +67 -0
  402. package/src/lifecycle/converge-ledger.ts +1 -1
  403. package/src/lifecycle/deep-diff.test.ts +267 -14
  404. package/src/lifecycle/deep-diff.ts +217 -14
  405. package/src/lifecycle/deep-observe.test.ts +3 -1
  406. package/src/lifecycle/deep-observe.ts +16 -6
  407. package/src/lifecycle/gate-ledger.test.ts +74 -2
  408. package/src/lifecycle/gate-ledger.ts +154 -36
  409. package/src/lifecycle/index.ts +1 -0
  410. package/src/lifecycle/release-ledger.ts +2 -2
  411. package/src/lifecycle/run-ledger.test.ts +268 -0
  412. package/src/lifecycle/run-ledger.ts +201 -0
  413. package/src/lifecycle/symptoms.ts +3 -3
  414. package/src/lint/config.test.ts +82 -1
  415. package/src/lint/config.ts +112 -3
  416. package/src/lint/pipeline-change-gate.test.ts +2 -2
  417. package/src/lint/policy.test.ts +1 -1
  418. package/src/lint/post-synth.test.ts +31 -0
  419. package/src/lint/post-synth.ts +54 -1
  420. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  421. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  422. package/src/lint/rules/comp/comp.test.ts +27 -1
  423. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -0
  424. package/src/lint/rules/comp/index.ts +3 -3
  425. package/src/lint/rules/op/index.ts +23 -0
  426. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  427. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  428. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  429. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  430. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  431. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  432. package/src/lint/rules/op/support.ts +11 -0
  433. package/src/lint/suppressions.test.ts +163 -0
  434. package/src/lint/suppressions.ts +204 -0
  435. package/src/live-endpoint.ts +1 -1
  436. package/src/op/activities/activity-contracts.ts +66 -0
  437. package/src/op/activities/apply.test.ts +567 -0
  438. package/src/op/activities/apply.ts +662 -0
  439. package/src/op/activities/build.ts +26 -0
  440. package/src/op/activities/converge.test.ts +330 -0
  441. package/src/op/activities/converge.ts +449 -0
  442. package/src/op/activities/env-teardown.test.ts +187 -0
  443. package/src/op/activities/env-teardown.ts +171 -0
  444. package/src/op/activities/guard-validate.test.ts +139 -0
  445. package/src/op/activities/guard-validate.ts +223 -0
  446. package/src/op/activities/http-check.test.ts +48 -0
  447. package/src/op/activities/http-check.ts +55 -0
  448. package/src/op/activities/index.ts +119 -0
  449. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  450. package/src/op/activities/lexicon-upgrade.ts +673 -0
  451. package/src/op/activities/lifecycle.ts +85 -0
  452. package/src/op/activities/pipeline-audit.test.ts +67 -0
  453. package/src/op/activities/pipeline-audit.ts +201 -0
  454. package/src/op/activities/policy.test.ts +19 -0
  455. package/src/op/activities/policy.ts +37 -0
  456. package/src/op/activities/reconcile.test.ts +161 -0
  457. package/src/op/activities/reconcile.ts +354 -0
  458. package/src/op/activities/shell.ts +26 -0
  459. package/src/op/activities/teardown.ts +22 -0
  460. package/src/op/activities/util.ts +6 -0
  461. package/src/op/activities/wait.ts +69 -0
  462. package/src/op/activities/workflow-audit.test.ts +90 -0
  463. package/src/op/activities/workflow-audit.ts +203 -0
  464. package/src/op/activity-contract-registry.test.ts +98 -0
  465. package/src/op/activity-contract-registry.ts +114 -0
  466. package/src/op/activity-contract.test.ts +1 -1
  467. package/src/op/activity-contract.ts +37 -33
  468. package/src/op/activity-failure.ts +34 -0
  469. package/src/op/activity-profiles.test.ts +56 -0
  470. package/src/op/activity-profiles.ts +116 -0
  471. package/src/op/activity-registry.test.ts +37 -28
  472. package/src/op/activity-registry.ts +27 -56
  473. package/src/op/activity-runtime.ts +3 -47
  474. package/src/op/builders-exports.test.ts +0 -49
  475. package/src/op/builders-typed.test.ts +164 -0
  476. package/src/op/builders.ts +125 -41
  477. package/src/op/change-signal.test.ts +117 -0
  478. package/src/op/change-signal.ts +169 -0
  479. package/src/op/composites/apply-op.ts +218 -0
  480. package/src/op/composites/composites.test.ts +379 -0
  481. package/src/op/composites/converge-op.test.ts +168 -0
  482. package/src/op/composites/converge-op.ts +214 -0
  483. package/src/op/composites/index.ts +26 -0
  484. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  485. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  486. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  487. package/src/op/composites/pipeline-audit-op.ts +78 -0
  488. package/src/op/composites/reconcile-op.test.ts +51 -0
  489. package/src/op/composites/reconcile-op.ts +110 -0
  490. package/src/op/composites/watch-op.ts +108 -0
  491. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  492. package/src/op/composites/workflow-audit-op.ts +88 -0
  493. package/src/op/converge-rule.ts +12 -10
  494. package/src/op/cron.test.ts +128 -0
  495. package/src/op/cron.ts +149 -0
  496. package/src/op/discover-root.test.ts +105 -1
  497. package/src/op/discover.test.ts +6 -2
  498. package/src/op/discover.ts +84 -16
  499. package/src/op/duration.ts +21 -0
  500. package/src/op/effect-step.test.ts +29 -14
  501. package/src/op/emulator-lifecycle.ts +1 -2
  502. package/src/op/gate-name.test.ts +65 -0
  503. package/src/op/gate-name.ts +60 -0
  504. package/src/op/gate-summary.ts +84 -0
  505. package/src/op/gate.ts +155 -0
  506. package/src/op/generate-pipeline.test.ts +65 -3
  507. package/src/op/generate-pipeline.ts +19 -2
  508. package/src/op/index.ts +48 -8
  509. package/src/op/local-executor.test.ts +178 -34
  510. package/src/op/local-executor.ts +371 -102
  511. package/src/op/local-output.test.ts +95 -11
  512. package/src/op/local-output.ts +33 -4
  513. package/src/op/op-ir.test.ts +292 -0
  514. package/src/op/op-ir.ts +419 -0
  515. package/src/op/op-verb-class.test.ts +2 -2
  516. package/src/op/op-verb-class.ts +19 -5
  517. package/src/op/op.test.ts +28 -7
  518. package/src/op/operator.test.ts +543 -6
  519. package/src/op/operator.ts +203 -20
  520. package/src/op/receipt-store.ts +1 -1
  521. package/src/op/resource.ts +20 -2
  522. package/src/op/runtime.ts +203 -0
  523. package/src/op/runtimes/local.test.ts +181 -0
  524. package/src/op/runtimes/local.ts +212 -0
  525. package/src/op/step-output-ref.ts +15 -13
  526. package/src/op/types.ts +106 -26
  527. package/src/ownership.ts +1 -1
  528. package/src/provenance.test.ts +6 -0
  529. package/src/provenance.ts +21 -1
  530. package/src/serializer-walker.test.ts +42 -0
  531. package/src/serializer-walker.ts +21 -2
  532. package/src/terraform/aws-resources.test.ts +13 -4
  533. package/src/terraform/bridge.test.ts +22 -9
  534. package/src/terraform/bridge.ts +89 -28
  535. package/src/terraform/carve-provider.ts +38 -2
  536. package/src/terraform/data-source-shape.ts +95 -0
  537. package/src/terraform/graph.ts +35 -7
  538. package/src/terraform/providers/kubernetes.ts +48 -2
  539. package/src/terraform/tier-map.ts +21 -6
  540. package/src/terraform/types.ts +8 -0
  541. package/dist/cli/handlers/op-progress.d.ts +0 -90
  542. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  543. package/dist/cli/handlers/run-client.d.ts +0 -127
  544. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  545. package/dist/cli/handlers/run-report.d.ts +0 -5
  546. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  547. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  548. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  549. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  550. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  551. package/src/cli/handlers/op-progress.test.ts +0 -268
  552. package/src/cli/handlers/op-progress.ts +0 -248
  553. package/src/cli/handlers/run-client.test.ts +0 -82
  554. package/src/cli/handlers/run-client.ts +0 -219
  555. package/src/cli/handlers/run-report.test.ts +0 -62
  556. package/src/cli/handlers/run-report.ts +0 -122
  557. package/src/components/temporal-codegen-loader.ts +0 -41
  558. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -1,14 +1,14 @@
1
1
  import { describe, test, expect } from "vitest";
2
2
  import { readdirSync } from "fs";
3
3
  import { join } from "path";
4
- import { RULE_CATALOG, RULE_CATEGORY, auditRule, resolveAuditCatalog } from "./catalog";
4
+ import { PRIOR_ART, RULE_CATALOG, RULE_CATEGORY, auditRule, resolveAuditCatalog, canonicalRuleId } from "./catalog";
5
5
  import { loadPlugins } from "../cli/plugins";
6
6
 
7
7
  /**
8
8
  * Every lexicon in the repo, enumerated rather than listed (#1346).
9
9
  *
10
10
  * This was nine hardcoded names. It missed fountain, which contributes a
11
- * catalog, and could not notice fly and temporal, which did not — so their six
11
+ * catalog, and could not notice fly, which did not — so their six
12
12
  * post-synth checks reached `chant audit` with no title, tier, fix kind, or
13
13
  * category, and the guard meant to catch exactly that was blind to them. A
14
14
  * thirteenth lexicon would have been invisible to it on arrival.
@@ -67,6 +67,11 @@ describe("RULE_CATALOG (aggregated: core static + lexicon-contributed, #687)", (
67
67
  .filter((id) => !shipped.has(id))
68
68
  // Core's own cross-cutting ids are not any one lexicon's to ship.
69
69
  .filter((id) => !(id in RULE_CATALOG))
70
+ // A deprecated rule is deliberately kept in the catalog after its check
71
+ // stops shipping (chant #2113). That's the whole point of `deprecated`
72
+ // over deleting the entry, so it's not "stale" the way an orphaned
73
+ // entry with no explanation is.
74
+ .filter((id) => !catalog[id]?.deprecated)
70
75
  .sort();
71
76
  expect(stale).toEqual([]);
72
77
  });
@@ -123,6 +128,33 @@ describe("RULE_CATALOG (aggregated: core static + lexicon-contributed, #687)", (
123
128
  }
124
129
  });
125
130
 
131
+ test("lineage entries key into PRIOR_ART, link over https, and use a known relation", async () => {
132
+ const relations = new Set(["equivalent", "overlaps", "extends"]);
133
+ for (const [id, m] of Object.entries(await aggregate())) {
134
+ for (const l of m.lineage ?? []) {
135
+ expect(l.tool in PRIOR_ART, `${id} credits unregistered tool "${l.tool}"`).toBe(true);
136
+ expect(l.url.startsWith("https://"), `${id} lineage url ${l.url}`).toBe(true);
137
+ expect(relations.has(l.relation), `${id} lineage relation ${l.relation}`).toBe(true);
138
+ }
139
+ }
140
+ });
141
+
142
+ test("lineage is credit, not authority: it never forces a category or tier", () => {
143
+ const m = auditRule("ZZZ001", "report-only", "guidance", "t", "r", {
144
+ lineage: [{ tool: Object.keys(PRIOR_ART)[0] as never, url: "https://example.invalid/x", relation: "overlaps" }],
145
+ });
146
+ expect(m.category).toBe("best-practice");
147
+ expect(m.tier).toBe("report-only");
148
+ expect(m.lineage?.length).toBe(1);
149
+ });
150
+
151
+ test("every registered prior-art tool is credited by at least one rule", async () => {
152
+ const used = new Set<string>();
153
+ for (const m of Object.values(await aggregate())) for (const l of m.lineage ?? []) used.add(l.tool);
154
+ const idle = Object.keys(PRIOR_ART).filter((k) => !used.has(k)).sort();
155
+ expect(idle, "PRIOR_ART entries no rule credits").toEqual([]);
156
+ });
157
+
126
158
  test("flagship security rules carry an authority citation", async () => {
127
159
  const catalog = await aggregate();
128
160
  const flagship = ["GHA017", "GHA021", "GHA029", "GHA033", "GHA034", "GHA036", "GHA037", "WGL016", "WGL029"];
@@ -134,13 +166,34 @@ describe("RULE_CATALOG (aggregated: core static + lexicon-contributed, #687)", (
134
166
  }
135
167
  });
136
168
 
169
+ test("a deprecated id must still resolve (chant #2113): the inverse of the every-check-has-metadata test above", async () => {
170
+ // A retired check ships no runtime PostSynthCheck any more, so it is
171
+ // absent from `realCheckIds()`/`shippedRuleIds()` — that's what makes it
172
+ // "deprecated" rather than merely renamed. The catalog entry, and its
173
+ // resolution, must survive that removal.
174
+ const catalog = {
175
+ ...RULE_CATALOG,
176
+ ZZZ900: auditRule("ZZZ900", "report-only", "guidance", "Retired check", "No longer applicable.", {
177
+ category: "best-practice",
178
+ }),
179
+ };
180
+ catalog.ZZZ900.deprecated = "superseded by ZZZ901";
181
+ expect(catalog.ZZZ900).toBeDefined();
182
+ expect(catalog.ZZZ900.deprecated).toBe("superseded by ZZZ901");
183
+ expect(canonicalRuleId("ZZZ900", catalog)).toBe("ZZZ900");
184
+ });
185
+
137
186
  test("deterministic fixes are limited to the safe mechanical set", async () => {
138
187
  const deterministic = Object.values(await aggregate())
139
188
  .filter((m) => m.fixKind === "deterministic")
140
189
  .map((m) => m.id)
141
190
  .sort();
191
+ // TF016 and TF019 (#2110) are the first report-only rules in the set: both
192
+ // unquote or delete exactly the flagged line, which is as mechanical as
193
+ // pinning an action, and `report-model.ts` renders them as quick wins for
194
+ // that reason. Every entry here needs a case in `proof.ts`'s `proveFix`.
142
195
  expect(deterministic).toEqual(
143
- ["GHA017", "GHA021", "GHA029", "GHA030", "GHA033", "WGL031"].sort(),
196
+ ["GHA017", "GHA021", "GHA029", "GHA030", "GHA033", "TF016", "TF019", "WGL031"].sort(),
144
197
  );
145
198
  });
146
199
  });
@@ -164,6 +217,29 @@ describe("auditRule (lexicon-facing catalog constructor, #687)", () => {
164
217
  });
165
218
  });
166
219
 
220
+ describe("canonicalRuleId (chant #2113, alias resolution)", () => {
221
+ const catalog: Record<string, ReturnType<typeof auditRule>> = {
222
+ NEW001: { ...auditRule("NEW001", "report-only", "guidance", "T", "r"), aliases: ["OLD001", "LEGACY_OLD"] },
223
+ };
224
+
225
+ test("a canonical id passes through unchanged", () => {
226
+ expect(canonicalRuleId("NEW001", catalog)).toBe("NEW001");
227
+ });
228
+
229
+ test("an alias resolves to the canonical id", () => {
230
+ expect(canonicalRuleId("OLD001", catalog)).toBe("NEW001");
231
+ expect(canonicalRuleId("LEGACY_OLD", catalog)).toBe("NEW001");
232
+ });
233
+
234
+ test("an id that is neither a key nor an alias passes through unchanged", () => {
235
+ expect(canonicalRuleId("NOBODY_KNOWS_THIS", catalog)).toBe("NOBODY_KNOWS_THIS");
236
+ });
237
+
238
+ test("defaults to the static core RULE_CATALOG when no catalog is given", () => {
239
+ expect(canonicalRuleId("SEC001")).toBe("SEC001");
240
+ });
241
+ });
242
+
167
243
  describe("resolveAuditCatalog (#687 aggregation seam)", () => {
168
244
  test("with no lexicons, returns a copy of the static core catalog", async () => {
169
245
  const resolved = await resolveAuditCatalog([]);
@@ -17,6 +17,11 @@
17
17
  * test asserts the aggregate stays in sync with the lexicons' post-synth checks.
18
18
  */
19
19
 
20
+ import type { Lineage } from "./prior-art";
21
+ import { applyLineage as attachLineage } from "./prior-art";
22
+ import { coreAuditLineage } from "./lineage";
23
+ export { PRIOR_ART, applyLineage, lineageLabel, type Lineage, type LineageRelation, type PriorArtEntry, type PriorArtTool } from "./prior-art";
24
+
20
25
  export type Tier = "merge-worthy" | "report-only";
21
26
 
22
27
  /** deterministic = safe auto-fix/diff; guidance = report text only (needs judgment). */
@@ -48,14 +53,42 @@ export interface RuleMeta {
48
53
  title: string;
49
54
  /** External backing so a finding isn't just chant's opinion. */
50
55
  authority?: Authority[];
56
+ /**
57
+ * Prior art: the open-source tools whose rules check the same condition
58
+ * (./prior-art.ts). Credit, not authority — never affects tier or category.
59
+ */
60
+ lineage?: Lineage[];
51
61
  /** One-line fix guidance (always present). */
52
62
  remediation: string;
53
63
  /**
54
64
  * False if the check reads the chant model (`ctx.entities`) rather than the
55
65
  * emitted YAML (`ctx.outputs`) — such a check won't fire on audited YAML.
56
- * All current post-synth checks are output-based, so this is true.
66
+ * Most post-synth checks are output-based, but cpln, fly, fountain, k3s
67
+ * and render all ship model-based checks with this false.
57
68
  */
58
69
  yamlBased: boolean;
70
+ /**
71
+ * Retired ids this rule used to ship under (chant #2113). A rule id is
72
+ * public the day it ships. trivy's checks carry `id`, `long_id` and
73
+ * `aliases` for exactly this reason, so a rename or a scheme migration
74
+ * doesn't strand every existing reference to the old id. An alias resolves
75
+ * to this entry wherever a rule id is looked up by a human or a config.
76
+ * `canonicalRuleId()` (below) is the shared resolver; a `lint.rules` key
77
+ * and #2111's HCL suppression both go through it (or will, since #2111
78
+ * lands separately and is not touched by this change).
79
+ */
80
+ aliases?: string[];
81
+ /**
82
+ * Set when this rule is retired: `true`, or a string explaining what
83
+ * replaced it / why it no longer fires. A deprecated rule stays in the
84
+ * catalog rather than being deleted. trivy's `deprecated: true` field lets
85
+ * a ruleset retire a check without breaking every existing suppression or
86
+ * dashboard that still names it, so its id keeps resolving (via `aliases`
87
+ * on whichever rule superseded it, when there is one) and the rules page
88
+ * still lists it, greyed out, instead of the id going dark with no
89
+ * explanation.
90
+ */
91
+ deprecated?: boolean | string;
59
92
  }
60
93
 
61
94
  // ── Authority references ─────────────────────────────────────────────
@@ -147,6 +180,10 @@ export const CWE_HARDCODED_CREDS: Authority = {
147
180
  name: "CWE-798 — Use of Hard-coded Credentials",
148
181
  url: "https://cwe.mitre.org/data/definitions/798.html",
149
182
  };
183
+ export const HASHICORP_STYLE_GITIGNORE: Authority = {
184
+ name: "HashiCorp Terraform style guide — .gitignore",
185
+ url: "https://developer.hashicorp.com/terraform/language/style#gitignore",
186
+ };
150
187
  export const CWE_CLEARTEXT: Authority = {
151
188
  name: "CWE-319 — Cleartext Transmission of Sensitive Information",
152
189
  url: "https://cwe.mitre.org/data/definitions/319.html",
@@ -159,13 +196,14 @@ function meta(
159
196
  title: string,
160
197
  remediation: string,
161
198
  authority?: Authority[],
199
+ lineage?: Lineage[],
162
200
  ): RuleMeta {
163
201
  // An authority citation is the strongest security signal, so it wins. Otherwise
164
202
  // the curated RULE_CATEGORY map (defined below, before RULE_CATALOG runs)
165
203
  // decides correctness vs best-practice vs security-without-authority; the drift
166
204
  // test guarantees every id is mapped, so the fallback is only a type belt.
167
205
  const category: Category = authority && authority.length > 0 ? "security" : RULE_CATEGORY[id] ?? "best-practice";
168
- return { id, tier, fixKind, category, title, remediation, authority, yamlBased: true };
206
+ return { id, tier, fixKind, category, title, remediation, authority, ...(lineage?.length ? { lineage } : {}), yamlBased: true };
169
207
  }
170
208
 
171
209
  const M = "merge-worthy" as const;
@@ -221,6 +259,10 @@ export const RULE_CATEGORY: Record<string, Category> = {
221
259
  NGX005: "security",
222
260
  NGX006: "best-practice",
223
261
  NGX007: "best-practice",
262
+ // TF023 — Terraform state committed to the repository. Core-owned for the
263
+ // same reason SEC/WRG/NGX are: it reads the discovered file list, not a
264
+ // parsed root module. See ./terraform-state.ts.
265
+ TF023: "security",
224
266
  // AGT — agent configuration (`chant audit --agents`). Core-owned like COR/EXT:
225
267
  // these run against the machine's own agent config, not against any one
226
268
  // lexicon's emitted output, so no lexicon ships them.
@@ -282,6 +324,21 @@ export const RULE_CATALOG: Record<string, RuleMeta> = {
282
324
  NGX006: meta("NGX006", R, G, "Server version disclosure", "Add `server_tokens off;` in the http block so nginx stops advertising its exact version in the Server header and error pages."),
283
325
  NGX007: meta("NGX007", R, G, "Access logging disabled at server scope", "Re-enable access_log at http/server scope (silencing a single noisy location is fine) so requests are recorded for incident investigation."),
284
326
 
327
+ // TF023 (#2110) — Terraform state, or the `.terraform/` working directory,
328
+ // committed to the repository. The one TF rule core owns: every other TF id
329
+ // is a post-synth check over a parsed root module (the terraform lexicon's
330
+ // `auditCatalog()`), while this one reads the discovered file list and never
331
+ // runs during `chant build`. Same shape as SEC/WRG/NGX above, and the same
332
+ // reason it lives here: `terraform-state.ts` needs no lexicon installed.
333
+ TF023: meta(
334
+ "TF023",
335
+ M,
336
+ G,
337
+ "Terraform state committed to the repository",
338
+ "Delete the state file (or `.terraform/`) from version control, add it to .gitignore, move the state to a remote backend, and rotate every credential the file held.",
339
+ [HASHICORP_STYLE_GITIGNORE],
340
+ ),
341
+
285
342
  // ── Agent configuration (`chant audit --agents`) ──────────────────
286
343
  AGT001: agentMeta(
287
344
  "AGT001",
@@ -337,6 +394,9 @@ export const RULE_CATALOG: Record<string, RuleMeta> = {
337
394
  ),
338
395
  };
339
396
 
397
+ // Prior art credits for the core static rules (SEC/WRG/NGX/AGT/EXT) — ./lineage.ts.
398
+ attachLineage(RULE_CATALOG, coreAuditLineage);
399
+
340
400
  /**
341
401
  * Build one catalog entry — the lexicon-facing constructor for
342
402
  * `LexiconPlugin.auditCatalog()` (#687), so a lexicon can declare its own rules'
@@ -351,10 +411,20 @@ export function auditRule(
351
411
  fixKind: FixKind,
352
412
  title: string,
353
413
  remediation: string,
354
- opts?: { authority?: Authority[]; category?: Category },
414
+ opts?: { authority?: Authority[]; category?: Category; lineage?: Lineage[] },
355
415
  ): RuleMeta {
356
416
  const category: Category = opts?.authority && opts.authority.length > 0 ? "security" : (opts?.category ?? "best-practice");
357
- return { id, tier, fixKind, category, title, remediation, authority: opts?.authority, yamlBased: true };
417
+ return {
418
+ id,
419
+ tier,
420
+ fixKind,
421
+ category,
422
+ title,
423
+ remediation,
424
+ authority: opts?.authority,
425
+ ...(opts?.lineage?.length ? { lineage: opts.lineage } : {}),
426
+ yamlBased: true,
427
+ };
358
428
  }
359
429
 
360
430
  /**
@@ -374,15 +444,18 @@ export async function resolveAuditCatalog(lexicons: string[]): Promise<Record<st
374
444
  lexicons.includes("forgejo") && !lexicons.includes("github") ? [...lexicons, "github"] : lexicons;
375
445
  if (needed.length === 0) return catalog;
376
446
  // Lazy import so importing this module doesn't pull in the plugin/config graph
377
- // (matches ./core.ts's `load` — see #408).
378
- const { loadPlugins } = await import("../cli/plugins");
379
- let plugins: Array<{ auditCatalog?(): Record<string, RuleMeta> }>;
380
- try {
381
- plugins = await loadPlugins(needed);
382
- } catch {
383
- return catalog; // a lexicon package isn't installed — fall back to the static core catalog.
384
- }
385
- for (const plugin of plugins) {
447
+ // (matches ./core.ts's `load` — see #408). Loaded one at a time (rather than
448
+ // the bulk `loadPlugins`, which aborts on the first failure) so one
449
+ // not-yet-installed lexicon (e.g. terraform, #2085) doesn't drop every other
450
+ // lexicon's contributed catalog back to the static core map.
451
+ const { loadPlugin } = await import("../cli/plugins");
452
+ for (const name of needed) {
453
+ let plugin: { auditCatalog?(): Record<string, RuleMeta> };
454
+ try {
455
+ plugin = await loadPlugin(name);
456
+ } catch {
457
+ continue; // a lexicon package isn't installed, its catalog just doesn't contribute.
458
+ }
386
459
  const contributed = plugin?.auditCatalog?.();
387
460
  if (contributed) Object.assign(catalog, contributed);
388
461
  }
@@ -394,6 +467,31 @@ export function ruleMeta(id: string): RuleMeta | undefined {
394
467
  return RULE_CATALOG[id];
395
468
  }
396
469
 
470
+ /**
471
+ * Resolve a rule id to its canonical form against `catalog` (chant #2113). An
472
+ * id that is itself a key of `catalog` passes through unchanged (this covers
473
+ * the common case without scanning every entry's `aliases`); otherwise every
474
+ * entry's `aliases` is searched for a match, and the id it belongs to is
475
+ * returned. An id neither a key nor anyone's alias passes through unchanged
476
+ * too, since this is a best-effort resolver, not a validator, so an unknown
477
+ * id is a caller's problem, not this function's.
478
+ *
479
+ * Exported so a `lint.rules`/preset key that names an alias resolves to the
480
+ * rule that actually runs (see `resolveConfiguredSeverity`,
481
+ * `../lint/config.ts`), and so #2111's HCL suppression code can key an
482
+ * inline `# chant-ignore <id>` off the same resolution once it lands — that
483
+ * suppression mechanism does not exist yet in this worktree (#2111 is a
484
+ * separate, parallel issue), so this function is exported now for it to call
485
+ * later rather than being wired into it here.
486
+ */
487
+ export function canonicalRuleId(id: string, catalog: Record<string, RuleMeta> = RULE_CATALOG): string {
488
+ if (id in catalog) return id;
489
+ for (const [canonicalId, meta] of Object.entries(catalog)) {
490
+ if (meta.aliases?.includes(id)) return canonicalId;
491
+ }
492
+ return id;
493
+ }
494
+
397
495
  /** Docs path for the audit rules reference (one anchor per rule id). */
398
496
  export const RULES_DOC_PATH = "/chant/lint-rules/audit-rules/";
399
497
 
@@ -208,6 +208,32 @@ describe("auditFiles", () => {
208
208
  expect(findings.map((f) => `${f.file}:${f.entity}`).sort()).toEqual(["a.yaml:one", "b.yaml:two"]);
209
209
  });
210
210
 
211
+ test("parse-to-graph with an async entities parser: auditFiles awaits it into ctx.entities (#2082)", async () => {
212
+ const entityCheck: PostSynthCheck = {
213
+ id: "ENT003",
214
+ description: "reads ctx.entities, not ctx.outputs",
215
+ check: (ctx) =>
216
+ [...ctx.entities.keys()].map((name) => ({ checkId: "ENT003", severity: "warning" as const, message: `saw ${name}`, entity: name })),
217
+ };
218
+ const findings = await auditFiles(
219
+ [
220
+ { path: "a.tf", content: "one", lexicon: "terraform" },
221
+ { path: "b.tf", content: "two", lexicon: "terraform" },
222
+ ],
223
+ {
224
+ checksProvider: async () => [entityCheck],
225
+ // The lexicon's parser is inherently async (a lazy-loaded wasm HCL
226
+ // parser, per #1567's terraform follow-up). The returned Promise
227
+ // must be awaited before the check sees ctx.entities.
228
+ entitiesProvider: async () => async (content) => {
229
+ await Promise.resolve();
230
+ return new Map([[content, { entityType: "T::X" } as never]]);
231
+ },
232
+ },
233
+ );
234
+ expect(findings.map((f) => `${f.file}:${f.entity}`).sort()).toEqual(["a.tf:one", "b.tf:two"]);
235
+ });
236
+
211
237
  test("parse-to-graph merge: the same entity name in two files keeps both — a duplicate check sees the collision cross-file", async () => {
212
238
  const dupCheck: PostSynthCheck = {
213
239
  id: "DUP001",
package/src/audit/core.ts CHANGED
@@ -22,9 +22,10 @@
22
22
 
23
23
  import { basename } from "path";
24
24
  import type { Severity } from "../lint/rule";
25
- import type { PostSynthCheck, PostSynthContext } from "../lint/post-synth";
25
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "../lint/post-synth";
26
+ import { applyInlineSuppressions, type SuppressionMetaFinding } from "../lint/suppressions";
26
27
  import type { SerializerResult } from "../serializer";
27
- import type { LexiconPlugin } from "../lexicon";
28
+ import type { AuditEntitiesInput, LexiconPlugin } from "../lexicon";
28
29
  import type { Declarable } from "../declarable";
29
30
 
30
31
  /**
@@ -60,6 +61,16 @@ export interface AuditInput {
60
61
  * — checks that read `output.files` (helm, docker) see the whole bundle.
61
62
  */
62
63
  files?: Record<string, string>;
64
+ /**
65
+ * Absolute directory this input's files were read from, set by discovery on
66
+ * a local walk for the inputs whose `path` names a directory (#2217). It
67
+ * reaches a lexicon's `auditEntities` as `AuditEntitiesInput.dir`, which is
68
+ * what lets terraform follow a root module's local `module` calls. Undefined
69
+ * for a remote tree fetch and for a single-file input.
70
+ */
71
+ dir?: string;
72
+ /** Absolute path of the audited root, when discovery walked one (#2217). The boundary `dir` may read within. */
73
+ baseDir?: string;
63
74
  }
64
75
 
65
76
  /** A finding produced by a post-synth check against an audited file. */
@@ -82,6 +93,8 @@ export interface AuditFinding {
82
93
  * itself.
83
94
  */
84
95
  fingerprint?: string;
96
+ /** Carried verbatim from the producing `PostSynthDiagnostic.missing` (chant #2113) — see its doc comment. */
97
+ missing?: { kind: string; scope: string };
85
98
  }
86
99
 
87
100
  /**
@@ -94,8 +107,18 @@ export type ChecksProvider = (lexicon: AuditLexicon) => Promise<PostSynthCheck[]
94
107
  * Parse one classified file's content into the lexicon's entity graph — the
95
108
  * parse-to-graph half of the audit (#1567). The returned map is what
96
109
  * `ctx.entities` holds during a build, so entity-reading checks run unchanged.
110
+ * May return a `Promise` for a lexicon whose parser is inherently async (e.g.
111
+ * terraform's HCL parser); `auditLexicon`'s `entitiesFor` awaits it.
112
+ *
113
+ * The second argument carries the input's discovery facts (its path, and the
114
+ * directory it was read from on a local walk), so a lexicon whose unit of
115
+ * parsing is a directory can name its scope after that directory and read the
116
+ * directories it references (#2217).
97
117
  */
98
- export type EntitiesParser = (content: string) => Map<string, Declarable>;
118
+ export type EntitiesParser = (
119
+ content: string,
120
+ input?: AuditEntitiesInput,
121
+ ) => Map<string, Declarable> | Promise<Map<string, Declarable>>;
99
122
 
100
123
  /**
101
124
  * Resolve the entities parser for a lexicon (its plugin's `auditEntities`,
@@ -179,13 +202,25 @@ async function defaultEntitiesProvider(lexicon: AuditLexicon): Promise<EntitiesP
179
202
  return parser;
180
203
  }
181
204
 
205
+ /**
206
+ * Mutable out-param for the inline (`# chant-ignore*`) suppression count
207
+ * (chant #2111): `auditFiles`'s return type stays `AuditFinding[]` for every
208
+ * existing caller, so a caller that wants the count (`chant audit`'s CLI, for
209
+ * its summary line) passes this in and reads `.count` back afterward, rather
210
+ * than `auditFiles` growing a `{findings, suppressedCount}` shape that would
211
+ * touch every existing call site.
212
+ */
213
+ export interface SuppressionStats {
214
+ count: number;
215
+ }
216
+
182
217
  /**
183
218
  * Audit a set of CI files and return all findings. Pure with respect to the
184
219
  * filesystem and network — callers supply file contents.
185
220
  */
186
221
  export async function auditFiles(
187
222
  inputs: AuditInput[],
188
- opts: { checksProvider?: ChecksProvider; entitiesProvider?: EntitiesProvider } = {},
223
+ opts: { checksProvider?: ChecksProvider; entitiesProvider?: EntitiesProvider; suppressionStats?: SuppressionStats } = {},
189
224
  ): Promise<AuditFinding[]> {
190
225
  const provider = opts.checksProvider ?? defaultChecksProvider;
191
226
  const entitiesProvider = opts.entitiesProvider ?? defaultEntitiesProvider;
@@ -203,7 +238,7 @@ export async function auditFiles(
203
238
  const checks = await provider(lexicon);
204
239
  if (checks.length === 0) continue;
205
240
  const parseEntities = await entitiesProvider(lexicon);
206
- findings.push(...auditLexicon(lexicon, files, checks, parseEntities));
241
+ findings.push(...(await auditLexicon(lexicon, files, checks, parseEntities, opts.suppressionStats)));
207
242
  }
208
243
 
209
244
  return findings;
@@ -224,14 +259,20 @@ function toOutput(file: AuditInput): string | SerializerResult {
224
259
  return { primary: file.content, files: { [basename(file.path)]: file.content } };
225
260
  }
226
261
 
262
+ interface CheckRunResult {
263
+ diagnostics: PostSynthDiagnostic[];
264
+ suppressed: PostSynthDiagnostic[];
265
+ meta: SuppressionMetaFinding[];
266
+ }
267
+
227
268
  function runChecks(
228
269
  checks: PostSynthCheck[],
229
270
  outputs: Map<string, string | SerializerResult>,
230
271
  entities: Map<string, Declarable> = new Map(),
231
- ): ReturnType<PostSynthCheck["check"]> {
272
+ ): CheckRunResult {
232
273
  const buildResult: PostSynthContext["buildResult"] = { outputs, entities, warnings: [], errors: [], sourceFileCount: outputs.size };
233
274
  const ctx: PostSynthContext = { outputs, entities: buildResult.entities, buildResult };
234
- const diags = [];
275
+ const diags: PostSynthDiagnostic[] = [];
235
276
  for (const check of checks) {
236
277
  try {
237
278
  diags.push(...check.check(ctx));
@@ -239,7 +280,11 @@ function runChecks(
239
280
  // A check that throws on unusual external YAML must not abort the audit.
240
281
  }
241
282
  }
242
- return diags;
283
+ // chant #2111: `rules` (for `ignorable: false`) is omitted, since `chant audit`
284
+ // has no project `lint.rules` to read here (see `applyInlineSuppressions`'s
285
+ // doc comment for why that's a deliberate scope line, not an oversight), so
286
+ // every rule id reads as ignorable, same as an unconfigured one everywhere.
287
+ return applyInlineSuppressions(diags, entities);
243
288
  }
244
289
 
245
290
  function diagKey(d: { checkId: string; entity?: string; message: string }): string {
@@ -281,40 +326,61 @@ function mergeEntities(maps: Array<Map<string, Declarable>>): Map<string, Declar
281
326
  * the entity graph parsed from its files — per file for the per-file pass, all
282
327
  * files merged for the all-files pass — so entity-reading checks fire and
283
328
  * cross-file facts (an Agent's Environment declared elsewhere, a name declared
284
- * twice across the apply directory) resolve in the all-files pass.
329
+ * twice across the apply directory) resolve in the all-files pass. The parser
330
+ * may be async (a lexicon's own choice, e.g. a wasm-backed HCL parser); this
331
+ * function awaits each file's entities before either pass runs.
285
332
  */
286
- function auditLexicon(lexicon: AuditLexicon, files: AuditInput[], checks: PostSynthCheck[], parseEntities?: EntitiesParser): AuditFinding[] {
287
- const entitiesFor = (file: AuditInput): Map<string, Declarable> => {
333
+ async function auditLexicon(
334
+ lexicon: AuditLexicon,
335
+ files: AuditInput[],
336
+ checks: PostSynthCheck[],
337
+ parseEntities?: EntitiesParser,
338
+ suppressionStats?: SuppressionStats,
339
+ ): Promise<AuditFinding[]> {
340
+ const entitiesFor = async (file: AuditInput): Promise<Map<string, Declarable>> => {
288
341
  if (!parseEntities) return new Map();
289
342
  try {
290
- return parseEntities(file.content);
343
+ return await parseEntities(file.content, {
344
+ path: file.path,
345
+ ...(file.dir !== undefined ? { dir: file.dir } : {}),
346
+ ...(file.baseDir !== undefined ? { baseDir: file.baseDir } : {}),
347
+ });
291
348
  } catch {
292
349
  // The audit contract is "runs against any repo" — unparseable content
293
350
  // contributes no entities, never a crash.
294
351
  return new Map();
295
352
  }
296
353
  };
297
- const perEntities = new Map(files.map((f) => [f.path, entitiesFor(f)]));
354
+ const perEntities = new Map(await Promise.all(files.map(async (f) => [f.path, await entitiesFor(f)] as const)));
298
355
 
299
356
  const perFindings: AuditFinding[] = [];
300
357
  const perKeys = new Set<string>();
358
+ // chant #2111: meta findings (an expired/misplaced/denied suppression
359
+ // directive) are collected from the per-file pass only: the all-files pass
360
+ // below sees the same entities merged, so it would report the identical
361
+ // directive again. Counted the same way, once, here too.
362
+ const metaFindings: AuditFinding[] = [];
301
363
  for (const file of files) {
302
- const diags = runChecks(checks, new Map([[file.path, toOutput(file)]]), perEntities.get(file.path));
364
+ const { diagnostics: diags, suppressed, meta } = runChecks(checks, new Map([[file.path, toOutput(file)]]), perEntities.get(file.path));
365
+ if (suppressionStats) suppressionStats.count += suppressed.length;
366
+ for (const m of meta) {
367
+ metaFindings.push({ checkId: m.checkId, severity: m.severity, message: m.message, file: m.file, lexicon, line: m.line });
368
+ }
303
369
  for (const d of diags) {
304
- perFindings.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: file.path, lexicon: d.lexicon ?? lexicon, entity: d.entity });
370
+ perFindings.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: file.path, lexicon: d.lexicon ?? lexicon, entity: d.entity, missing: d.missing });
305
371
  perKeys.add(diagKey(d));
306
372
  }
307
373
  }
308
374
 
309
375
  const allOutputs = new Map<string, string | SerializerResult>(files.map((f) => [f.path, toOutput(f)]));
310
- const allDiags = runChecks(checks, allOutputs, mergeEntities([...perEntities.values()]));
376
+ const { diagnostics: allDiags } = runChecks(checks, allOutputs, mergeEntities([...perEntities.values()]));
311
377
  const allKeys = new Set(allDiags.map(diagKey));
312
378
 
313
379
  const out: AuditFinding[] = perFindings.filter((f) => allKeys.has(diagKey(f)));
314
380
  for (const d of allDiags) {
315
381
  if (!perKeys.has(diagKey(d))) {
316
- out.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: CROSS_FILE, lexicon: d.lexicon ?? lexicon, entity: d.entity });
382
+ out.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: CROSS_FILE, lexicon: d.lexicon ?? lexicon, entity: d.entity, missing: d.missing });
317
383
  }
318
384
  }
319
- return out;
385
+ return [...out, ...metaFindings];
320
386
  }
@@ -92,6 +92,45 @@ describe("discoverByDetection (unified, detectTemplate-driven)", () => {
92
92
  const plugins = await loadAuditPlugins(["github", "definitely-not-a-real-lexicon"]);
93
93
  expect(plugins.map((p) => p.name)).toEqual(["github"]);
94
94
  });
95
+
96
+ test("loadAuditPlugins skips a lexicon whose package is not installed, quietly", async () => {
97
+ const plugins = await loadAuditPlugins(["github", "not-a-lexicon"]);
98
+ expect(plugins.map((p) => p.name)).toEqual(["github"]);
99
+ });
100
+
101
+ describe("Terraform directories as bundles, modeled on classifyHelm", () => {
102
+ // No real @intentius/chant-lexicon-terraform package exists yet (#2085), so
103
+ // these tests supply a bare stand-in plugin rather than loadAuditPlugins().
104
+ const terraform = { name: "terraform" };
105
+
106
+ test("a directory with two .tf files, and a nested subdirectory with its own .tf file, classify as two separate terraform inputs", () => {
107
+ const found = discoverByDetection(fixture("audit-terraform"), [terraform]);
108
+ const tf = found.filter((i) => i.lexicon === "terraform");
109
+ expect(targets(tf)).toEqual(["terraform:infra", "terraform:infra/modules/foo"]);
110
+ const root = tf.find((i) => i.path === "infra")!;
111
+ expect(Object.keys(root.files!).sort()).toEqual(["main.tf", "variables.tf"]);
112
+ const nested = tf.find((i) => i.path === "infra/modules/foo")!;
113
+ expect(Object.keys(nested.files!).sort()).toEqual(["main.tf"]);
114
+ });
115
+
116
+ test("a bundle's content is the whole root module in filename order, each file behind a `# file:` marker", () => {
117
+ const found = discoverByDetection(fixture("audit-terraform"), [terraform]);
118
+ const root = found.find((i) => i.lexicon === "terraform" && i.path === "infra")!;
119
+ expect(root.content).toContain("# file: main.tf\nresource \"null_resource\" \"root\" {}");
120
+ expect(root.content).toContain("# file: variables.tf\nvariable \"region\"");
121
+ expect(root.content.indexOf("# file: main.tf")).toBeLessThan(root.content.indexOf("# file: variables.tf"));
122
+ });
123
+
124
+ test(".terraform/ contributes no bundle (walkFiles already skips dot-directories outside WALK_DOT_DIRS)", () => {
125
+ const found = discoverByDetection(fixture("audit-terraform"), [terraform]);
126
+ expect(found.some((i) => i.path.includes(".terraform"))).toBe(false);
127
+ });
128
+
129
+ test("without the terraform plugin, .tf files are classified into nothing (skipped, same as an unloaded helm)", () => {
130
+ const found = discoverByDetection(fixture("audit-terraform"), []);
131
+ expect(found).toEqual([]);
132
+ });
133
+ });
95
134
  });
96
135
 
97
136
  describe("lexicon hints for unclaimed files (#1623)", () => {