@intentius/chant 0.57.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 (504) 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/init-lexicon/templates/codegen.d.ts +1 -0
  37. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
  38. package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
  39. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  40. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  41. package/dist/cli/commands/init.d.ts.map +1 -1
  42. package/dist/cli/commands/lint.d.ts.map +1 -1
  43. package/dist/cli/commands/onboard.d.ts +2 -0
  44. package/dist/cli/commands/onboard.d.ts.map +1 -1
  45. package/dist/cli/handlers/build.d.ts.map +1 -1
  46. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  47. package/dist/cli/handlers/operator.d.ts +82 -16
  48. package/dist/cli/handlers/operator.d.ts.map +1 -1
  49. package/dist/cli/handlers/run.d.ts +49 -42
  50. package/dist/cli/handlers/run.d.ts.map +1 -1
  51. package/dist/cli/main.d.ts +28 -1
  52. package/dist/cli/main.d.ts.map +1 -1
  53. package/dist/cli/mcp/op-tools.d.ts +16 -1
  54. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  55. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  56. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  57. package/dist/cli/registry.d.ts +26 -9
  58. package/dist/cli/registry.d.ts.map +1 -1
  59. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  60. package/dist/components/auto-release.d.ts +10 -20
  61. package/dist/components/auto-release.d.ts.map +1 -1
  62. package/dist/components/capability.d.ts +4 -4
  63. package/dist/components/cli-support.d.ts +20 -21
  64. package/dist/components/cli-support.d.ts.map +1 -1
  65. package/dist/components/component.d.ts +3 -3
  66. package/dist/components/component.d.ts.map +1 -1
  67. package/dist/components/driver-output.d.ts.map +1 -1
  68. package/dist/components/driver.d.ts +66 -36
  69. package/dist/components/driver.d.ts.map +1 -1
  70. package/dist/components/index.d.ts +1 -2
  71. package/dist/components/index.d.ts.map +1 -1
  72. package/dist/components/manifest-persistence.d.ts +1 -9
  73. package/dist/components/manifest-persistence.d.ts.map +1 -1
  74. package/dist/components/run-progress.d.ts +6 -6
  75. package/dist/components/run-progress.d.ts.map +1 -1
  76. package/dist/components/verbs/run-agent.d.ts +9 -15
  77. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  78. package/dist/components/verbs/verify.d.ts +2 -2
  79. package/dist/deep-observation.d.ts +17 -0
  80. package/dist/deep-observation.d.ts.map +1 -1
  81. package/dist/discovery/files.d.ts +4 -4
  82. package/dist/discovery/fold-import.d.ts.map +1 -1
  83. package/dist/discovery/param-deps.d.ts +33 -0
  84. package/dist/discovery/param-deps.d.ts.map +1 -1
  85. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  87. package/dist/fold/foldable-helpers.d.ts +5 -1
  88. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  89. package/dist/fold-provenance.d.ts +236 -0
  90. package/dist/fold-provenance.d.ts.map +1 -0
  91. package/dist/graph-ops.d.ts +1 -1
  92. package/dist/held-elsewhere.d.ts +85 -0
  93. package/dist/held-elsewhere.d.ts.map +1 -0
  94. package/dist/index.d.ts +3 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/kubectl-context.d.ts +3 -3
  97. package/dist/lexicon-config.d.ts +4 -4
  98. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  99. package/dist/lexicon.d.ts +104 -14
  100. package/dist/lexicon.d.ts.map +1 -1
  101. package/dist/lifecycle/change-set.d.ts +13 -0
  102. package/dist/lifecycle/change-set.d.ts.map +1 -1
  103. package/dist/lifecycle/deep-diff.d.ts +133 -6
  104. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  105. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  106. package/dist/lifecycle/gate-ledger.d.ts +71 -3
  107. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  108. package/dist/lifecycle/index.d.ts +1 -0
  109. package/dist/lifecycle/index.d.ts.map +1 -1
  110. package/dist/lifecycle/release-ledger.d.ts +2 -2
  111. package/dist/lifecycle/run-ledger.d.ts +53 -0
  112. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  113. package/dist/lifecycle/symptoms.d.ts +3 -3
  114. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  115. package/dist/lint/config.d.ts +81 -2
  116. package/dist/lint/config.d.ts.map +1 -1
  117. package/dist/lint/post-synth.d.ts +55 -2
  118. package/dist/lint/post-synth.d.ts.map +1 -1
  119. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  120. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  121. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  122. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  123. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  124. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  125. package/dist/lint/rules/comp/index.d.ts +1 -1
  126. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  127. package/dist/lint/rules/op/index.d.ts +16 -0
  128. package/dist/lint/rules/op/index.d.ts.map +1 -0
  129. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  130. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  131. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  132. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  133. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  134. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  135. package/dist/lint/rules/op/support.d.ts +12 -0
  136. package/dist/lint/rules/op/support.d.ts.map +1 -0
  137. package/dist/lint/suppressions.d.ts +108 -0
  138. package/dist/lint/suppressions.d.ts.map +1 -0
  139. package/dist/live-endpoint.d.ts +1 -1
  140. package/dist/op/activities/activity-contracts.d.ts +54 -0
  141. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  142. package/dist/op/activities/apply.d.ts +262 -0
  143. package/dist/op/activities/apply.d.ts.map +1 -0
  144. package/dist/op/activities/build.d.ts +13 -0
  145. package/dist/op/activities/build.d.ts.map +1 -0
  146. package/dist/op/activities/converge.d.ts +110 -0
  147. package/dist/op/activities/converge.d.ts.map +1 -0
  148. package/dist/op/activities/env-teardown.d.ts +76 -0
  149. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  150. package/dist/op/activities/guard-validate.d.ts +64 -0
  151. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  152. package/dist/op/activities/http-check.d.ts +34 -0
  153. package/dist/op/activities/http-check.d.ts.map +1 -0
  154. package/dist/op/activities/index.d.ts +31 -0
  155. package/dist/op/activities/index.d.ts.map +1 -0
  156. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  157. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  158. package/dist/op/activities/lifecycle.d.ts +42 -0
  159. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  160. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  161. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  162. package/dist/op/activities/policy.d.ts +17 -0
  163. package/dist/op/activities/policy.d.ts.map +1 -0
  164. package/dist/op/activities/reconcile.d.ts +89 -0
  165. package/dist/op/activities/reconcile.d.ts.map +1 -0
  166. package/dist/op/activities/shell.d.ts +13 -0
  167. package/dist/op/activities/shell.d.ts.map +1 -0
  168. package/dist/op/activities/teardown.d.ts +10 -0
  169. package/dist/op/activities/teardown.d.ts.map +1 -0
  170. package/dist/op/activities/util.d.ts +7 -0
  171. package/dist/op/activities/util.d.ts.map +1 -0
  172. package/dist/op/activities/wait.d.ts +28 -0
  173. package/dist/op/activities/wait.d.ts.map +1 -0
  174. package/dist/op/activities/workflow-audit.d.ts +92 -0
  175. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  176. package/dist/op/activity-contract-registry.d.ts +76 -0
  177. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  178. package/dist/op/activity-contract.d.ts +25 -18
  179. package/dist/op/activity-contract.d.ts.map +1 -1
  180. package/dist/op/activity-failure.d.ts +28 -0
  181. package/dist/op/activity-failure.d.ts.map +1 -0
  182. package/dist/op/activity-profiles.d.ts +118 -0
  183. package/dist/op/activity-profiles.d.ts.map +1 -0
  184. package/dist/op/activity-registry.d.ts +22 -38
  185. package/dist/op/activity-registry.d.ts.map +1 -1
  186. package/dist/op/activity-runtime.d.ts +3 -14
  187. package/dist/op/activity-runtime.d.ts.map +1 -1
  188. package/dist/op/builders.d.ts +84 -29
  189. package/dist/op/builders.d.ts.map +1 -1
  190. package/dist/op/composites/apply-op.d.ts +113 -0
  191. package/dist/op/composites/apply-op.d.ts.map +1 -0
  192. package/dist/op/composites/converge-op.d.ts +120 -0
  193. package/dist/op/composites/converge-op.d.ts.map +1 -0
  194. package/dist/op/composites/index.d.ts +26 -0
  195. package/dist/op/composites/index.d.ts.map +1 -0
  196. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  197. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  198. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  199. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  200. package/dist/op/composites/reconcile-op.d.ts +57 -0
  201. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  202. package/dist/op/composites/watch-op.d.ts +60 -0
  203. package/dist/op/composites/watch-op.d.ts.map +1 -0
  204. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  205. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  206. package/dist/op/converge-rule.d.ts +12 -10
  207. package/dist/op/converge-rule.d.ts.map +1 -1
  208. package/dist/op/cron.d.ts +51 -0
  209. package/dist/op/cron.d.ts.map +1 -0
  210. package/dist/op/discover.d.ts +9 -0
  211. package/dist/op/discover.d.ts.map +1 -1
  212. package/dist/op/duration.d.ts +11 -0
  213. package/dist/op/duration.d.ts.map +1 -0
  214. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  215. package/dist/op/gate.d.ts +90 -0
  216. package/dist/op/gate.d.ts.map +1 -0
  217. package/dist/op/generate-pipeline.d.ts +9 -0
  218. package/dist/op/generate-pipeline.d.ts.map +1 -1
  219. package/dist/op/index.d.ts +23 -7
  220. package/dist/op/index.d.ts.map +1 -1
  221. package/dist/op/local-executor.d.ts +114 -28
  222. package/dist/op/local-executor.d.ts.map +1 -1
  223. package/dist/op/local-output.d.ts +11 -1
  224. package/dist/op/local-output.d.ts.map +1 -1
  225. package/dist/op/op-ir.d.ts +184 -0
  226. package/dist/op/op-ir.d.ts.map +1 -0
  227. package/dist/op/op-verb-class.d.ts +10 -2
  228. package/dist/op/op-verb-class.d.ts.map +1 -1
  229. package/dist/op/operator.d.ts +30 -5
  230. package/dist/op/operator.d.ts.map +1 -1
  231. package/dist/op/receipt-store.d.ts +1 -1
  232. package/dist/op/receipt-store.d.ts.map +1 -1
  233. package/dist/op/resource.d.ts +15 -1
  234. package/dist/op/resource.d.ts.map +1 -1
  235. package/dist/op/runtime.d.ts +200 -0
  236. package/dist/op/runtime.d.ts.map +1 -0
  237. package/dist/op/runtimes/local.d.ts +29 -0
  238. package/dist/op/runtimes/local.d.ts.map +1 -0
  239. package/dist/op/step-output-ref.d.ts +15 -13
  240. package/dist/op/step-output-ref.d.ts.map +1 -1
  241. package/dist/op/types.d.ts +79 -27
  242. package/dist/op/types.d.ts.map +1 -1
  243. package/dist/ownership.d.ts +1 -1
  244. package/dist/provenance.d.ts +21 -0
  245. package/dist/provenance.d.ts.map +1 -1
  246. package/dist/serializer-walker.d.ts.map +1 -1
  247. package/package.json +6 -1
  248. package/src/audit/catalog.test.ts +79 -3
  249. package/src/audit/catalog.ts +111 -13
  250. package/src/audit/core.test.ts +26 -0
  251. package/src/audit/core.ts +61 -17
  252. package/src/audit/discover.test.ts +39 -0
  253. package/src/audit/discover.ts +91 -15
  254. package/src/audit/lineage.ts +100 -0
  255. package/src/audit/prior-art.ts +153 -0
  256. package/src/audit/proof.test.ts +67 -0
  257. package/src/audit/proof.ts +70 -0
  258. package/src/audit/report-html.ts +9 -4
  259. package/src/audit/report-model.ts +16 -2
  260. package/src/audit/report.test.ts +33 -0
  261. package/src/audit/report.ts +5 -3
  262. package/src/audit/rules-doc.test.ts +34 -2
  263. package/src/audit/rules-doc.ts +82 -9
  264. package/src/audit/secrets.ts +15 -3
  265. package/src/audit/terraform-state.test.ts +144 -0
  266. package/src/audit/terraform-state.ts +131 -0
  267. package/src/build.ts +31 -1
  268. package/src/claimed-fields.test.ts +145 -0
  269. package/src/claimed-fields.ts +208 -0
  270. package/src/cli/build-params-cli.ts +2 -2
  271. package/src/cli/command-group.ts +14 -0
  272. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  273. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  274. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  275. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  276. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  277. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  278. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  279. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  280. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  281. package/src/cli/commands/audit.test.ts +189 -11
  282. package/src/cli/commands/audit.ts +73 -27
  283. package/src/cli/commands/build.test.ts +292 -9
  284. package/src/cli/commands/build.ts +156 -24
  285. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  286. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  287. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  288. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  289. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  290. package/src/cli/commands/init-lexicon.test.ts +97 -2
  291. package/src/cli/commands/init-lexicon.ts +4 -2
  292. package/src/cli/commands/init.test.ts +35 -1
  293. package/src/cli/commands/init.ts +14 -3
  294. package/src/cli/commands/lint.test.ts +48 -0
  295. package/src/cli/commands/lint.ts +123 -0
  296. package/src/cli/commands/onboard.test.ts +75 -0
  297. package/src/cli/commands/onboard.ts +54 -23
  298. package/src/cli/handlers/build.test.ts +81 -1
  299. package/src/cli/handlers/build.ts +15 -1
  300. package/src/cli/handlers/graph.test.ts +3 -3
  301. package/src/cli/handlers/lifecycle.test.ts +195 -12
  302. package/src/cli/handlers/lifecycle.ts +123 -6
  303. package/src/cli/handlers/misc.ts +1 -1
  304. package/src/cli/handlers/operator.test.ts +183 -11
  305. package/src/cli/handlers/operator.ts +260 -55
  306. package/src/cli/handlers/run.test.ts +431 -1287
  307. package/src/cli/handlers/run.ts +365 -915
  308. package/src/cli/main.test.ts +23 -15
  309. package/src/cli/main.ts +88 -33
  310. package/src/cli/mcp/op-tools.ts +147 -88
  311. package/src/cli/mcp/resource-handlers.ts +10 -37
  312. package/src/cli/mcp/resources/context.ts +11 -12
  313. package/src/cli/mcp/server.test.ts +25 -27
  314. package/src/cli/mcp/server.ts +2 -2
  315. package/src/cli/registry.ts +26 -9
  316. package/src/codegen/docs.ts +1 -1
  317. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  318. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  319. package/src/components/README.md +3 -2
  320. package/src/components/auto-release.test.ts +4 -20
  321. package/src/components/auto-release.ts +17 -42
  322. package/src/components/capability.ts +4 -4
  323. package/src/components/cli-support.test.ts +10 -5
  324. package/src/components/cli-support.ts +52 -44
  325. package/src/components/component.schema.json +2 -2
  326. package/src/components/component.ts +3 -3
  327. package/src/components/driver-output.ts +14 -2
  328. package/src/components/driver.test.ts +92 -22
  329. package/src/components/driver.ts +193 -83
  330. package/src/components/index.ts +1 -6
  331. package/src/components/manifest-persistence.test.ts +2 -20
  332. package/src/components/manifest-persistence.ts +4 -24
  333. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  334. package/src/components/run-progress.ts +6 -6
  335. package/src/components/verbs/run-agent.ts +11 -17
  336. package/src/components/verbs/verify.ts +2 -2
  337. package/src/deep-observation.test.ts +75 -0
  338. package/src/deep-observation.ts +48 -0
  339. package/src/discovery/collect.test.ts +3 -3
  340. package/src/discovery/files.ts +4 -4
  341. package/src/discovery/fold-import.ts +105 -2
  342. package/src/discovery/param-deps.test.ts +112 -1
  343. package/src/discovery/param-deps.ts +193 -0
  344. package/src/discovery/sandbox/bundle.ts +15 -1
  345. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  346. package/src/discovery/sandbox/config-wire.ts +2 -2
  347. package/src/fold/fold.test.ts +56 -0
  348. package/src/fold/foldable-helpers.ts +81 -1
  349. package/src/fold-provenance-exclusion.test.ts +186 -0
  350. package/src/fold-provenance.test.ts +495 -0
  351. package/src/fold-provenance.ts +441 -0
  352. package/src/graph-ops.test.ts +2 -2
  353. package/src/graph-ops.ts +1 -1
  354. package/src/held-elsewhere.test.ts +102 -0
  355. package/src/held-elsewhere.ts +114 -0
  356. package/src/index.ts +3 -0
  357. package/src/kubectl-context.ts +3 -3
  358. package/src/lexicon-config.test.ts +5 -5
  359. package/src/lexicon-config.ts +4 -4
  360. package/src/lexicon-plugin-helpers.ts +1 -1
  361. package/src/lexicon.ts +120 -14
  362. package/src/lifecycle/change-set.test.ts +49 -0
  363. package/src/lifecycle/change-set.ts +67 -0
  364. package/src/lifecycle/converge-ledger.ts +1 -1
  365. package/src/lifecycle/deep-diff.test.ts +267 -14
  366. package/src/lifecycle/deep-diff.ts +217 -14
  367. package/src/lifecycle/deep-observe.test.ts +3 -1
  368. package/src/lifecycle/deep-observe.ts +16 -6
  369. package/src/lifecycle/gate-ledger.test.ts +74 -2
  370. package/src/lifecycle/gate-ledger.ts +143 -36
  371. package/src/lifecycle/index.ts +1 -0
  372. package/src/lifecycle/release-ledger.ts +2 -2
  373. package/src/lifecycle/run-ledger.test.ts +268 -0
  374. package/src/lifecycle/run-ledger.ts +201 -0
  375. package/src/lifecycle/symptoms.ts +3 -3
  376. package/src/lint/config.test.ts +82 -1
  377. package/src/lint/config.ts +112 -3
  378. package/src/lint/policy.test.ts +1 -1
  379. package/src/lint/post-synth.test.ts +31 -0
  380. package/src/lint/post-synth.ts +54 -1
  381. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  382. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  383. package/src/lint/rules/comp/comp.test.ts +1 -1
  384. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
  385. package/src/lint/rules/comp/index.ts +3 -3
  386. package/src/lint/rules/op/index.ts +23 -0
  387. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  388. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  389. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  390. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  391. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  392. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  393. package/src/lint/rules/op/support.ts +11 -0
  394. package/src/lint/suppressions.test.ts +163 -0
  395. package/src/lint/suppressions.ts +204 -0
  396. package/src/live-endpoint.ts +1 -1
  397. package/src/op/activities/activity-contracts.ts +66 -0
  398. package/src/op/activities/apply.test.ts +567 -0
  399. package/src/op/activities/apply.ts +662 -0
  400. package/src/op/activities/build.ts +26 -0
  401. package/src/op/activities/converge.test.ts +330 -0
  402. package/src/op/activities/converge.ts +449 -0
  403. package/src/op/activities/env-teardown.test.ts +187 -0
  404. package/src/op/activities/env-teardown.ts +171 -0
  405. package/src/op/activities/guard-validate.test.ts +139 -0
  406. package/src/op/activities/guard-validate.ts +223 -0
  407. package/src/op/activities/http-check.test.ts +48 -0
  408. package/src/op/activities/http-check.ts +55 -0
  409. package/src/op/activities/index.ts +119 -0
  410. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  411. package/src/op/activities/lexicon-upgrade.ts +673 -0
  412. package/src/op/activities/lifecycle.ts +85 -0
  413. package/src/op/activities/pipeline-audit.test.ts +67 -0
  414. package/src/op/activities/pipeline-audit.ts +201 -0
  415. package/src/op/activities/policy.test.ts +19 -0
  416. package/src/op/activities/policy.ts +37 -0
  417. package/src/op/activities/reconcile.test.ts +81 -0
  418. package/src/op/activities/reconcile.ts +187 -0
  419. package/src/op/activities/shell.ts +26 -0
  420. package/src/op/activities/teardown.ts +22 -0
  421. package/src/op/activities/util.ts +6 -0
  422. package/src/op/activities/wait.ts +69 -0
  423. package/src/op/activities/workflow-audit.test.ts +90 -0
  424. package/src/op/activities/workflow-audit.ts +203 -0
  425. package/src/op/activity-contract-registry.test.ts +98 -0
  426. package/src/op/activity-contract-registry.ts +114 -0
  427. package/src/op/activity-contract.test.ts +1 -1
  428. package/src/op/activity-contract.ts +37 -33
  429. package/src/op/activity-failure.ts +34 -0
  430. package/src/op/activity-profiles.test.ts +56 -0
  431. package/src/op/activity-profiles.ts +116 -0
  432. package/src/op/activity-registry.test.ts +37 -28
  433. package/src/op/activity-registry.ts +27 -56
  434. package/src/op/activity-runtime.ts +3 -47
  435. package/src/op/builders-exports.test.ts +0 -49
  436. package/src/op/builders-typed.test.ts +164 -0
  437. package/src/op/builders.ts +123 -39
  438. package/src/op/composites/apply-op.ts +208 -0
  439. package/src/op/composites/composites.test.ts +366 -0
  440. package/src/op/composites/converge-op.test.ts +168 -0
  441. package/src/op/composites/converge-op.ts +214 -0
  442. package/src/op/composites/index.ts +26 -0
  443. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  444. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  445. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  446. package/src/op/composites/pipeline-audit-op.ts +78 -0
  447. package/src/op/composites/reconcile-op.test.ts +51 -0
  448. package/src/op/composites/reconcile-op.ts +107 -0
  449. package/src/op/composites/watch-op.ts +108 -0
  450. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  451. package/src/op/composites/workflow-audit-op.ts +88 -0
  452. package/src/op/converge-rule.ts +12 -10
  453. package/src/op/cron.test.ts +128 -0
  454. package/src/op/cron.ts +149 -0
  455. package/src/op/discover-root.test.ts +105 -1
  456. package/src/op/discover.test.ts +6 -2
  457. package/src/op/discover.ts +84 -16
  458. package/src/op/duration.ts +21 -0
  459. package/src/op/effect-step.test.ts +26 -11
  460. package/src/op/emulator-lifecycle.ts +1 -2
  461. package/src/op/gate.ts +155 -0
  462. package/src/op/generate-pipeline.test.ts +65 -3
  463. package/src/op/generate-pipeline.ts +19 -2
  464. package/src/op/index.ts +39 -6
  465. package/src/op/local-executor.test.ts +163 -34
  466. package/src/op/local-executor.ts +370 -102
  467. package/src/op/local-output.test.ts +95 -11
  468. package/src/op/local-output.ts +33 -4
  469. package/src/op/op-ir.test.ts +281 -0
  470. package/src/op/op-ir.ts +417 -0
  471. package/src/op/op-verb-class.ts +19 -5
  472. package/src/op/op.test.ts +26 -5
  473. package/src/op/operator.test.ts +175 -6
  474. package/src/op/operator.ts +63 -16
  475. package/src/op/receipt-store.ts +1 -1
  476. package/src/op/resource.ts +20 -2
  477. package/src/op/runtime.ts +203 -0
  478. package/src/op/runtimes/local.test.ts +181 -0
  479. package/src/op/runtimes/local.ts +212 -0
  480. package/src/op/step-output-ref.ts +15 -13
  481. package/src/op/types.ts +84 -24
  482. package/src/ownership.ts +1 -1
  483. package/src/provenance.test.ts +6 -0
  484. package/src/provenance.ts +21 -1
  485. package/src/serializer-walker.test.ts +42 -0
  486. package/src/serializer-walker.ts +21 -2
  487. package/dist/cli/handlers/op-progress.d.ts +0 -90
  488. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  489. package/dist/cli/handlers/run-client.d.ts +0 -127
  490. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  491. package/dist/cli/handlers/run-report.d.ts +0 -5
  492. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  493. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  494. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  495. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  496. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  497. package/src/cli/handlers/op-progress.test.ts +0 -268
  498. package/src/cli/handlers/op-progress.ts +0 -248
  499. package/src/cli/handlers/run-client.test.ts +0 -82
  500. package/src/cli/handlers/run-client.ts +0 -219
  501. package/src/cli/handlers/run-report.test.ts +0 -62
  502. package/src/cli/handlers/run-report.ts +0 -122
  503. package/src/components/temporal-codegen-loader.ts +0 -41
  504. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Op run ledger (#2118, epic #2114) — one immutable record per Op run, on the
3
+ * `chant/lifecycle` orphan branch, modelled on `./converge-ledger.ts` down to
4
+ * the CAS-retry loop and the "skip a malformed line, count it" reader.
5
+ *
6
+ * ## Why an Op run leaves a record at all
7
+ *
8
+ * `OpConfig` used to carry `searchAttributes`, and the generated orchestrator
9
+ * code upserted `Phase`, `Drift`, `Approver` and `RollbackFailed` into them as
10
+ * it ran. Those are not properties of the declaration — they are facts about
11
+ * one run, and they only existed while a cluster held the run's history. Ops
12
+ * now run on whatever runtime `chant run --on`
13
+ * selects (#2121), including the plain in-process one, so the outcome has to
14
+ * be durable somewhere chant owns. Here. What stayed on the declaration is
15
+ * `OpConfig.labels`, the discovery half of the same field, and it is copied
16
+ * onto every record so a reader can filter runs the way it filters
17
+ * declarations.
18
+ *
19
+ * The record shape itself lives in `../op/runtime.ts`, beside the provider
20
+ * contract that reads it back: this is the ledger, not the vocabulary.
21
+ *
22
+ * ## Storage
23
+ *
24
+ * `<env>/runs__<op>.jsonl`, beside `<env>/converge.jsonl`. The `runs__`
25
+ * prefix folds what would read as a `runs/` subdirectory into one path
26
+ * segment: `writeBlobToPath` (./git.ts) builds the orphan branch's tree with
27
+ * `git mktree` over a flat `<env>/<file>` layout, and mktree rejects a slash
28
+ * in an entry name outright, so a genuine third level would mean rewriting
29
+ * that function's tree assembly. `__` is the same separator `./git.ts`'s
30
+ * `snapshotKey` already uses to fold a stack name into a flat blob name.
31
+ *
32
+ * Per-op rather than one file per env because a run ledger's reader almost
33
+ * always wants one op's history (`chant run <op>`'s own record, the local
34
+ * runtime's `status`/`log`), and an env with a busy op should not make every
35
+ * other op's read proportional to it.
36
+ */
37
+ import { randomUUID } from "node:crypto";
38
+ import { sortedJsonReplacer } from "../utils";
39
+ import type { OpConfig } from "../op/types";
40
+ import type { StepRecord } from "../op/local-executor";
41
+ import type { OpRunRecord, OpRunRecordInput, OpRunPhaseRecord, OpRunStepRecord } from "../op/runtime";
42
+ import { readBlobFromPath, readPathSha, readBlobBySha, writeBlobToPath, RefCASConflictError } from "./git";
43
+
44
+ /** Prefix folding the per-op run ledgers into one flat name under `<env>/`. See the module doc. */
45
+ const PREFIX = "runs__";
46
+
47
+ /** Read-modify-append retry budget, same shape and reasoning as `appendConvergeRecord`'s. */
48
+ const APPEND_RETRY_ATTEMPTS = 5;
49
+
50
+ /** The env a run records against when its Op declares no `labels.Env`. */
51
+ export const DEFAULT_RUN_ENV = "local";
52
+
53
+ function fileFor(op: string): string {
54
+ return `${PREFIX}${op}.jsonl`;
55
+ }
56
+
57
+ /** The env a run records against: the Op's own `labels.Env`, else {@link DEFAULT_RUN_ENV}. */
58
+ export function runEnvOf(config: Pick<OpConfig, "labels">): string {
59
+ return config.labels?.Env ?? DEFAULT_RUN_ENV;
60
+ }
61
+
62
+ function phaseStatus(steps: OpRunStepRecord[]): OpRunPhaseRecord["status"] {
63
+ if (steps.some((s) => s.status === "fail")) return "fail";
64
+ if (steps.length > 0 && steps.every((s) => s.status === "skipped")) return "skipped";
65
+ return "ok";
66
+ }
67
+
68
+ /**
69
+ * Fold a run's step records into the ledger shape, pure — no clock, no git.
70
+ * `started`/`ended` are supplied by the caller for the same reason
71
+ * `ConvergeTickRecord.timestamp` is: library code here never calls
72
+ * `Date.now()` itself.
73
+ *
74
+ * Phases are grouped by `StepRecord.phase` in first-seen order, which is
75
+ * execution order for the sequential path and the authored order for a
76
+ * parallel one. A compensation (`onFailure`) phase appears after the phase
77
+ * that failed, exactly where the executor emitted its records.
78
+ */
79
+ export function buildRunRecord(
80
+ config: Pick<OpConfig, "name" | "labels">,
81
+ records: readonly StepRecord[],
82
+ times: {
83
+ started: string;
84
+ ended: string;
85
+ status: OpRunRecord["status"];
86
+ id?: string;
87
+ gate?: OpRunRecord["gate"];
88
+ },
89
+ ): OpRunRecordInput {
90
+ const phases: OpRunPhaseRecord[] = [];
91
+ const byName = new Map<string, OpRunPhaseRecord>();
92
+ const outcomes: Record<string, unknown> = {};
93
+
94
+ for (const record of records) {
95
+ let phase = byName.get(record.phase);
96
+ if (!phase) {
97
+ phase = { name: record.phase, status: "ok", steps: [] };
98
+ byName.set(record.phase, phase);
99
+ phases.push(phase);
100
+ }
101
+ phase.steps.push({
102
+ fn: record.fn,
103
+ status: record.status,
104
+ durationMs: record.durationMs,
105
+ ...(record.outcome ? { outcome: record.outcome } : {}),
106
+ ...(record.approval ? { approval: record.approval } : {}),
107
+ ...(record.error !== undefined ? { error: record.error } : {}),
108
+ });
109
+ if (record.outcome) outcomes[record.outcome.name] = record.outcome.value;
110
+ }
111
+ for (const phase of phases) phase.status = phaseStatus(phase.steps);
112
+
113
+ return {
114
+ ...(times.id !== undefined ? { id: times.id } : {}),
115
+ op: config.name,
116
+ env: runEnvOf(config),
117
+ started: times.started,
118
+ ended: times.ended,
119
+ status: times.status,
120
+ labels: { ...(config.labels ?? {}) },
121
+ outcomes,
122
+ phases,
123
+ ...(times.gate ? { gate: times.gate } : {}),
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Append one immutable run record. Does not push to the remote — call
129
+ * `pushLifecycle` (./git.ts) afterward, the two-step shape every ledger here
130
+ * uses.
131
+ *
132
+ * Mints `record.id` when the input carries none, and retries the whole
133
+ * read-modify-write on `RefCASConflictError` re-reading fresh each attempt —
134
+ * see `appendConvergeRecord`'s doc for why both of those live in the append
135
+ * function rather than in its callers.
136
+ */
137
+ export async function appendRunRecord(
138
+ input: OpRunRecordInput,
139
+ opts?: { cwd?: string },
140
+ ): Promise<{ commit: string; record: OpRunRecord }> {
141
+ const record: OpRunRecord = { version: 1, ...input, id: input.id ?? randomUUID() };
142
+ const json = JSON.stringify(record, sortedJsonReplacer);
143
+ const filename = fileFor(record.op);
144
+
145
+ let lastErr: unknown;
146
+ for (let attempt = 1; attempt <= APPEND_RETRY_ATTEMPTS; attempt++) {
147
+ try {
148
+ const priorSha = await readPathSha(record.env, filename, opts);
149
+ const existing = priorSha ? await readBlobBySha(priorSha, opts) : null;
150
+ const content = existing ? `${existing.replace(/\n$/, "")}\n${json}` : json;
151
+ const commit = await writeBlobToPath(record.env, filename, content, `Op run record: ${record.op}`, {
152
+ ...opts,
153
+ expectPriorPathSha: priorSha,
154
+ });
155
+ return { commit, record };
156
+ } catch (err) {
157
+ if (!(err instanceof RefCASConflictError)) throw err;
158
+ lastErr = err;
159
+ }
160
+ }
161
+ throw lastErr;
162
+ }
163
+
164
+ /**
165
+ * Read every run record for `op` in `environment`, oldest first. Malformed
166
+ * lines are skipped and counted, never thrown on — a hand-edited or truncated
167
+ * ledger degrades to a shorter history, the stance every ledger here takes.
168
+ */
169
+ export async function readRunLedger(
170
+ environment: string,
171
+ op: string,
172
+ opts?: { cwd?: string },
173
+ ): Promise<{ records: OpRunRecord[]; malformed: number }> {
174
+ const content = await readBlobFromPath(environment, fileFor(op), opts);
175
+ if (!content) return { records: [], malformed: 0 };
176
+
177
+ const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
178
+ const records: OpRunRecord[] = [];
179
+ let malformed = 0;
180
+ for (const line of lines) {
181
+ try {
182
+ const parsed = JSON.parse(line) as Partial<OpRunRecord>;
183
+ if (
184
+ parsed.version !== 1 ||
185
+ typeof parsed.op !== "string" ||
186
+ typeof parsed.env !== "string" ||
187
+ typeof parsed.started !== "string" ||
188
+ typeof parsed.ended !== "string" ||
189
+ typeof parsed.status !== "string" ||
190
+ !Array.isArray(parsed.phases)
191
+ ) {
192
+ malformed++;
193
+ continue;
194
+ }
195
+ records.push(parsed as OpRunRecord);
196
+ } catch {
197
+ malformed++;
198
+ }
199
+ }
200
+ return { records, malformed };
201
+ }
@@ -8,8 +8,8 @@
8
8
  * `LexiconPlugin.symptoms?()` interface in v1 — everything here is a pure
9
9
  * projection of structures chant already computes (`ChangeSet`,
10
10
  * `ComponentStatusRow[]`). No I/O in this module; a caller (the `convergeTick`
11
- * activity, lexicons/temporal/src/op/activities/converge.ts) gathers those
12
- * inputs and hands them here.
11
+ * activity, ../op/activities/converge.ts) gathers those inputs and hands them
12
+ * here.
13
13
  *
14
14
  * One record per tick, not per component: the issue's own rule sketch reads
15
15
  * `s.status`/`s.backupVerifiedAge` as a single symptom `s` a rule closes
@@ -109,7 +109,7 @@ export function deriveSymptoms(env: string, cs: ChangeSet, statusRows: Component
109
109
  };
110
110
  }
111
111
 
112
- /** Every field name a `ConvergeSymptom` produces — the runtime whitelist `TMP014` (lexicons/temporal) re-validates a serialized rule table's predicates against. Kept in sync with {@link ConvergeSymptom} by hand (deliberately small, changes rarely). */
112
+ /** Every field name a `ConvergeSymptom` produces — the runtime whitelist `OPS014` (packages/core/src/lint/rules/op/ops014-converge-rule-refusals.ts) re-validates a serialized rule table's predicates against. Kept in sync with {@link ConvergeSymptom} by hand (deliberately small, changes rarely). */
113
113
  export const CONVERGE_SYMPTOM_FIELDS: ReadonlySet<string> = new Set([
114
114
  "env",
115
115
  "status",
@@ -1,6 +1,7 @@
1
1
  import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
- import { loadConfig, DEFAULT_CONFIG, findProjectRoot, resolveConfiguredSeverity, applyConfiguredSeverity } from "./config";
2
+ import { loadConfig, DEFAULT_CONFIG, findProjectRoot, resolveConfiguredSeverity, applyConfiguredSeverity, resolvePresetIds, applyConfiguredPreset } from "./config";
3
3
  import type { PostSynthDiagnostic } from "./post-synth";
4
+ import { auditRule, type RuleMeta } from "../audit/catalog";
4
5
  import { writeFileSync, mkdirSync, rmSync } from "fs";
5
6
  import { join, resolve } from "path";
6
7
 
@@ -755,6 +756,26 @@ describe("resolveConfiguredSeverity", () => {
755
756
  resolveConfiguredSeverity({ COR009: ["fatal" as never, { max: 12 }] }, "COR009", "error"),
756
757
  ).toThrow(/severity "fatal"/);
757
758
  });
759
+
760
+ describe("alias resolution (chant #2113, opt-in via `catalog`)", () => {
761
+ const catalog: Record<string, RuleMeta> = {
762
+ NEW001: { ...auditRule("NEW001", "report-only", "guidance", "T", "r"), aliases: ["OLD001"] },
763
+ };
764
+
765
+ test("with no catalog, a `rules` key naming an alias does not match, today's behavior, unchanged", () => {
766
+ expect(resolveConfiguredSeverity({ OLD001: "off" }, "NEW001", "error")).toEqual({ severity: "error" });
767
+ });
768
+
769
+ test("with a catalog, a `rules` key naming an alias resolves to the canonical id", () => {
770
+ expect(resolveConfiguredSeverity({ OLD001: "off" }, "NEW001", "error", catalog)).toEqual({ severity: "off" });
771
+ });
772
+
773
+ test("a `rules` key already at the canonical id still wins over an alias entry", () => {
774
+ expect(
775
+ resolveConfiguredSeverity({ OLD001: "off", NEW001: "warning" }, "NEW001", "error", catalog),
776
+ ).toEqual({ severity: "warning" });
777
+ });
778
+ });
758
779
  });
759
780
 
760
781
  /**
@@ -808,4 +829,64 @@ describe("applyConfiguredSeverity", () => {
808
829
  test("an empty diagnostics list is a no-op", () => {
809
830
  expect(applyConfiguredSeverity([], { WAW019: "off" })).toEqual({ diagnostics: [], suppressed: [] });
810
831
  });
832
+
833
+ test("a `rules` key naming a retired rule's alias suppresses the renamed rule's finding, given a catalog (#2113)", () => {
834
+ const catalog: Record<string, RuleMeta> = {
835
+ WAW019: { ...auditRule("WAW019", "merge-worthy", "guidance", "T", "r"), aliases: ["WAW019-OLD"] },
836
+ };
837
+ const result = applyConfiguredSeverity([diag()], { "WAW019-OLD": "off" }, catalog);
838
+ expect(result.diagnostics).toEqual([]);
839
+ expect(result.suppressed).toEqual([diag()]);
840
+ });
841
+ });
842
+
843
+ describe("resolvePresetIds (chant #2113)", () => {
844
+ const presets = { recommended: ["A001", "A002"], all: ["A001", "A002", "A003"] };
845
+
846
+ test("a lexicon with no presets at all returns undefined (no filtering)", () => {
847
+ expect(resolvePresetIds(undefined, "recommended")).toBeUndefined();
848
+ });
849
+
850
+ test("a known preset name resolves to its id set", () => {
851
+ expect(resolvePresetIds(presets, "recommended")).toEqual(new Set(["A001", "A002"]));
852
+ expect(resolvePresetIds(presets, "all")).toEqual(new Set(["A001", "A002", "A003"]));
853
+ });
854
+
855
+ test("an unrecognized preset name falls back to unfiltered (undefined), not an empty set", () => {
856
+ expect(resolvePresetIds(presets, "typo-of-recommended")).toBeUndefined();
857
+ });
858
+ });
859
+
860
+ describe("applyConfiguredPreset (chant #2113)", () => {
861
+ function diag(checkId: string, overrides: Partial<PostSynthDiagnostic> = {}): PostSynthDiagnostic {
862
+ return { checkId, severity: "warning", message: `finding for ${checkId}`, ...overrides };
863
+ }
864
+
865
+ test("no presetIds (undefined) is a no-op: every diagnostic passes through", () => {
866
+ const diags = [diag("A001"), diag("Z999")];
867
+ const result = applyConfiguredPreset(diags, undefined, undefined);
868
+ expect(result.diagnostics).toEqual(diags);
869
+ expect(result.suppressed).toEqual([]);
870
+ });
871
+
872
+ test("a report-only rule outside the active preset is not reported, moved to suppressed, not dropped", () => {
873
+ const recommended = new Set(["A001"]);
874
+ const result = applyConfiguredPreset([diag("A001"), diag("Z999")], recommended, undefined);
875
+ expect(result.diagnostics.map((d) => d.checkId)).toEqual(["A001"]);
876
+ expect(result.suppressed.map((d) => d.checkId)).toEqual(["Z999"]);
877
+ });
878
+
879
+ test("the same rule under a wider preset (e.g. `all`) IS reported", () => {
880
+ const all = new Set(["A001", "Z999"]);
881
+ const result = applyConfiguredPreset([diag("A001"), diag("Z999")], all, undefined);
882
+ expect(result.diagnostics.map((d) => d.checkId).sort()).toEqual(["A001", "Z999"]);
883
+ expect(result.suppressed).toEqual([]);
884
+ });
885
+
886
+ test("an explicit `lint.rules` entry re-enables a rule the active preset excludes", () => {
887
+ const recommended = new Set(["A001"]);
888
+ const result = applyConfiguredPreset([diag("Z999")], recommended, { Z999: "warning" });
889
+ expect(result.diagnostics.map((d) => d.checkId)).toEqual(["Z999"]);
890
+ expect(result.suppressed).toEqual([]);
891
+ });
811
892
  });
@@ -5,6 +5,7 @@ import { evaluateProjectConfigSync } from "../config-sandbox";
5
5
  import type { Severity, RuleConfig } from "./rule";
6
6
  import type { PostSynthDiagnostic } from "./post-synth";
7
7
  import { moduleDir, getRuntime } from "../runtime-adapter";
8
+ import { canonicalRuleId, type RuleMeta } from "../audit/catalog";
8
9
  import strictPreset from "./presets/strict.json";
9
10
 
10
11
  // chant #1117 — the upward config-discovery walk moved to a shared module
@@ -38,6 +39,7 @@ export const LintConfigSchema = z.object({
38
39
  })).optional(),
39
40
  plugins: z.array(z.string()).optional(),
40
41
  policies: z.array(z.string()).optional(),
42
+ presets: z.record(z.string(), z.string()).optional(),
41
43
  });
42
44
 
43
45
  /**
@@ -166,6 +168,23 @@ export interface LintConfig {
166
168
  * phase; same engine.
167
169
  */
168
170
  policies?: string[];
171
+ /**
172
+ * Lexicon name mapped to a preset name (chant #2113), e.g.
173
+ * `{ terraform: "recommended" }`. A preset is a lexicon's own bundle of
174
+ * post-synth check ids. tflint ships `recommended` (a subset) and `all`
175
+ * for its Terraform-language rules the same way, and a lexicon that ships
176
+ * one exposes it through its plugin's `lintPresets()` (`../lexicon.ts`).
177
+ * Naming a preset here changes which of that lexicon's post-synth findings
178
+ * are reported at all, distinct from `rules`, which changes an
179
+ * already-reported finding's severity. A rule a preset excludes is not
180
+ * suppressed (it never runs the severity-override gauntlet in the first
181
+ * place), but an explicit `rules` entry for it always wins and re-enables
182
+ * it regardless of the active preset, see `resolvePresetIds`/
183
+ * `applyConfiguredPreset` below. Unset, or naming a preset the lexicon
184
+ * does not export, falls back to `"recommended"`. A lexicon that ships no
185
+ * presets at all is unaffected by this field, same as today.
186
+ */
187
+ presets?: Record<string, string>;
169
188
  }
170
189
 
171
190
  /**
@@ -226,15 +245,30 @@ export function parseRuleConfig(value: RuleConfig): ParsedRuleConfig {
226
245
  * checks have no per-file scope to begin with (see {@link
227
246
  * ./post-synth.ts!PostSynthDiagnostic}'s doc for why), so they always pass
228
247
  * `config.rules` directly.
248
+ *
249
+ * `catalog` (chant #2113) is optional and off by default: pass a resolved
250
+ * audit catalog to also honor a `rules` key that names one of `id`'s
251
+ * `RuleMeta.aliases` rather than `id` itself. A project's
252
+ * `lint.rules: { OLD_ID: "off" }` then keeps suppressing the rule after it
253
+ * is renamed/renumbered, instead of silently starting to match nothing. Every
254
+ * existing call site omits `catalog` and is unaffected; a caller that has one
255
+ * handy (an audit-shaped consumer, not `chant build`'s post-synth loop, which
256
+ * has no catalog readily resolved at that point) opts in by passing it.
229
257
  */
230
258
  export function resolveConfiguredSeverity(
231
259
  rules: Record<string, RuleConfig> | undefined,
232
260
  id: string,
233
261
  defaultSeverity: Severity,
262
+ catalog?: Record<string, RuleMeta>,
234
263
  ): ParsedRuleConfig {
235
264
  const configValue = rules?.[id];
236
- if (configValue === undefined) return { severity: defaultSeverity };
237
- return parseRuleConfig(configValue);
265
+ if (configValue !== undefined) return parseRuleConfig(configValue);
266
+ if (catalog && rules) {
267
+ for (const [key, value] of Object.entries(rules)) {
268
+ if (canonicalRuleId(key, catalog) === id) return parseRuleConfig(value);
269
+ }
270
+ }
271
+ return { severity: defaultSeverity };
238
272
  }
239
273
 
240
274
  /**
@@ -500,16 +534,30 @@ export interface PostSynthSeverityResult {
500
534
  * remove. Config severity (`"off"`) is the one suppression surface this can
501
535
  * offer uniformly; a check that wants a per-instance escape hatch can read
502
536
  * `ctx.env`/its own options to decide not to emit a diagnostic at all.
537
+ *
538
+ * chant #2111 adds a second, narrower surface next to this one:
539
+ * `./suppressions.ts`'s `applyInlineSuppressions` matches `diag.entity`
540
+ * against `ctx.entities` (a source-level map key every `PostSynthContext`
541
+ * carries) instead of a name in the synthesized output, so it sidesteps the
542
+ * exact mismatch this comment describes. It is opt-in per lexicon (an entity
543
+ * has to carry a `suppressions` field) and runs as its own pass, right after
544
+ * this one, in both `chant build` and `chant audit`. See that module's doc
545
+ * for the full reasoning.
546
+ *
547
+ * `catalog` (chant #2113, optional) passes straight through to
548
+ * `resolveConfiguredSeverity` above for alias resolution; see its doc for
549
+ * what that buys and why it's opt-in.
503
550
  */
504
551
  export function applyConfiguredSeverity(
505
552
  diagnostics: readonly PostSynthDiagnostic[],
506
553
  rules: Record<string, RuleConfig> | undefined,
554
+ catalog?: Record<string, RuleMeta>,
507
555
  ): PostSynthSeverityResult {
508
556
  const kept: PostSynthDiagnostic[] = [];
509
557
  const suppressed: PostSynthDiagnostic[] = [];
510
558
 
511
559
  for (const diag of diagnostics) {
512
- const resolved = resolveConfiguredSeverity(rules, diag.checkId, diag.severity);
560
+ const resolved = resolveConfiguredSeverity(rules, diag.checkId, diag.severity, catalog);
513
561
  if (resolved.severity === "off") {
514
562
  suppressed.push(diag);
515
563
  continue;
@@ -519,3 +567,64 @@ export function applyConfiguredSeverity(
519
567
 
520
568
  return { diagnostics: kept, suppressed };
521
569
  }
570
+
571
+ /**
572
+ * Result of applying a lexicon preset to a set of post-synth diagnostics.
573
+ * Shaped like {@link PostSynthSeverityResult} so a caller threading both
574
+ * through (a preset filter, then a severity override) handles them the same
575
+ * way. See `applyConfiguredPreset` below.
576
+ */
577
+ export interface PostSynthPresetResult {
578
+ /** Diagnostics whose check id the active preset enables (or that `rules` explicitly configures, see below). */
579
+ diagnostics: PostSynthDiagnostic[];
580
+ /** Diagnostics the active preset excluded, unaltered. Countable rather than silently dropped, mirroring `PostSynthSeverityResult.suppressed`. */
581
+ suppressed: PostSynthDiagnostic[];
582
+ }
583
+
584
+ /**
585
+ * Resolve `lint.presets`' name for one lexicon into the set of check ids that
586
+ * preset enables (chant #2113). `presets` is what a lexicon plugin's
587
+ * `lintPresets()` returns (`../lexicon.ts`). A lexicon that ships none
588
+ * passes `undefined` here, in which case this returns `undefined` and
589
+ * `applyConfiguredPreset` below becomes a no-op (every rule reports, exactly
590
+ * today's behavior for a lexicon this feature doesn't touch).
591
+ *
592
+ * An unrecognized preset name (a typo, or a name the lexicon doesn't define)
593
+ * also returns `undefined` rather than an empty set. Falling back to
594
+ * unfiltered is the safe failure direction; silently reporting nothing would
595
+ * not be.
596
+ */
597
+ export function resolvePresetIds(
598
+ presets: Record<string, string[]> | undefined,
599
+ presetName: string,
600
+ ): Set<string> | undefined {
601
+ const ids = presets?.[presetName];
602
+ return ids ? new Set(ids) : undefined;
603
+ }
604
+
605
+ /**
606
+ * Filter post-synth diagnostics down to an active preset's ids (chant
607
+ * #2113). `presetIds` is `resolvePresetIds`'s output. `undefined` disables
608
+ * filtering entirely (a lexicon with no presets, or an unresolved preset
609
+ * name), matching tflint's own precedence chain (`--only` / rule config,
610
+ * then preset, then `disabled_by_default`, `tflint-ruleset-terraform`'s
611
+ * config docs): an explicit `lint.rules` entry for a check id always keeps
612
+ * that id's findings, whether or not the active preset includes it, so a
613
+ * project can opt a single report-only rule back in under `recommended`
614
+ * without switching its whole lexicon to `all`.
615
+ */
616
+ export function applyConfiguredPreset(
617
+ diagnostics: readonly PostSynthDiagnostic[],
618
+ presetIds: Set<string> | undefined,
619
+ rules: Record<string, RuleConfig> | undefined,
620
+ ): PostSynthPresetResult {
621
+ if (!presetIds) return { diagnostics: [...diagnostics], suppressed: [] };
622
+
623
+ const kept: PostSynthDiagnostic[] = [];
624
+ const suppressed: PostSynthDiagnostic[] = [];
625
+ for (const diag of diagnostics) {
626
+ if (presetIds.has(diag.checkId) || rules?.[diag.checkId] !== undefined) kept.push(diag);
627
+ else suppressed.push(diag);
628
+ }
629
+ return { diagnostics: kept, suppressed };
630
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * chant #1138 — `evaluateProjectPolicies` (the `policyGate` Op step's entry
3
- * point, ../../lexicons/temporal/src/op/activities/policy.ts) applies
3
+ * point, ./op/activities/policy.ts) applies
4
4
  * `lint.rules` severity overrides to policy diagnostics the same way `chant
5
5
  * build` does (`../cli/commands/build.ts`), so a check `lint.rules` turns
6
6
  * "off" no longer gates an apply either — before this fix, only `chant
@@ -1,5 +1,7 @@
1
1
  import { describe, test, expect } from "vitest";
2
2
  import { runPostSynthChecks } from "./post-synth";
3
+ import { activityContract, type ActivityContract } from "../op/activity-contract";
4
+ import { z } from "zod";
3
5
  import type { PostSynthCheck, PostSynthContext } from "./post-synth";
4
6
 
5
7
  function createBuildResult(overrides: Partial<PostSynthContext["buildResult"]> = {}) {
@@ -131,6 +133,35 @@ describe("environment-aware checks (#201)", () => {
131
133
  });
132
134
  });
133
135
 
136
+ describe("cross-lexicon activity contracts (#2101)", () => {
137
+ // A check shaped like temporal's TMP012/TMP013: it reads the merged
138
+ // contract map the runner hands it, and behaves as it did before the field
139
+ // existed when the context carries none.
140
+ const readsContracts: PostSynthCheck = {
141
+ id: "CONTRACTS",
142
+ description: "reports the activity names the context carries contracts for",
143
+ check(ctx) {
144
+ const names = [...(ctx.activityContracts?.keys() ?? [])].sort();
145
+ return [{ checkId: "CONTRACTS", severity: "warning", message: names.join(",") }];
146
+ },
147
+ };
148
+
149
+ test("the contract map is threaded into the context", () => {
150
+ const contracts = new Map<string, ActivityContract>([
151
+ ["terraformPlan", activityContract("terraformPlan", z.strictObject({ root: z.string() }))],
152
+ ]);
153
+ const diags = runPostSynthChecks([readsContracts], createBuildResult(), undefined, {
154
+ activityContracts: contracts,
155
+ });
156
+ expect(diags[0].message).toBe("terraformPlan");
157
+ });
158
+
159
+ test("a run with no contracts leaves the field undefined, not an empty map", () => {
160
+ const diags = runPostSynthChecks([readsContracts], createBuildResult());
161
+ expect(diags[0].message).toBe("");
162
+ });
163
+ });
164
+
134
165
  describe("isPostSynthCheck", () => {
135
166
  test("accepts a well-formed check and rejects others", async () => {
136
167
  const { isPostSynthCheck } = await import("./post-synth");
@@ -1,4 +1,5 @@
1
1
  import type { Declarable } from "../declarable";
2
+ import type { ActivityContract } from "../op/activity-contract";
2
3
  import type { SerializerResult } from "../serializer";
3
4
  import type { Severity } from "./rule";
4
5
  import { parseOutputDocs, type OutputDoc } from "./output-docs";
@@ -38,6 +39,22 @@ export interface PostSynthContext {
38
39
  * with `ctx.docs ?? []` only when a context's provenance is unknown.
39
40
  */
40
41
  readonly docs?: OutputDoc[];
42
+ /**
43
+ * Activity contracts resolved across every lexicon this build configured
44
+ * (chant #2101), keyed by activity name — core's own plus whatever each
45
+ * lexicon contributes at `@intentius/chant-lexicon-<name>/op/activity-contracts`
46
+ * or through its plugin. Filled in by `chant build`, `chant lint` and
47
+ * `check-lexicon`'s example harness via `loadActivityContracts`.
48
+ *
49
+ * The checks that validate Op steps against contracts (OPS012 and OPS013,
50
+ * `./rules/op/`) merge this over their own statically imported table with
51
+ * `mergeActivityContracts`, so an Op calling `terraformPlan` or
52
+ * `k3sInstall` validates against the contract the owning lexicon declared
53
+ * instead of failing for the absence of one. Optional, and absent from a
54
+ * hand-built context: a check must behave as it did before this existed
55
+ * when it is `undefined`.
56
+ */
57
+ readonly activityContracts?: ReadonlyMap<string, ActivityContract>;
41
58
  /** Raw build result object */
42
59
  buildResult: {
43
60
  outputs: Map<string, string | SerializerResult>;
@@ -104,6 +121,14 @@ export function getAdditionalFiles(output: string | SerializerResult): Record<st
104
121
  * comment (`chant-disable`) suppression is out of scope for post-synth
105
122
  * findings — see `./config.ts`'s `applyConfiguredSeverity` doc for the full
106
123
  * reasoning and what suppression surface post-synth findings get instead.
124
+ *
125
+ * chant #2111 finds a narrower anchor that does generalize: `./suppressions.ts`'s
126
+ * `applyInlineSuppressions` matches `entity` against `ctx.entities` (a map key
127
+ * every context already carries, source-level, before serialization), not
128
+ * against a name in the synthesized OUTPUT. A lexicon whose entities carry a
129
+ * `suppressions` field (see that file's module doc) gets inline `# chant-
130
+ * ignore`-style comments this way; one that doesn't is unaffected, and this
131
+ * interface's own contract (no `file`/`line`) is unchanged either way.
107
132
  */
108
133
  export interface PostSynthDiagnostic {
109
134
  /** ID of the check that produced this diagnostic */
@@ -120,6 +145,30 @@ export interface PostSynthDiagnostic {
120
145
  entity?: string;
121
146
  /** Optional lexicon related to this diagnostic */
122
147
  lexicon?: string;
148
+ /**
149
+ * Set when the finding is that something does not exist, rather than that
150
+ * something present is wrong (chant #2113). TF001 (no remote backend) is
151
+ * the motivating case: there is no resource to set `entity` to, only the
152
+ * scope that is missing one. Snyk's policy-engine spec names this the
153
+ * "missing-resource" archetype (a `deny` whose `info` carries a
154
+ * `resource_type` instead of a `resource`, because there is nothing to
155
+ * attach to) and is the only tool in chant's TF-family survey with a
156
+ * first-class shape for it; trivy's own inline-ignore comments admit they
157
+ * cannot suppress an absence finding for exactly this reason. `kind` names
158
+ * what is missing (a `backend`/`cloud` block, a required resource type);
159
+ * `scope` names where it is missing (a root module name, a file). Renders
160
+ * distinctly in all three reporters (stylish/JSON/SARIF, `../cli/commands/
161
+ * audit.ts` and `../audit/report-model.ts`) instead of falling back to
162
+ * `entity`, and gives #2111's HCL suppression a block-anchored key to
163
+ * suppress an absence finding by (the block that *should* declare `kind`
164
+ * inside `scope`), independent of whatever `entity` this diagnostic sets.
165
+ */
166
+ missing?: {
167
+ /** What kind of thing is absent (e.g. `"backend"`, `"cloud"`). */
168
+ kind: string;
169
+ /** Where it is missing from (e.g. a root module name). */
170
+ scope: string;
171
+ };
123
172
  }
124
173
 
125
174
  /**
@@ -149,12 +198,15 @@ export function isPostSynthCheck(value: unknown): value is PostSynthCheck {
149
198
 
150
199
  /**
151
200
  * Run a set of post-synthesis checks against a build result. `env` is threaded
152
- * into the context so a check can branch on the current environment/stack.
201
+ * into the context so a check can branch on the current environment/stack, and
202
+ * `opts.activityContracts` (chant #2101) carries the build's cross-lexicon
203
+ * activity-contract map to the checks that validate Op steps against it.
153
204
  */
154
205
  export function runPostSynthChecks(
155
206
  checks: PostSynthCheck[],
156
207
  buildResult: PostSynthContext["buildResult"],
157
208
  env?: string,
209
+ opts?: { activityContracts?: ReadonlyMap<string, ActivityContract> },
158
210
  ): PostSynthDiagnostic[] {
159
211
  const getDocs = createDocsAccessor(buildResult.outputs);
160
212
  const ctx: PostSynthContext = {
@@ -162,6 +214,7 @@ export function runPostSynthChecks(
162
214
  entities: buildResult.entities,
163
215
  env,
164
216
  buildResult,
217
+ ...(opts?.activityContracts ? { activityContracts: opts.activityContracts } : {}),
165
218
  get docs(): OutputDoc[] {
166
219
  return getDocs();
167
220
  },
@@ -1,8 +1,8 @@
1
- // chant-disable COMP004 -- graduates to `chant run --temporal`; the Node 1 gate is a deliberate human-approval wait, see docs/components/orchestration.mdx
1
+ // chant-disable COMP004 -- the Node 1 gate is a deliberate human-approval wait, cleared with `chant approve neo4j-cluster approve-neo4j-node-1`
2
2
  import type { Component } from "../../../../../../components/component";
3
3
  import { gate, phase } from "../../../../../../components/component";
4
4
 
5
- /** COMP004 pass case: a genuine gate, opted out via a file-level disable directive with a documented reason — the intended path for a component that graduates to the Temporal backend. */
5
+ /** COMP004 pass case: a genuine gate, opted out via a file-level disable directive with a documented reason — the intended path for a component whose rollout genuinely waits on a person. */
6
6
  export const neo4jCluster: Component = {
7
7
  name: "neo4j-cluster",
8
8
  archetype: "infra",
@@ -1,7 +1,7 @@
1
1
  import type { Component } from "../../../../../../components/component";
2
2
  import { phase } from "../../../../../../components/component";
3
3
 
4
- /** COMP004 pass case: no gate step anywhere — nothing requires the durable backend. */
4
+ /** COMP004 pass case: no gate step anywhere — nothing here stops the run for a person. */
5
5
  export const ordersTable: Component = {
6
6
  name: "orders-table",
7
7
  archetype: "infra",