@intentius/chant 0.56.0 → 0.58.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 (515) 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 +20 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +14 -8
  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/check-lexicon-docs.d.ts +1 -1
  34. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  35. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  36. package/dist/cli/commands/import.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.map +1 -1
  44. package/dist/cli/commands/onboard.d.ts +2 -0
  45. package/dist/cli/commands/onboard.d.ts.map +1 -1
  46. package/dist/cli/handlers/build.d.ts.map +1 -1
  47. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  48. package/dist/cli/handlers/operator.d.ts +82 -16
  49. package/dist/cli/handlers/operator.d.ts.map +1 -1
  50. package/dist/cli/handlers/run.d.ts +49 -42
  51. package/dist/cli/handlers/run.d.ts.map +1 -1
  52. package/dist/cli/main.d.ts +28 -1
  53. package/dist/cli/main.d.ts.map +1 -1
  54. package/dist/cli/mcp/op-tools.d.ts +16 -1
  55. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  56. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  57. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  58. package/dist/cli/registry.d.ts +26 -9
  59. package/dist/cli/registry.d.ts.map +1 -1
  60. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  61. package/dist/components/auto-release.d.ts +10 -20
  62. package/dist/components/auto-release.d.ts.map +1 -1
  63. package/dist/components/capability.d.ts +4 -4
  64. package/dist/components/cli-support.d.ts +20 -21
  65. package/dist/components/cli-support.d.ts.map +1 -1
  66. package/dist/components/component.d.ts +3 -3
  67. package/dist/components/component.d.ts.map +1 -1
  68. package/dist/components/driver-output.d.ts.map +1 -1
  69. package/dist/components/driver.d.ts +66 -36
  70. package/dist/components/driver.d.ts.map +1 -1
  71. package/dist/components/index.d.ts +1 -2
  72. package/dist/components/index.d.ts.map +1 -1
  73. package/dist/components/manifest-persistence.d.ts +1 -9
  74. package/dist/components/manifest-persistence.d.ts.map +1 -1
  75. package/dist/components/run-progress.d.ts +6 -6
  76. package/dist/components/run-progress.d.ts.map +1 -1
  77. package/dist/components/verbs/run-agent.d.ts +9 -15
  78. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  79. package/dist/components/verbs/verify.d.ts +2 -2
  80. package/dist/deep-observation.d.ts +17 -0
  81. package/dist/deep-observation.d.ts.map +1 -1
  82. package/dist/discovery/files.d.ts +4 -4
  83. package/dist/discovery/fold-import.d.ts.map +1 -1
  84. package/dist/discovery/param-deps.d.ts +33 -0
  85. package/dist/discovery/param-deps.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  87. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  88. package/dist/fold/foldable-helpers.d.ts +5 -1
  89. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  90. package/dist/fold-provenance.d.ts +236 -0
  91. package/dist/fold-provenance.d.ts.map +1 -0
  92. package/dist/graph-ops.d.ts +1 -1
  93. package/dist/held-elsewhere.d.ts +85 -0
  94. package/dist/held-elsewhere.d.ts.map +1 -0
  95. package/dist/import/ir-utils.d.ts +1 -1
  96. package/dist/import/ir-utils.d.ts.map +1 -1
  97. package/dist/import/parser.d.ts +33 -0
  98. package/dist/import/parser.d.ts.map +1 -1
  99. package/dist/index.d.ts +3 -0
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/kubectl-context.d.ts +3 -3
  102. package/dist/lexicon-config.d.ts +4 -4
  103. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  104. package/dist/lexicon.d.ts +104 -14
  105. package/dist/lexicon.d.ts.map +1 -1
  106. package/dist/lifecycle/change-set.d.ts +13 -0
  107. package/dist/lifecycle/change-set.d.ts.map +1 -1
  108. package/dist/lifecycle/deep-diff.d.ts +133 -6
  109. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  110. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  111. package/dist/lifecycle/gate-ledger.d.ts +71 -3
  112. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  113. package/dist/lifecycle/index.d.ts +1 -0
  114. package/dist/lifecycle/index.d.ts.map +1 -1
  115. package/dist/lifecycle/release-ledger.d.ts +2 -2
  116. package/dist/lifecycle/run-ledger.d.ts +53 -0
  117. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  118. package/dist/lifecycle/symptoms.d.ts +3 -3
  119. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  120. package/dist/lint/config.d.ts +81 -2
  121. package/dist/lint/config.d.ts.map +1 -1
  122. package/dist/lint/post-synth.d.ts +55 -2
  123. package/dist/lint/post-synth.d.ts.map +1 -1
  124. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  125. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  126. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  127. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  128. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  129. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  130. package/dist/lint/rules/comp/index.d.ts +1 -1
  131. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  132. package/dist/lint/rules/op/index.d.ts +16 -0
  133. package/dist/lint/rules/op/index.d.ts.map +1 -0
  134. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  135. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  136. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  137. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  138. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  139. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  140. package/dist/lint/rules/op/support.d.ts +12 -0
  141. package/dist/lint/rules/op/support.d.ts.map +1 -0
  142. package/dist/lint/suppressions.d.ts +108 -0
  143. package/dist/lint/suppressions.d.ts.map +1 -0
  144. package/dist/live-endpoint.d.ts +1 -1
  145. package/dist/op/activities/activity-contracts.d.ts +54 -0
  146. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  147. package/dist/op/activities/apply.d.ts +262 -0
  148. package/dist/op/activities/apply.d.ts.map +1 -0
  149. package/dist/op/activities/build.d.ts +13 -0
  150. package/dist/op/activities/build.d.ts.map +1 -0
  151. package/dist/op/activities/converge.d.ts +110 -0
  152. package/dist/op/activities/converge.d.ts.map +1 -0
  153. package/dist/op/activities/env-teardown.d.ts +76 -0
  154. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  155. package/dist/op/activities/guard-validate.d.ts +64 -0
  156. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  157. package/dist/op/activities/http-check.d.ts +34 -0
  158. package/dist/op/activities/http-check.d.ts.map +1 -0
  159. package/dist/op/activities/index.d.ts +31 -0
  160. package/dist/op/activities/index.d.ts.map +1 -0
  161. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  162. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  163. package/dist/op/activities/lifecycle.d.ts +42 -0
  164. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  165. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  166. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  167. package/dist/op/activities/policy.d.ts +17 -0
  168. package/dist/op/activities/policy.d.ts.map +1 -0
  169. package/dist/op/activities/reconcile.d.ts +89 -0
  170. package/dist/op/activities/reconcile.d.ts.map +1 -0
  171. package/dist/op/activities/shell.d.ts +13 -0
  172. package/dist/op/activities/shell.d.ts.map +1 -0
  173. package/dist/op/activities/teardown.d.ts +10 -0
  174. package/dist/op/activities/teardown.d.ts.map +1 -0
  175. package/dist/op/activities/util.d.ts +7 -0
  176. package/dist/op/activities/util.d.ts.map +1 -0
  177. package/dist/op/activities/wait.d.ts +28 -0
  178. package/dist/op/activities/wait.d.ts.map +1 -0
  179. package/dist/op/activities/workflow-audit.d.ts +92 -0
  180. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  181. package/dist/op/activity-contract-registry.d.ts +76 -0
  182. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  183. package/dist/op/activity-contract.d.ts +25 -18
  184. package/dist/op/activity-contract.d.ts.map +1 -1
  185. package/dist/op/activity-failure.d.ts +28 -0
  186. package/dist/op/activity-failure.d.ts.map +1 -0
  187. package/dist/op/activity-profiles.d.ts +118 -0
  188. package/dist/op/activity-profiles.d.ts.map +1 -0
  189. package/dist/op/activity-registry.d.ts +22 -38
  190. package/dist/op/activity-registry.d.ts.map +1 -1
  191. package/dist/op/activity-runtime.d.ts +3 -14
  192. package/dist/op/activity-runtime.d.ts.map +1 -1
  193. package/dist/op/builders.d.ts +84 -29
  194. package/dist/op/builders.d.ts.map +1 -1
  195. package/dist/op/composites/apply-op.d.ts +113 -0
  196. package/dist/op/composites/apply-op.d.ts.map +1 -0
  197. package/dist/op/composites/converge-op.d.ts +120 -0
  198. package/dist/op/composites/converge-op.d.ts.map +1 -0
  199. package/dist/op/composites/index.d.ts +26 -0
  200. package/dist/op/composites/index.d.ts.map +1 -0
  201. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  202. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  203. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  204. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  205. package/dist/op/composites/reconcile-op.d.ts +57 -0
  206. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  207. package/dist/op/composites/watch-op.d.ts +60 -0
  208. package/dist/op/composites/watch-op.d.ts.map +1 -0
  209. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  210. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  211. package/dist/op/converge-rule.d.ts +12 -10
  212. package/dist/op/converge-rule.d.ts.map +1 -1
  213. package/dist/op/cron.d.ts +51 -0
  214. package/dist/op/cron.d.ts.map +1 -0
  215. package/dist/op/discover.d.ts +9 -0
  216. package/dist/op/discover.d.ts.map +1 -1
  217. package/dist/op/duration.d.ts +11 -0
  218. package/dist/op/duration.d.ts.map +1 -0
  219. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  220. package/dist/op/gate.d.ts +90 -0
  221. package/dist/op/gate.d.ts.map +1 -0
  222. package/dist/op/generate-pipeline.d.ts +9 -0
  223. package/dist/op/generate-pipeline.d.ts.map +1 -1
  224. package/dist/op/index.d.ts +23 -7
  225. package/dist/op/index.d.ts.map +1 -1
  226. package/dist/op/local-executor.d.ts +114 -28
  227. package/dist/op/local-executor.d.ts.map +1 -1
  228. package/dist/op/local-output.d.ts +11 -1
  229. package/dist/op/local-output.d.ts.map +1 -1
  230. package/dist/op/op-ir.d.ts +184 -0
  231. package/dist/op/op-ir.d.ts.map +1 -0
  232. package/dist/op/op-verb-class.d.ts +10 -2
  233. package/dist/op/op-verb-class.d.ts.map +1 -1
  234. package/dist/op/operator.d.ts +30 -5
  235. package/dist/op/operator.d.ts.map +1 -1
  236. package/dist/op/receipt-store.d.ts +1 -1
  237. package/dist/op/receipt-store.d.ts.map +1 -1
  238. package/dist/op/resource.d.ts +15 -1
  239. package/dist/op/resource.d.ts.map +1 -1
  240. package/dist/op/runtime.d.ts +200 -0
  241. package/dist/op/runtime.d.ts.map +1 -0
  242. package/dist/op/runtimes/local.d.ts +29 -0
  243. package/dist/op/runtimes/local.d.ts.map +1 -0
  244. package/dist/op/step-output-ref.d.ts +15 -13
  245. package/dist/op/step-output-ref.d.ts.map +1 -1
  246. package/dist/op/types.d.ts +79 -27
  247. package/dist/op/types.d.ts.map +1 -1
  248. package/dist/ownership.d.ts +1 -1
  249. package/dist/provenance.d.ts +21 -0
  250. package/dist/provenance.d.ts.map +1 -1
  251. package/dist/serializer-walker.d.ts.map +1 -1
  252. package/dist/stack-output.d.ts +5 -0
  253. package/dist/stack-output.d.ts.map +1 -1
  254. package/package.json +6 -1
  255. package/src/audit/catalog.test.ts +79 -3
  256. package/src/audit/catalog.ts +111 -13
  257. package/src/audit/core.test.ts +26 -0
  258. package/src/audit/core.ts +61 -17
  259. package/src/audit/discover.test.ts +39 -0
  260. package/src/audit/discover.ts +91 -15
  261. package/src/audit/lineage.ts +100 -0
  262. package/src/audit/prior-art.ts +153 -0
  263. package/src/audit/proof.test.ts +67 -0
  264. package/src/audit/proof.ts +70 -0
  265. package/src/audit/report-html.ts +9 -4
  266. package/src/audit/report-model.ts +16 -2
  267. package/src/audit/report.test.ts +33 -0
  268. package/src/audit/report.ts +5 -3
  269. package/src/audit/rules-doc.test.ts +34 -2
  270. package/src/audit/rules-doc.ts +82 -9
  271. package/src/audit/secrets.ts +15 -3
  272. package/src/audit/terraform-state.test.ts +144 -0
  273. package/src/audit/terraform-state.ts +131 -0
  274. package/src/build.ts +31 -1
  275. package/src/claimed-fields.test.ts +145 -0
  276. package/src/claimed-fields.ts +208 -0
  277. package/src/cli/build-params-cli.ts +2 -2
  278. package/src/cli/command-group.ts +14 -0
  279. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  280. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  281. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  282. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  283. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  284. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  285. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  286. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  287. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  288. package/src/cli/commands/audit.test.ts +189 -11
  289. package/src/cli/commands/audit.ts +73 -27
  290. package/src/cli/commands/build.test.ts +292 -9
  291. package/src/cli/commands/build.ts +156 -24
  292. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  293. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  294. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  295. package/src/cli/commands/import.ts +6 -0
  296. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  297. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  298. package/src/cli/commands/init-lexicon.test.ts +97 -2
  299. package/src/cli/commands/init-lexicon.ts +4 -2
  300. package/src/cli/commands/init.test.ts +35 -1
  301. package/src/cli/commands/init.ts +14 -3
  302. package/src/cli/commands/lint.test.ts +48 -0
  303. package/src/cli/commands/lint.ts +123 -0
  304. package/src/cli/commands/onboard.test.ts +75 -0
  305. package/src/cli/commands/onboard.ts +54 -23
  306. package/src/cli/handlers/build.test.ts +81 -1
  307. package/src/cli/handlers/build.ts +15 -1
  308. package/src/cli/handlers/graph.test.ts +3 -3
  309. package/src/cli/handlers/lifecycle.test.ts +195 -12
  310. package/src/cli/handlers/lifecycle.ts +123 -6
  311. package/src/cli/handlers/misc.ts +1 -1
  312. package/src/cli/handlers/operator.test.ts +183 -11
  313. package/src/cli/handlers/operator.ts +260 -55
  314. package/src/cli/handlers/run.test.ts +431 -1287
  315. package/src/cli/handlers/run.ts +365 -915
  316. package/src/cli/main.test.ts +23 -15
  317. package/src/cli/main.ts +88 -33
  318. package/src/cli/mcp/op-tools.ts +147 -88
  319. package/src/cli/mcp/resource-handlers.ts +10 -37
  320. package/src/cli/mcp/resources/context.ts +11 -12
  321. package/src/cli/mcp/server.test.ts +25 -27
  322. package/src/cli/mcp/server.ts +2 -2
  323. package/src/cli/registry.ts +26 -9
  324. package/src/codegen/docs.ts +1 -1
  325. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  326. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  327. package/src/components/README.md +3 -2
  328. package/src/components/auto-release.test.ts +4 -20
  329. package/src/components/auto-release.ts +17 -42
  330. package/src/components/capability.ts +4 -4
  331. package/src/components/cli-support.test.ts +10 -5
  332. package/src/components/cli-support.ts +52 -44
  333. package/src/components/component.schema.json +2 -2
  334. package/src/components/component.ts +3 -3
  335. package/src/components/driver-output.ts +14 -2
  336. package/src/components/driver.test.ts +92 -22
  337. package/src/components/driver.ts +193 -83
  338. package/src/components/index.ts +1 -6
  339. package/src/components/manifest-persistence.test.ts +2 -20
  340. package/src/components/manifest-persistence.ts +4 -24
  341. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  342. package/src/components/run-progress.ts +6 -6
  343. package/src/components/verbs/run-agent.ts +11 -17
  344. package/src/components/verbs/verify.ts +2 -2
  345. package/src/deep-observation.test.ts +75 -0
  346. package/src/deep-observation.ts +48 -0
  347. package/src/discovery/collect.test.ts +3 -3
  348. package/src/discovery/files.ts +4 -4
  349. package/src/discovery/fold-import.ts +105 -2
  350. package/src/discovery/param-deps.test.ts +112 -1
  351. package/src/discovery/param-deps.ts +193 -0
  352. package/src/discovery/sandbox/bundle.ts +15 -1
  353. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  354. package/src/discovery/sandbox/config-wire.ts +2 -2
  355. package/src/fold/fold.test.ts +56 -0
  356. package/src/fold/foldable-helpers.ts +81 -1
  357. package/src/fold-provenance-exclusion.test.ts +186 -0
  358. package/src/fold-provenance.test.ts +495 -0
  359. package/src/fold-provenance.ts +441 -0
  360. package/src/graph-ops.test.ts +2 -2
  361. package/src/graph-ops.ts +1 -1
  362. package/src/held-elsewhere.test.ts +102 -0
  363. package/src/held-elsewhere.ts +114 -0
  364. package/src/import/ir-utils.ts +11 -1
  365. package/src/import/parser.ts +35 -0
  366. package/src/index.ts +3 -0
  367. package/src/kubectl-context.ts +3 -3
  368. package/src/lexicon-config.test.ts +5 -5
  369. package/src/lexicon-config.ts +4 -4
  370. package/src/lexicon-plugin-helpers.ts +1 -1
  371. package/src/lexicon.ts +120 -14
  372. package/src/lifecycle/change-set.test.ts +49 -0
  373. package/src/lifecycle/change-set.ts +67 -0
  374. package/src/lifecycle/converge-ledger.ts +1 -1
  375. package/src/lifecycle/deep-diff.test.ts +267 -14
  376. package/src/lifecycle/deep-diff.ts +217 -14
  377. package/src/lifecycle/deep-observe.test.ts +3 -1
  378. package/src/lifecycle/deep-observe.ts +16 -6
  379. package/src/lifecycle/gate-ledger.test.ts +74 -2
  380. package/src/lifecycle/gate-ledger.ts +143 -36
  381. package/src/lifecycle/index.ts +1 -0
  382. package/src/lifecycle/release-ledger.ts +2 -2
  383. package/src/lifecycle/run-ledger.test.ts +268 -0
  384. package/src/lifecycle/run-ledger.ts +201 -0
  385. package/src/lifecycle/symptoms.ts +3 -3
  386. package/src/lint/config.test.ts +82 -1
  387. package/src/lint/config.ts +112 -3
  388. package/src/lint/policy.test.ts +1 -1
  389. package/src/lint/post-synth.test.ts +31 -0
  390. package/src/lint/post-synth.ts +54 -1
  391. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  392. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  393. package/src/lint/rules/comp/comp.test.ts +1 -1
  394. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
  395. package/src/lint/rules/comp/index.ts +3 -3
  396. package/src/lint/rules/op/index.ts +23 -0
  397. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  398. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  399. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  400. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  401. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  402. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  403. package/src/lint/rules/op/support.ts +11 -0
  404. package/src/lint/suppressions.test.ts +163 -0
  405. package/src/lint/suppressions.ts +204 -0
  406. package/src/live-endpoint.ts +1 -1
  407. package/src/op/activities/activity-contracts.ts +66 -0
  408. package/src/op/activities/apply.test.ts +567 -0
  409. package/src/op/activities/apply.ts +662 -0
  410. package/src/op/activities/build.ts +26 -0
  411. package/src/op/activities/converge.test.ts +330 -0
  412. package/src/op/activities/converge.ts +449 -0
  413. package/src/op/activities/env-teardown.test.ts +187 -0
  414. package/src/op/activities/env-teardown.ts +171 -0
  415. package/src/op/activities/guard-validate.test.ts +139 -0
  416. package/src/op/activities/guard-validate.ts +223 -0
  417. package/src/op/activities/http-check.test.ts +48 -0
  418. package/src/op/activities/http-check.ts +55 -0
  419. package/src/op/activities/index.ts +119 -0
  420. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  421. package/src/op/activities/lexicon-upgrade.ts +673 -0
  422. package/src/op/activities/lifecycle.ts +85 -0
  423. package/src/op/activities/pipeline-audit.test.ts +67 -0
  424. package/src/op/activities/pipeline-audit.ts +201 -0
  425. package/src/op/activities/policy.test.ts +19 -0
  426. package/src/op/activities/policy.ts +37 -0
  427. package/src/op/activities/reconcile.test.ts +81 -0
  428. package/src/op/activities/reconcile.ts +187 -0
  429. package/src/op/activities/shell.ts +26 -0
  430. package/src/op/activities/teardown.ts +22 -0
  431. package/src/op/activities/util.ts +6 -0
  432. package/src/op/activities/wait.ts +69 -0
  433. package/src/op/activities/workflow-audit.test.ts +90 -0
  434. package/src/op/activities/workflow-audit.ts +203 -0
  435. package/src/op/activity-contract-registry.test.ts +98 -0
  436. package/src/op/activity-contract-registry.ts +114 -0
  437. package/src/op/activity-contract.test.ts +1 -1
  438. package/src/op/activity-contract.ts +37 -33
  439. package/src/op/activity-failure.ts +34 -0
  440. package/src/op/activity-profiles.test.ts +56 -0
  441. package/src/op/activity-profiles.ts +116 -0
  442. package/src/op/activity-registry.test.ts +37 -28
  443. package/src/op/activity-registry.ts +27 -56
  444. package/src/op/activity-runtime.ts +3 -47
  445. package/src/op/builders-exports.test.ts +0 -49
  446. package/src/op/builders-typed.test.ts +164 -0
  447. package/src/op/builders.ts +123 -39
  448. package/src/op/composites/apply-op.ts +208 -0
  449. package/src/op/composites/composites.test.ts +366 -0
  450. package/src/op/composites/converge-op.test.ts +168 -0
  451. package/src/op/composites/converge-op.ts +214 -0
  452. package/src/op/composites/index.ts +26 -0
  453. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  454. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  455. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  456. package/src/op/composites/pipeline-audit-op.ts +78 -0
  457. package/src/op/composites/reconcile-op.test.ts +51 -0
  458. package/src/op/composites/reconcile-op.ts +107 -0
  459. package/src/op/composites/watch-op.ts +108 -0
  460. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  461. package/src/op/composites/workflow-audit-op.ts +88 -0
  462. package/src/op/converge-rule.ts +12 -10
  463. package/src/op/cron.test.ts +128 -0
  464. package/src/op/cron.ts +149 -0
  465. package/src/op/discover-root.test.ts +105 -1
  466. package/src/op/discover.test.ts +6 -2
  467. package/src/op/discover.ts +84 -16
  468. package/src/op/duration.ts +21 -0
  469. package/src/op/effect-step.test.ts +26 -11
  470. package/src/op/emulator-lifecycle.ts +1 -2
  471. package/src/op/gate.ts +155 -0
  472. package/src/op/generate-pipeline.test.ts +65 -3
  473. package/src/op/generate-pipeline.ts +19 -2
  474. package/src/op/index.ts +39 -6
  475. package/src/op/local-executor.test.ts +163 -34
  476. package/src/op/local-executor.ts +370 -102
  477. package/src/op/local-output.test.ts +95 -11
  478. package/src/op/local-output.ts +33 -4
  479. package/src/op/op-ir.test.ts +281 -0
  480. package/src/op/op-ir.ts +417 -0
  481. package/src/op/op-verb-class.ts +19 -5
  482. package/src/op/op.test.ts +26 -5
  483. package/src/op/operator.test.ts +175 -6
  484. package/src/op/operator.ts +63 -16
  485. package/src/op/receipt-store.ts +1 -1
  486. package/src/op/resource.ts +20 -2
  487. package/src/op/runtime.ts +203 -0
  488. package/src/op/runtimes/local.test.ts +181 -0
  489. package/src/op/runtimes/local.ts +212 -0
  490. package/src/op/step-output-ref.ts +15 -13
  491. package/src/op/types.ts +84 -24
  492. package/src/ownership.ts +1 -1
  493. package/src/provenance.test.ts +6 -0
  494. package/src/provenance.ts +21 -1
  495. package/src/serializer-walker.test.ts +42 -0
  496. package/src/serializer-walker.ts +21 -2
  497. package/src/stack-output.ts +6 -1
  498. package/dist/cli/handlers/op-progress.d.ts +0 -90
  499. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  500. package/dist/cli/handlers/run-client.d.ts +0 -127
  501. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  502. package/dist/cli/handlers/run-report.d.ts +0 -5
  503. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  504. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  505. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  506. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  507. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  508. package/src/cli/handlers/op-progress.test.ts +0 -268
  509. package/src/cli/handlers/op-progress.ts +0 -248
  510. package/src/cli/handlers/run-client.test.ts +0 -82
  511. package/src/cli/handlers/run-client.ts +0 -219
  512. package/src/cli/handlers/run-report.test.ts +0 -62
  513. package/src/cli/handlers/run-report.ts +0 -122
  514. package/src/components/temporal-codegen-loader.ts +0 -41
  515. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -7,12 +7,13 @@
7
7
 
8
8
  import { existsSync, readFileSync, statSync, writeFileSync } from "fs";
9
9
  import { join } from "path";
10
- import { auditFiles, type AuditInput, type AuditFinding, type ChecksProvider } from "../../audit/core";
10
+ import { auditFiles, type AuditInput, type AuditFinding, type ChecksProvider, type SuppressionStats } from "../../audit/core";
11
11
  import { AUDIT_LEXICONS, classifyFiles, collectCandidates, loadAuditPlugins, unclaimedFiles, type DetectPlugin, type RepoFile, type UnclaimedFile } from "../../audit/discover";
12
12
  import { RULE_CATALOG, resolveAuditCatalog, type RuleMeta } from "../../audit/catalog";
13
13
  import { scanForSecrets, parseSecretsConfig, type SecretsScanOptions } from "../../audit/secrets";
14
14
  import { auditWranglerConfigs } from "../../audit/wrangler";
15
15
  import { auditNginxConfigs } from "../../audit/nginx";
16
+ import { auditTerraformState } from "../../audit/terraform-state";
16
17
  import { renderMarkdown } from "../../audit/report";
17
18
  import { renderHtml, type ReportTheme } from "../../audit/report-html";
18
19
  import { buildReportJson, REPORT_SCHEMA_VERSION, type AuditSnapshot } from "../../audit/report-model";
@@ -23,7 +24,17 @@ import type { Severity } from "../../lint/rule";
23
24
 
24
25
  export type AuditFormat = "stylish" | "json" | "sarif" | "markdown" | "html";
25
26
  export type AuditTier = "merge-worthy" | "all";
26
- export type AuditFailOn = "merge-worthy" | "warning" | "none";
27
+ /**
28
+ * `"error"` (chant #2113) is the minimum-failure-severity floor tflint's
29
+ * `--minimum-failure-severity` maps onto (`error | warning | notice`): unlike
30
+ * `"merge-worthy"`/`"warning"` above (which key off a rule's catalog `tier`
31
+ * or a finding's severity being at-or-above warning), `"error"` fails only on
32
+ * an `error`-severity finding, decoupling what CI fails a build over from
33
+ * what a rule ships reporting by default. It's the lever that lets a new
34
+ * rule ship at `warning` and be promoted to `error` later without a pipeline
35
+ * pinned to `--fail-on error` breaking the moment the rule starts firing.
36
+ */
37
+ export type AuditFailOn = "merge-worthy" | "warning" | "error" | "none";
27
38
 
28
39
  export interface AuditCommandOptions {
29
40
  /** Repo root/dir to scan, or an https:// repo URL to fetch and audit. */
@@ -79,6 +90,12 @@ export interface AuditCommandResult {
79
90
  unclaimed?: UnclaimedFile[];
80
91
  /** Where `output` belongs; diagnostics go to stderr, reports to stdout (default). */
81
92
  stream?: "stdout" | "stderr";
93
+ /**
94
+ * Findings an inline `# chant-ignore*` comment suppressed (chant #2111),
95
+ * counted, never dropped silently, mirroring `chant build`'s equivalent
96
+ * summary line.
97
+ */
98
+ suppressedCount?: number;
82
99
  }
83
100
 
84
101
  /** Exit code when the audit had no lexicons to look with. Distinct from 1 (findings / failure). */
@@ -89,9 +106,9 @@ function lexiconPackage(name: string): string {
89
106
  return `@intentius/chant-lexicon-${name}`;
90
107
  }
91
108
 
92
- /** Missing audit lexicons the unclaimed files pointed at, in first-seen order. `terraform` is not installable. */
109
+ /** Missing audit lexicons the unclaimed files pointed at, in first-seen order. */
93
110
  function wantedLexicons(unclaimed: UnclaimedFile[]): string[] {
94
- return [...new Set(unclaimed.map((u) => u.lexicon))].filter((l) => l !== "terraform");
111
+ return [...new Set(unclaimed.map((u) => u.lexicon))];
95
112
  }
96
113
 
97
114
  /**
@@ -107,17 +124,10 @@ export function installLine(lexicons: string[], target: string): string {
107
124
  /** One-line coverage hint for the partial case: some lexicons loaded, others wanted by files on disk. */
108
125
  function missingLexiconHint(unclaimed: UnclaimedFile[]): string | undefined {
109
126
  const wanted = wantedLexicons(unclaimed);
110
- const tf = unclaimed.filter((u) => u.lexicon === "terraform").length;
111
- const parts: string[] = [];
112
- if (wanted.length > 0) {
113
- const n = unclaimed.length - tf;
114
- parts.push(
115
- `${n} file${n === 1 ? " looks" : "s look"} like ${wanted.join("/")} but ${wanted.length === 1 ? "that lexicon is" : "those lexicons are"} not installed, so ${n === 1 ? "it was" : "they were"} skipped` +
116
- ` (npm i ${wanted.map(lexiconPackage).join(" ")}).`,
117
- );
118
- }
119
- if (tf > 0) parts.push(`${tf} Terraform file${tf === 1 ? "" : "s"} skipped; the audit does not read HCL (see chant carve).`);
120
- return parts.length > 0 ? parts.join(" ") : undefined;
127
+ if (wanted.length === 0) return undefined;
128
+ const n = unclaimed.length;
129
+ return `${n} file${n === 1 ? " looks" : "s look"} like ${wanted.join("/")} but ${wanted.length === 1 ? "that lexicon is" : "those lexicons are"} not installed, so ${n === 1 ? "it was" : "they were"} skipped` +
130
+ ` (npm i ${wanted.map(lexiconPackage).join(" ")}).`;
121
131
  }
122
132
 
123
133
  /** Human-readable diagnostic for the zero-lexicon case. */
@@ -129,11 +139,10 @@ function renderNoLexicons(target: string, unclaimed: UnclaimedFile[]): string {
129
139
  if (unclaimed.length > 0) {
130
140
  lines.push("", "Files that wanted a lexicon:");
131
141
  for (const u of unclaimed) {
132
- const note = u.lexicon === "terraform" ? "terraform (not audited; see chant carve)" : u.lexicon;
133
- lines.push(` ${u.path} -> ${note}`);
142
+ lines.push(` ${u.path} -> ${u.lexicon}`);
134
143
  }
135
144
  } else {
136
- lines.push("", "No file under the target looked like CI, Kubernetes, Helm, Docker, CloudFormation, ARM, Config Connector, or fountain either.");
145
+ lines.push("", "No file under the target looked like CI, Kubernetes, Helm, Docker, CloudFormation, ARM, Config Connector, fountain, or Terraform either.");
137
146
  }
138
147
  lines.push("", "Run it with the lexicons those files need:");
139
148
  lines.push(` ${installLine(wanted.length > 0 ? wanted : [...AUDIT_LEXICONS], target)}`);
@@ -197,6 +206,7 @@ function exitCodeFor(findings: AuditFinding[], failOn: AuditFailOn, catalog: Rec
197
206
  if (failOn === "warning") {
198
207
  return findings.some((f) => f.severity === "error" || f.severity === "warning") ? 1 : 0;
199
208
  }
209
+ if (failOn === "error") return findings.some((f) => f.severity === "error") ? 1 : 0;
200
210
  return 0;
201
211
  }
202
212
 
@@ -252,7 +262,15 @@ function renderStylish(findings: AuditFinding[], scanned: string[], notes: strin
252
262
  lines.push("", title);
253
263
  for (const f of list) {
254
264
  const loc = f.line ? `${f.file}:${f.line}` : f.file;
255
- const where = f.entity ? `${loc} (${f.entity})` : loc;
265
+ // A `missing` finding (chant #2113) has nothing present to anchor
266
+ // `entity` to. The whole point is that `f.missing.kind` does not
267
+ // exist in `f.missing.scope`, so it renders that absence directly
268
+ // instead of falling back to `entity` (which such a check rarely sets).
269
+ const where = f.missing
270
+ ? `${loc} (missing ${f.missing.kind} in ${f.missing.scope})`
271
+ : f.entity
272
+ ? `${loc} (${f.entity})`
273
+ : loc;
256
274
  const title = catalog[f.checkId]?.title ?? f.checkId;
257
275
  lines.push(` [${f.checkId}] ${f.severity} ${where} — ${title}`);
258
276
  }
@@ -280,11 +298,21 @@ function renderSarif(findings: AuditFinding[], catalog: Record<string, RuleMeta>
280
298
  shortDescription: { text: m?.title ?? id },
281
299
  ...(m?.remediation ? { help: { text: m.remediation } } : {}),
282
300
  helpUri: m?.authority?.[0]?.url,
283
- ...(m?.category ? { properties: { category: m.category, dimension: m.category } } : {}),
301
+ // `priorArt` credits the upstream tools whose rules check the same thing
302
+ // (audit/prior-art.ts) — tool key, upstream rule id, doc URL, relation.
303
+ ...(m?.category || m?.lineage?.length
304
+ ? {
305
+ properties: {
306
+ ...(m?.category ? { category: m.category, dimension: m.category } : {}),
307
+ ...(m?.lineage?.length ? { priorArt: m.lineage } : {}),
308
+ },
309
+ }
310
+ : {}),
284
311
  };
285
312
  });
286
313
  const results = findings.map((f) => {
287
314
  const tier = catalog[f.checkId]?.tier;
315
+ const properties = { ...(tier ? { tier } : {}), ...(f.missing ? { missing: f.missing } : {}) };
288
316
  return {
289
317
  ruleId: f.checkId,
290
318
  level: sarifLevel(f.severity),
@@ -297,7 +325,12 @@ function renderSarif(findings: AuditFinding[], catalog: Record<string, RuleMeta>
297
325
  },
298
326
  },
299
327
  ],
300
- ...(tier ? { properties: { tier } } : {}),
328
+ // `f.missing` (chant #2113) is Snyk policy-engine's "missing-resource"
329
+ // archetype: nothing at `f.file` actually declares the absent thing, so
330
+ // there is no line to point SARIF's region at either. `missing` rides
331
+ // in the property bag (SARIF's core schema has no dedicated slot for an
332
+ // absence finding) rather than pretending a region exists.
333
+ ...(Object.keys(properties).length > 0 ? { properties } : {}),
301
334
  };
302
335
  });
303
336
  return JSON.stringify(
@@ -395,29 +428,37 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
395
428
  // its own path detector plus a parsed-content marker check (a `.conf` in a
396
429
  // shared directory name like conf.d/ only counts once it parses as nginx).
397
430
  const nginxFindings = auditNginxConfigs(candidates);
431
+ // TF023 (#2110) — committed Terraform state. Lexicon-independent for the
432
+ // same reason the three above are: it reads the discovered paths, not a
433
+ // parsed root module, so it fires with or without the terraform lexicon.
434
+ const terraformStateFindings = auditTerraformState(candidates);
398
435
 
399
436
  if (plugins.length === 0) {
400
437
  const output = format === "json" ? renderNoLexiconsJson(options.path, unclaimed) : renderNoLexicons(options.path, unclaimed);
401
- return { success: true, status: "no-lexicons", output, findings: [...secretsFindings, ...wranglerFindings, ...nginxFindings], scanned: [], unclaimed, exitCode: NO_LEXICONS_EXIT_CODE, stream: format === "json" ? "stdout" : "stderr" };
438
+ return { success: true, status: "no-lexicons", output, findings: [...secretsFindings, ...wranglerFindings, ...nginxFindings, ...terraformStateFindings], scanned: [], unclaimed, exitCode: NO_LEXICONS_EXIT_CODE, stream: format === "json" ? "stdout" : "stderr" };
402
439
  }
403
440
 
404
441
  const missingLexiconNote = missingLexiconHint(unclaimed);
405
442
 
406
- if (inputs.length === 0 && secretsFindings.length === 0 && wranglerFindings.length === 0 && nginxFindings.length === 0) {
443
+ if (inputs.length === 0 && secretsFindings.length === 0 && wranglerFindings.length === 0 && nginxFindings.length === 0 && terraformStateFindings.length === 0) {
407
444
  const output = `No auditable files found under ${options.path}.${missingLexiconNote ? ` ${missingLexiconNote}` : ""}`;
408
445
  return { success: true, status: "ok", output, findings: [], scanned: [], unclaimed, exitCode: 0 };
409
446
  }
410
447
 
411
448
  let findings: AuditFinding[] = [];
449
+ // chant #2111: mutated in place by `auditFiles` so its inline (`# chant-
450
+ // ignore*`) suppressed count reaches the summary without changing
451
+ // `auditFiles`'s `AuditFinding[]` return type; see `SuppressionStats`'s doc.
452
+ const suppressionStats: SuppressionStats = { count: 0 };
412
453
  if (inputs.length > 0) {
413
454
  try {
414
- findings = await auditFiles(inputs, { checksProvider: options.checksProvider });
455
+ findings = await auditFiles(inputs, { checksProvider: options.checksProvider, suppressionStats });
415
456
  } catch (err) {
416
457
  const msg = err instanceof Error ? err.message : String(err);
417
458
  return { success: false, output: "", findings: [], scanned, exitCode: 1, error: msg };
418
459
  }
419
460
  }
420
- findings = [...findings, ...secretsFindings, ...wranglerFindings, ...nginxFindings];
461
+ findings = [...findings, ...secretsFindings, ...wranglerFindings, ...nginxFindings, ...terraformStateFindings];
421
462
  // Resolve the audit catalog once, aggregating the audited lexicons' own
422
463
  // metadata over core's static catalog (#687). The lexicons are already loaded
423
464
  // by `auditFiles` above, so this is cheap. Core's static catalog (always
@@ -428,6 +469,11 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
428
469
  if (tier === "merge-worthy") findings = findings.filter((f) => isMergeWorthy(f, catalog));
429
470
  const notes = coverageNotes(inputs);
430
471
  if (missingLexiconNote) notes.push(missingLexiconNote);
472
+ if (suppressionStats.count > 0) {
473
+ notes.push(
474
+ `${suppressionStats.count} finding${suppressionStats.count === 1 ? "" : "s"} suppressed via inline chant-ignore comments.`,
475
+ );
476
+ }
431
477
 
432
478
  // Diff-bearing renderers (markdown, html) need action SHAs / image digests
433
479
  // resolved up front (sync maps so rendering stays synchronous).
@@ -495,10 +541,10 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
495
541
  } catch (err) {
496
542
  return { success: false, output, findings, scanned, exitCode: 1, error: `Failed to write ${options.output}: ${err instanceof Error ? err.message : String(err)}` };
497
543
  }
498
- return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, wroteTo: options.output };
544
+ return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, wroteTo: options.output, suppressedCount: suppressionStats.count };
499
545
  }
500
546
 
501
- return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode };
547
+ return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, suppressedCount: suppressionStats.count };
502
548
  }
503
549
 
504
550
  /** Print an audit result to stdout. */
@@ -950,19 +950,21 @@ export const x = { [Symbol.for("chant.declarable")]: true, entityType: "X", lexi
950
950
  });
951
951
  });
952
952
 
953
- test("op worker files go to <project>/dist/ops even with no --output (#878)", async () => {
954
- // The generated Op worker must land where `chant run <op> --temporal` reads it
955
- // (`<project>/dist/ops/<name>/worker.ts`) even when the build has no --output
956
- // (a Temporal Op project often has no primary resource manifest to route).
953
+ test("serializer files under ops/ go to <project>/dist/ops even with no --output (#878)", async () => {
954
+ // Whatever a serializer emits under `ops/` must land in
955
+ // `<project>/dist/ops/<name>/`, beside the `op.json` core writes there,
956
+ // even when the build has no --output (an Op-only project often has no
957
+ // primary resource manifest to route). The routing is on the `ops/`
958
+ // prefix alone — it does not know or care what the files are.
957
959
  const opSerializer: Serializer = {
958
960
  name: "multi",
959
961
  rulePrefix: "MULTI",
960
962
  serialize: () => ({
961
963
  primary: "{}",
962
964
  files: {
963
- "ops/durable-hello/workflow.ts": "// workflow\n",
964
- "ops/durable-hello/worker.ts": "// worker\n",
965
- "ops/durable-hello/activities.ts": "// activities\n",
965
+ "ops/nightly-report/steps.json": "{}\n",
966
+ "ops/nightly-report/notes.md": "# notes\n",
967
+ "ops/nightly-report/nested/extra.txt": "extra\n",
966
968
  },
967
969
  }),
968
970
  };
@@ -979,8 +981,31 @@ export const x = { [Symbol.for("chant.declarable")]: true, entityType: "X", lexi
979
981
  } as BuildOptions);
980
982
 
981
983
  expect(result.errors).toEqual([]);
982
- expect(existsSync(join(testDir, "dist", "ops", "durable-hello", "worker.ts"))).toBe(true);
983
- expect(existsSync(join(testDir, "dist", "ops", "durable-hello", "workflow.ts"))).toBe(true);
984
+ expect(existsSync(join(testDir, "dist", "ops", "nightly-report", "steps.json"))).toBe(true);
985
+ expect(existsSync(join(testDir, "dist", "ops", "nightly-report", "notes.md"))).toBe(true);
986
+ expect(existsSync(join(testDir, "dist", "ops", "nightly-report", "nested", "extra.txt"))).toBe(true);
987
+ });
988
+
989
+ test("an Op entity emits dist/ops/<name>/op.json from core, with no serializer involved (#2118)", async () => {
990
+ await writeFile(
991
+ join(testDir, "deploy.op.ts"),
992
+ `export default { [Symbol.for("chant.declarable")]: true, entityType: "Chant::Op", lexicon: "chant", kind: "resource", attributes: {},` +
993
+ ` props: { name: "deploy", overview: "Deploy the thing", labels: { Env: "staging" },` +
994
+ ` phases: [{ name: "Apply", steps: [{ kind: "activity", fn: "shellCmd", args: { cmd: "true" } }] }] } };`,
995
+ );
996
+
997
+ const result = await buildCommand({ path: testDir, format: "json", serializers: [] } as BuildOptions);
998
+
999
+ expect(result.errors).toEqual([]);
1000
+ const ir = JSON.parse(readFileSync(join(testDir, "dist", "ops", "deploy", "op.json"), "utf-8"));
1001
+ expect(ir.formatVersion).toBe("2.0");
1002
+ expect(ir.name).toBe("deploy");
1003
+ expect(ir.labels).toEqual({ Env: "staging" });
1004
+ expect(ir).not.toHaveProperty("taskQueue");
1005
+ expect(ir.phases[0].steps[0]).toMatchObject({ kind: "activity", fn: "shellCmd", profile: "fastIdempotent" });
1006
+ // Ops are held out of `partitionByLexicon` (they have no serializer and
1007
+ // need none), so core's own `chant` lexicon never trips the warning.
1008
+ expect(result.warnings.filter((w) => w.includes("No serializer"))).toEqual([]);
984
1009
  });
985
1010
 
986
1011
  // ── #284 bug 2: array-of-objects must serialize to valid YAML ──────────
@@ -1219,6 +1244,264 @@ export const x = { [Symbol.for("chant.declarable")]: true, entityType: "X", lexi
1219
1244
  30_000,
1220
1245
  );
1221
1246
  });
1247
+
1248
+ describe("post-synth findings honor inline chant-ignore comments (chant #2111)", () => {
1249
+ /**
1250
+ * A discovered entity carrying a `suppressions` directive by hand, the
1251
+ * generic, duck-typed field `applyInlineSuppressions` reads (see
1252
+ * `../../lint/suppressions.ts`'s module doc). The terraform lexicon
1253
+ * populates this from real `# chant-ignore*` HCL comments
1254
+ * (`lexicons/terraform/src/hcl/suppressions.ts`); this test proves the
1255
+ * core wiring, that `chant build` applies it to ANY lexicon's entities,
1256
+ * without needing a real HCL parse.
1257
+ */
1258
+ async function writeSuppressibleEntity(ids: string): Promise<void> {
1259
+ await writeFile(
1260
+ join(testDir, "main.ts"),
1261
+ `export const e = {\n` +
1262
+ ` lexicon: "test",\n` +
1263
+ ` entityType: "TestEntity",\n` +
1264
+ ` [Symbol.for("chant.declarable")]: true,\n` +
1265
+ ` suppressions: [{ form: "chant-ignore", ids: new Set(${JSON.stringify(ids.split(","))}), file: "main.ts", line: 1, key: "main.ts:1:chant-ignore" }],\n` +
1266
+ `};\n`,
1267
+ );
1268
+ }
1269
+
1270
+ /** Same shape as `fakePostSynthPlugin` above, except its check attaches `entity` to whatever entity the build discovered, so a suppression directive on that entity has something to match against. */
1271
+ function fakeEntitySuppressionPlugin(checkId: string, severity: "error" | "warning" | "info"): LexiconPlugin {
1272
+ return {
1273
+ name: "fake",
1274
+ serializer: { name: "fake", rulePrefix: "FAKE", serialize: () => "{}" },
1275
+ generate: async () => {},
1276
+ validate: async () => {},
1277
+ coverage: async () => {},
1278
+ package: async () => {},
1279
+ postSynthChecks: () => [
1280
+ {
1281
+ id: checkId,
1282
+ description: "test check",
1283
+ check: (ctx) => {
1284
+ const [key] = [...ctx.entities.keys()];
1285
+ return key ? [{ checkId, severity, message: `${checkId} triggered`, entity: key }] : [];
1286
+ },
1287
+ },
1288
+ ],
1289
+ };
1290
+ }
1291
+
1292
+ test("an inline chant-ignore naming the finding's checkId suppresses it and reports a suppressed count", async () => {
1293
+ await writeSuppressibleEntity("FAKE005");
1294
+ await writeFile(join(testDir, "chant.config.ts"), `export default {};\n`);
1295
+
1296
+ const result = await buildCommand({
1297
+ path: testDir,
1298
+ format: "json",
1299
+ serializers: [mockSerializer],
1300
+ plugins: [fakeEntitySuppressionPlugin("FAKE005", "error")],
1301
+ });
1302
+
1303
+ expect(result.success).toBe(true);
1304
+ expect(result.errors).toEqual([]);
1305
+ expect(result.warnings.some((w) => w.includes("1 post-synth finding(s) suppressed via inline chant-ignore comments"))).toBe(true);
1306
+ });
1307
+
1308
+ test("an inline chant-ignore naming a different id has no effect", async () => {
1309
+ await writeSuppressibleEntity("FAKE006");
1310
+ await writeFile(join(testDir, "chant.config.ts"), `export default {};\n`);
1311
+
1312
+ const result = await buildCommand({
1313
+ path: testDir,
1314
+ format: "json",
1315
+ serializers: [mockSerializer],
1316
+ plugins: [fakeEntitySuppressionPlugin("FAKE007", "error")],
1317
+ });
1318
+
1319
+ expect(result.success).toBe(false);
1320
+ expect(result.errors.some((e) => e.includes("FAKE007 triggered"))).toBe(true);
1321
+ expect(result.warnings.some((w) => w.includes("suppressed"))).toBe(false);
1322
+ });
1323
+
1324
+ test("ignorable: false in lint.rules denies the ignore and reports the attempt, keeping the finding active", async () => {
1325
+ await writeSuppressibleEntity("FAKE008");
1326
+ await writeFile(
1327
+ join(testDir, "chant.config.ts"),
1328
+ `export default { lint: { rules: { "FAKE008": ["error", { ignorable: false }] } } };\n`,
1329
+ );
1330
+
1331
+ const result = await buildCommand({
1332
+ path: testDir,
1333
+ format: "json",
1334
+ serializers: [mockSerializer],
1335
+ plugins: [fakeEntitySuppressionPlugin("FAKE008", "error")],
1336
+ });
1337
+
1338
+ expect(result.success).toBe(false);
1339
+ expect(result.errors.some((e) => e.includes("FAKE008 triggered"))).toBe(true);
1340
+ expect(result.warnings.some((w) => w.includes("SUPP003") && w.includes("ignorable: false"))).toBe(true);
1341
+ });
1342
+ });
1343
+
1344
+ /**
1345
+ * chant #2113: `lint.presets` filters WHICH post-synth check ids are
1346
+ * reported at all, ahead of `lint.rules`' severity overrides (proven
1347
+ * above). A plugin with no `lintPresets()` is unaffected (the common case
1348
+ * today); one that ships presets defaults to `recommended` and is
1349
+ * overridden per-lexicon via `lint.presets: { <name>: "<preset>" }`.
1350
+ */
1351
+ describe("post-synth findings honor lint.presets (chant #2113)", () => {
1352
+ async function writeTrivialEntity(): Promise<void> {
1353
+ await writeFile(
1354
+ join(testDir, "main.ts"),
1355
+ `export const e = { lexicon: "test", entityType: "TestEntity", [Symbol.for("chant.declarable")]: true };\n`,
1356
+ );
1357
+ }
1358
+
1359
+ /** A `LexiconPlugin` whose one post-synth check always fires, with `recommended`/`all` presets. `recommended` excludes the check's own id, `all` includes it. */
1360
+ function fakePresetPlugin(): LexiconPlugin {
1361
+ return {
1362
+ name: "fakepreset",
1363
+ serializer: { name: "fakepreset", rulePrefix: "FP", serialize: () => "{}" },
1364
+ generate: async () => {},
1365
+ validate: async () => {},
1366
+ coverage: async () => {},
1367
+ package: async () => {},
1368
+ postSynthChecks: () => [
1369
+ {
1370
+ id: "FP001",
1371
+ description: "a report-only check, always fires",
1372
+ check: () => [{ checkId: "FP001", severity: "warning" as const, message: "FP001 triggered" }],
1373
+ },
1374
+ ],
1375
+ lintPresets: () => ({ recommended: [], all: ["FP001"] }),
1376
+ };
1377
+ }
1378
+
1379
+ test("with no lint.presets config, the default preset (recommended) excludes the finding", async () => {
1380
+ await writeTrivialEntity();
1381
+ const result = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [fakePresetPlugin()] });
1382
+ expect(result.warnings.join("\n")).not.toContain("FP001 triggered");
1383
+ expect(result.warnings.some((w) => w.includes("not reported: excluded by the active lint.presets preset"))).toBe(true);
1384
+ });
1385
+
1386
+ test('lint.presets: { fakepreset: "all" } includes the finding', async () => {
1387
+ await writeTrivialEntity();
1388
+ await writeFile(
1389
+ join(testDir, "chant.config.ts"),
1390
+ `export default { lint: { presets: { fakepreset: "all" } } };\n`,
1391
+ );
1392
+ const result = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [fakePresetPlugin()] });
1393
+ expect(result.warnings.join("\n")).toContain("FP001 triggered");
1394
+ });
1395
+
1396
+ test("an explicit lint.rules entry re-enables the rule even under the excluding (recommended) preset", async () => {
1397
+ await writeTrivialEntity();
1398
+ await writeFile(
1399
+ join(testDir, "chant.config.ts"),
1400
+ `export default { lint: { rules: { "FP001": "warning" } } };\n`,
1401
+ );
1402
+ const result = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [fakePresetPlugin()] });
1403
+ expect(result.warnings.join("\n")).toContain("FP001 triggered");
1404
+ });
1405
+ });
1406
+ });
1407
+
1408
+ // #2122 (epic #2114 sub-issue 6) — OPS012/OPS013/OPS014 are core-owned
1409
+ // post-synth checks over the Op model, run unscoped by `coreOpChecks()`
1410
+ // regardless of which plugins/lexicons are configured. This is the
1411
+ // end-to-end path acceptance criterion 3 needs: `chant build` over a real
1412
+ // ConvergeOp-shaped fixture with a destructive gated dispatch rule fails
1413
+ // with the OPS014 refusal, with zero plugins/lexicons loaded.
1414
+ describe("buildCommand — OPS014 (op-model post-synth checks, #2122)", () => {
1415
+ let testDir: string;
1416
+
1417
+ const mockSerializer: Serializer = {
1418
+ name: "test",
1419
+ rulePrefix: "TEST",
1420
+ serialize: (entities) => JSON.stringify({ resources: [...entities.keys()] }, null, 2),
1421
+ };
1422
+
1423
+ beforeEach(async () => {
1424
+ testDir = join(tmpdir(), `chant-ops014-test-${Date.now()}-${Math.random()}`);
1425
+ await mkdir(testDir, { recursive: true });
1426
+ });
1427
+
1428
+ afterEach(async () => {
1429
+ await rm(testDir, { recursive: true, force: true });
1430
+ });
1431
+
1432
+ test("fails build with the OPS014 refusal on a ConvergeOp dispatching a destructive, gated op", async () => {
1433
+ // The destructive dispatch target: a gated `nativeApply` with
1434
+ // `deleteMode: "gated"` — classified destructive by
1435
+ // `../../op/op-verb-class.ts`, gate or not.
1436
+ await writeFile(
1437
+ join(testDir, "prune-staging.op.ts"),
1438
+ `
1439
+ export const pruneStaging = {
1440
+ [Symbol.for("chant.declarable")]: true,
1441
+ entityType: "Chant::Op",
1442
+ lexicon: "chant",
1443
+ kind: "resource",
1444
+ props: {
1445
+ name: "prune-staging",
1446
+ overview: "test",
1447
+ phases: [
1448
+ { name: "Approve", steps: [{ kind: "gate", signalName: "approve-x" }] },
1449
+ { name: "Apply", steps: [
1450
+ { kind: "activity", fn: "nativeApply", args: { target: "kubectl", env: "staging", output: "dist", deleteMode: "gated" } },
1451
+ ] },
1452
+ ],
1453
+ },
1454
+ };
1455
+ `,
1456
+ );
1457
+
1458
+ // The ConvergeOp-shaped Op: dial "apply", one rule dispatching the
1459
+ // destructive target above.
1460
+ await writeFile(
1461
+ join(testDir, "converge.op.ts"),
1462
+ `
1463
+ export const converge = {
1464
+ [Symbol.for("chant.declarable")]: true,
1465
+ entityType: "Chant::Op",
1466
+ lexicon: "chant",
1467
+ kind: "resource",
1468
+ props: {
1469
+ name: "converge",
1470
+ overview: "test",
1471
+ labels: { Converge: "true", Env: "staging", Dial: "apply" },
1472
+ phases: [
1473
+ { name: "Observe", steps: [{ kind: "activity", fn: "lifecycleDiff", args: { env: "staging" }, id: "diff" }] },
1474
+ { name: "Converge", steps: [{ kind: "activity", fn: "convergeTick", args: { rules: [
1475
+ {
1476
+ id: "drift-prune",
1477
+ when: { kind: "field-comparison", field: "status", op: "eq", value: "drifted" },
1478
+ then: { kind: "run", op: "prune-staging" },
1479
+ why: "Prune drifted resources.",
1480
+ },
1481
+ ] } }] },
1482
+ ],
1483
+ },
1484
+ };
1485
+ `,
1486
+ );
1487
+
1488
+ const result = await buildCommand({
1489
+ path: testDir,
1490
+ format: "json",
1491
+ serializers: [mockSerializer],
1492
+ plugins: [],
1493
+ });
1494
+
1495
+ expect(result.success).toBe(false);
1496
+ // build.ts's plain error strings carry the diagnostic's message text, not
1497
+ // its checkId — OPS014's own message names what it refused and why (see
1498
+ // ../../lint/rules/op/ops014-converge-rule-refusals.ts).
1499
+ expect(
1500
+ result.errors.some(
1501
+ (e) => e.includes("destructive") && e.includes("prune-staging") && e.includes("refused in v1"),
1502
+ ),
1503
+ ).toBe(true);
1504
+ });
1222
1505
  });
1223
1506
 
1224
1507
  // ── #284 bug 1: -o extension drives format when --format is absent ────────