@intentius/chant 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (558) hide show
  1. package/dist/audit/catalog.d.ts +51 -1
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +36 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +26 -9
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/lineage.d.ts +10 -0
  8. package/dist/audit/lineage.d.ts.map +1 -0
  9. package/dist/audit/prior-art.d.ts +385 -0
  10. package/dist/audit/prior-art.d.ts.map +1 -0
  11. package/dist/audit/proof.d.ts +16 -0
  12. package/dist/audit/proof.d.ts.map +1 -1
  13. package/dist/audit/report-html.d.ts.map +1 -1
  14. package/dist/audit/report-model.d.ts +8 -1
  15. package/dist/audit/report-model.d.ts.map +1 -1
  16. package/dist/audit/report.d.ts.map +1 -1
  17. package/dist/audit/rules-doc.d.ts +3 -0
  18. package/dist/audit/rules-doc.d.ts.map +1 -1
  19. package/dist/audit/secrets.d.ts +15 -0
  20. package/dist/audit/secrets.d.ts.map +1 -1
  21. package/dist/audit/terraform-state.d.ts +46 -0
  22. package/dist/audit/terraform-state.d.ts.map +1 -0
  23. package/dist/build.d.ts +17 -1
  24. package/dist/build.d.ts.map +1 -1
  25. package/dist/claimed-fields.d.ts +161 -0
  26. package/dist/claimed-fields.d.ts.map +1 -0
  27. package/dist/cli/build-params-cli.d.ts +2 -2
  28. package/dist/cli/command-group.d.ts +14 -0
  29. package/dist/cli/command-group.d.ts.map +1 -1
  30. package/dist/cli/commands/audit.d.ts +17 -1
  31. package/dist/cli/commands/audit.d.ts.map +1 -1
  32. package/dist/cli/commands/build.d.ts.map +1 -1
  33. package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
  34. package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
  35. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  36. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
  38. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
  40. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  41. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  42. package/dist/cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/commands/lint.d.ts +13 -0
  44. package/dist/cli/commands/lint.d.ts.map +1 -1
  45. package/dist/cli/commands/onboard.d.ts +2 -0
  46. package/dist/cli/commands/onboard.d.ts.map +1 -1
  47. package/dist/cli/handlers/build.d.ts.map +1 -1
  48. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  49. package/dist/cli/handlers/operator.d.ts +82 -16
  50. package/dist/cli/handlers/operator.d.ts.map +1 -1
  51. package/dist/cli/handlers/run.d.ts +49 -42
  52. package/dist/cli/handlers/run.d.ts.map +1 -1
  53. package/dist/cli/main.d.ts +12 -1
  54. package/dist/cli/main.d.ts.map +1 -1
  55. package/dist/cli/mcp/op-tools.d.ts +16 -1
  56. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  57. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  58. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  59. package/dist/cli/plugins.d.ts +22 -0
  60. package/dist/cli/plugins.d.ts.map +1 -1
  61. package/dist/cli/registry.d.ts +40 -9
  62. package/dist/cli/registry.d.ts.map +1 -1
  63. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  64. package/dist/components/auto-release.d.ts +10 -20
  65. package/dist/components/auto-release.d.ts.map +1 -1
  66. package/dist/components/capability.d.ts +4 -4
  67. package/dist/components/cli-support.d.ts +24 -22
  68. package/dist/components/cli-support.d.ts.map +1 -1
  69. package/dist/components/component.d.ts +21 -6
  70. package/dist/components/component.d.ts.map +1 -1
  71. package/dist/components/driver-output.d.ts.map +1 -1
  72. package/dist/components/driver.d.ts +73 -37
  73. package/dist/components/driver.d.ts.map +1 -1
  74. package/dist/components/index.d.ts +1 -2
  75. package/dist/components/index.d.ts.map +1 -1
  76. package/dist/components/manifest-persistence.d.ts +1 -9
  77. package/dist/components/manifest-persistence.d.ts.map +1 -1
  78. package/dist/components/run-progress.d.ts +6 -6
  79. package/dist/components/run-progress.d.ts.map +1 -1
  80. package/dist/components/verbs/run-agent.d.ts +10 -22
  81. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  82. package/dist/components/verbs/verify.d.ts +2 -2
  83. package/dist/deep-observation.d.ts +17 -0
  84. package/dist/deep-observation.d.ts.map +1 -1
  85. package/dist/detectLexicon.d.ts +13 -0
  86. package/dist/detectLexicon.d.ts.map +1 -1
  87. package/dist/discovery/files.d.ts +4 -4
  88. package/dist/discovery/fold-import.d.ts.map +1 -1
  89. package/dist/discovery/param-deps.d.ts +33 -0
  90. package/dist/discovery/param-deps.d.ts.map +1 -1
  91. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  92. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  93. package/dist/fold/foldable-helpers.d.ts +5 -1
  94. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  95. package/dist/fold-provenance.d.ts +236 -0
  96. package/dist/fold-provenance.d.ts.map +1 -0
  97. package/dist/graph-ops.d.ts +1 -1
  98. package/dist/held-elsewhere.d.ts +85 -0
  99. package/dist/held-elsewhere.d.ts.map +1 -0
  100. package/dist/index.d.ts +3 -0
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/kubectl-context.d.ts +3 -3
  103. package/dist/lexicon-config.d.ts +4 -4
  104. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  105. package/dist/lexicon.d.ts +273 -16
  106. package/dist/lexicon.d.ts.map +1 -1
  107. package/dist/lifecycle/change-set.d.ts +13 -0
  108. package/dist/lifecycle/change-set.d.ts.map +1 -1
  109. package/dist/lifecycle/deep-diff.d.ts +133 -6
  110. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  111. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  112. package/dist/lifecycle/gate-ledger.d.ts +79 -3
  113. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  114. package/dist/lifecycle/index.d.ts +1 -0
  115. package/dist/lifecycle/index.d.ts.map +1 -1
  116. package/dist/lifecycle/release-ledger.d.ts +2 -2
  117. package/dist/lifecycle/run-ledger.d.ts +53 -0
  118. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  119. package/dist/lifecycle/symptoms.d.ts +3 -3
  120. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  121. package/dist/lint/config.d.ts +81 -2
  122. package/dist/lint/config.d.ts.map +1 -1
  123. package/dist/lint/post-synth.d.ts +55 -2
  124. package/dist/lint/post-synth.d.ts.map +1 -1
  125. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  126. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  127. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  128. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  129. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  130. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  131. package/dist/lint/rules/comp/index.d.ts +1 -1
  132. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  133. package/dist/lint/rules/op/index.d.ts +16 -0
  134. package/dist/lint/rules/op/index.d.ts.map +1 -0
  135. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  136. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  137. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  138. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  139. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  140. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  141. package/dist/lint/rules/op/support.d.ts +12 -0
  142. package/dist/lint/rules/op/support.d.ts.map +1 -0
  143. package/dist/lint/suppressions.d.ts +108 -0
  144. package/dist/lint/suppressions.d.ts.map +1 -0
  145. package/dist/live-endpoint.d.ts +1 -1
  146. package/dist/op/activities/activity-contracts.d.ts +54 -0
  147. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  148. package/dist/op/activities/apply.d.ts +262 -0
  149. package/dist/op/activities/apply.d.ts.map +1 -0
  150. package/dist/op/activities/build.d.ts +13 -0
  151. package/dist/op/activities/build.d.ts.map +1 -0
  152. package/dist/op/activities/converge.d.ts +110 -0
  153. package/dist/op/activities/converge.d.ts.map +1 -0
  154. package/dist/op/activities/env-teardown.d.ts +76 -0
  155. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  156. package/dist/op/activities/guard-validate.d.ts +64 -0
  157. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  158. package/dist/op/activities/http-check.d.ts +34 -0
  159. package/dist/op/activities/http-check.d.ts.map +1 -0
  160. package/dist/op/activities/index.d.ts +31 -0
  161. package/dist/op/activities/index.d.ts.map +1 -0
  162. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  163. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  164. package/dist/op/activities/lifecycle.d.ts +42 -0
  165. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  166. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  167. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  168. package/dist/op/activities/policy.d.ts +17 -0
  169. package/dist/op/activities/policy.d.ts.map +1 -0
  170. package/dist/op/activities/reconcile.d.ts +149 -0
  171. package/dist/op/activities/reconcile.d.ts.map +1 -0
  172. package/dist/op/activities/shell.d.ts +13 -0
  173. package/dist/op/activities/shell.d.ts.map +1 -0
  174. package/dist/op/activities/teardown.d.ts +10 -0
  175. package/dist/op/activities/teardown.d.ts.map +1 -0
  176. package/dist/op/activities/util.d.ts +7 -0
  177. package/dist/op/activities/util.d.ts.map +1 -0
  178. package/dist/op/activities/wait.d.ts +28 -0
  179. package/dist/op/activities/wait.d.ts.map +1 -0
  180. package/dist/op/activities/workflow-audit.d.ts +92 -0
  181. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  182. package/dist/op/activity-contract-registry.d.ts +76 -0
  183. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  184. package/dist/op/activity-contract.d.ts +25 -18
  185. package/dist/op/activity-contract.d.ts.map +1 -1
  186. package/dist/op/activity-failure.d.ts +28 -0
  187. package/dist/op/activity-failure.d.ts.map +1 -0
  188. package/dist/op/activity-profiles.d.ts +118 -0
  189. package/dist/op/activity-profiles.d.ts.map +1 -0
  190. package/dist/op/activity-registry.d.ts +22 -38
  191. package/dist/op/activity-registry.d.ts.map +1 -1
  192. package/dist/op/activity-runtime.d.ts +3 -14
  193. package/dist/op/activity-runtime.d.ts.map +1 -1
  194. package/dist/op/builders.d.ts +85 -30
  195. package/dist/op/builders.d.ts.map +1 -1
  196. package/dist/op/change-signal.d.ts +91 -0
  197. package/dist/op/change-signal.d.ts.map +1 -0
  198. package/dist/op/composites/apply-op.d.ts +118 -0
  199. package/dist/op/composites/apply-op.d.ts.map +1 -0
  200. package/dist/op/composites/converge-op.d.ts +120 -0
  201. package/dist/op/composites/converge-op.d.ts.map +1 -0
  202. package/dist/op/composites/index.d.ts +26 -0
  203. package/dist/op/composites/index.d.ts.map +1 -0
  204. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  205. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  206. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  207. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  208. package/dist/op/composites/reconcile-op.d.ts +57 -0
  209. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  210. package/dist/op/composites/watch-op.d.ts +60 -0
  211. package/dist/op/composites/watch-op.d.ts.map +1 -0
  212. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  213. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  214. package/dist/op/converge-rule.d.ts +12 -10
  215. package/dist/op/converge-rule.d.ts.map +1 -1
  216. package/dist/op/cron.d.ts +51 -0
  217. package/dist/op/cron.d.ts.map +1 -0
  218. package/dist/op/discover.d.ts +9 -0
  219. package/dist/op/discover.d.ts.map +1 -1
  220. package/dist/op/duration.d.ts +11 -0
  221. package/dist/op/duration.d.ts.map +1 -0
  222. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  223. package/dist/op/gate-name.d.ts +40 -0
  224. package/dist/op/gate-name.d.ts.map +1 -0
  225. package/dist/op/gate-summary.d.ts +43 -0
  226. package/dist/op/gate-summary.d.ts.map +1 -0
  227. package/dist/op/gate.d.ts +90 -0
  228. package/dist/op/gate.d.ts.map +1 -0
  229. package/dist/op/generate-pipeline.d.ts +9 -0
  230. package/dist/op/generate-pipeline.d.ts.map +1 -1
  231. package/dist/op/index.d.ts +29 -9
  232. package/dist/op/index.d.ts.map +1 -1
  233. package/dist/op/local-executor.d.ts +114 -28
  234. package/dist/op/local-executor.d.ts.map +1 -1
  235. package/dist/op/local-output.d.ts +11 -1
  236. package/dist/op/local-output.d.ts.map +1 -1
  237. package/dist/op/op-ir.d.ts +185 -0
  238. package/dist/op/op-ir.d.ts.map +1 -0
  239. package/dist/op/op-verb-class.d.ts +10 -2
  240. package/dist/op/op-verb-class.d.ts.map +1 -1
  241. package/dist/op/operator.d.ts +93 -5
  242. package/dist/op/operator.d.ts.map +1 -1
  243. package/dist/op/receipt-store.d.ts +1 -1
  244. package/dist/op/receipt-store.d.ts.map +1 -1
  245. package/dist/op/resource.d.ts +15 -1
  246. package/dist/op/resource.d.ts.map +1 -1
  247. package/dist/op/runtime.d.ts +200 -0
  248. package/dist/op/runtime.d.ts.map +1 -0
  249. package/dist/op/runtimes/local.d.ts +29 -0
  250. package/dist/op/runtimes/local.d.ts.map +1 -0
  251. package/dist/op/step-output-ref.d.ts +15 -13
  252. package/dist/op/step-output-ref.d.ts.map +1 -1
  253. package/dist/op/types.d.ts +96 -29
  254. package/dist/op/types.d.ts.map +1 -1
  255. package/dist/ownership.d.ts +1 -1
  256. package/dist/provenance.d.ts +21 -0
  257. package/dist/provenance.d.ts.map +1 -1
  258. package/dist/serializer-walker.d.ts.map +1 -1
  259. package/dist/terraform/bridge.d.ts +26 -7
  260. package/dist/terraform/bridge.d.ts.map +1 -1
  261. package/dist/terraform/carve-provider.d.ts +28 -2
  262. package/dist/terraform/carve-provider.d.ts.map +1 -1
  263. package/dist/terraform/data-source-shape.d.ts +77 -0
  264. package/dist/terraform/data-source-shape.d.ts.map +1 -0
  265. package/dist/terraform/graph.d.ts.map +1 -1
  266. package/dist/terraform/providers/kubernetes.d.ts +5 -0
  267. package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
  268. package/dist/terraform/tier-map.d.ts +16 -4
  269. package/dist/terraform/tier-map.d.ts.map +1 -1
  270. package/dist/terraform/types.d.ts +8 -0
  271. package/dist/terraform/types.d.ts.map +1 -1
  272. package/package.json +6 -1
  273. package/src/audit/catalog.test.ts +79 -3
  274. package/src/audit/catalog.ts +111 -13
  275. package/src/audit/core.test.ts +26 -0
  276. package/src/audit/core.ts +84 -18
  277. package/src/audit/discover.test.ts +39 -0
  278. package/src/audit/discover.ts +202 -18
  279. package/src/audit/lineage.ts +100 -0
  280. package/src/audit/prior-art.ts +153 -0
  281. package/src/audit/proof.test.ts +67 -0
  282. package/src/audit/proof.ts +70 -0
  283. package/src/audit/report-html.ts +9 -4
  284. package/src/audit/report-model.ts +16 -2
  285. package/src/audit/report.test.ts +33 -0
  286. package/src/audit/report.ts +5 -3
  287. package/src/audit/rules-doc.test.ts +34 -2
  288. package/src/audit/rules-doc.ts +82 -9
  289. package/src/audit/secrets.ts +15 -3
  290. package/src/audit/terraform-state.test.ts +144 -0
  291. package/src/audit/terraform-state.ts +131 -0
  292. package/src/build.ts +31 -1
  293. package/src/claimed-fields.test.ts +145 -0
  294. package/src/claimed-fields.ts +208 -0
  295. package/src/cli/build-params-cli.ts +2 -2
  296. package/src/cli/command-group.ts +14 -0
  297. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  298. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  299. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  300. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  301. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  302. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  303. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  304. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  305. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  306. package/src/cli/commands/audit.test.ts +191 -11
  307. package/src/cli/commands/audit.ts +78 -28
  308. package/src/cli/commands/build.test.ts +330 -9
  309. package/src/cli/commands/build.ts +170 -24
  310. package/src/cli/commands/carve-bridge.test.ts +237 -14
  311. package/src/cli/commands/carve-bridge.ts +21 -17
  312. package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
  313. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  314. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  315. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  316. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  317. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  318. package/src/cli/commands/init-lexicon.test.ts +97 -2
  319. package/src/cli/commands/init-lexicon.ts +4 -2
  320. package/src/cli/commands/init.test.ts +35 -1
  321. package/src/cli/commands/init.ts +14 -3
  322. package/src/cli/commands/lint.test.ts +194 -1
  323. package/src/cli/commands/lint.ts +205 -7
  324. package/src/cli/commands/onboard.test.ts +75 -0
  325. package/src/cli/commands/onboard.ts +54 -23
  326. package/src/cli/handlers/build.test.ts +81 -1
  327. package/src/cli/handlers/build.ts +15 -1
  328. package/src/cli/handlers/graph.test.ts +3 -3
  329. package/src/cli/handlers/lifecycle.test.ts +195 -12
  330. package/src/cli/handlers/lifecycle.ts +123 -6
  331. package/src/cli/handlers/misc.ts +1 -1
  332. package/src/cli/handlers/operator.test.ts +183 -11
  333. package/src/cli/handlers/operator.ts +341 -55
  334. package/src/cli/handlers/run.test.ts +568 -1295
  335. package/src/cli/handlers/run.ts +446 -912
  336. package/src/cli/main.test.ts +22 -16
  337. package/src/cli/main.ts +66 -33
  338. package/src/cli/mcp/op-tools.ts +147 -88
  339. package/src/cli/mcp/resource-handlers.ts +10 -37
  340. package/src/cli/mcp/resources/context.ts +11 -12
  341. package/src/cli/mcp/server.test.ts +25 -27
  342. package/src/cli/mcp/server.ts +2 -2
  343. package/src/cli/plugins.test.ts +68 -2
  344. package/src/cli/plugins.ts +39 -0
  345. package/src/cli/registry.ts +40 -9
  346. package/src/codegen/docs.ts +1 -1
  347. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  348. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  349. package/src/components/README.md +4 -3
  350. package/src/components/__fixtures__/neo4j-fanout.json +1 -1
  351. package/src/components/auto-release.test.ts +4 -20
  352. package/src/components/auto-release.ts +17 -42
  353. package/src/components/capability.ts +4 -4
  354. package/src/components/cli-support.test.ts +28 -12
  355. package/src/components/cli-support.ts +60 -47
  356. package/src/components/component-schema.test.ts +18 -2
  357. package/src/components/component.schema.json +18 -5
  358. package/src/components/component.test.ts +2 -2
  359. package/src/components/component.ts +27 -7
  360. package/src/components/config-defaults.test.ts +2 -2
  361. package/src/components/driver-output.ts +14 -2
  362. package/src/components/driver.test.ts +109 -20
  363. package/src/components/driver.ts +201 -84
  364. package/src/components/index.ts +1 -6
  365. package/src/components/manifest-persistence.test.ts +2 -20
  366. package/src/components/manifest-persistence.ts +4 -24
  367. package/src/components/pilots/neo4j-fanout.pilot.ts +3 -3
  368. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  369. package/src/components/run-progress.ts +6 -6
  370. package/src/components/verbs/run-agent.test.ts +19 -0
  371. package/src/components/verbs/run-agent.ts +12 -24
  372. package/src/components/verbs/verify.ts +2 -2
  373. package/src/deep-observation.test.ts +75 -0
  374. package/src/deep-observation.ts +48 -0
  375. package/src/detectLexicon.ts +18 -1
  376. package/src/discovery/collect.test.ts +3 -3
  377. package/src/discovery/files.ts +4 -4
  378. package/src/discovery/fold-import.test.ts +1 -1
  379. package/src/discovery/fold-import.ts +105 -2
  380. package/src/discovery/param-deps.test.ts +112 -1
  381. package/src/discovery/param-deps.ts +193 -0
  382. package/src/discovery/sandbox/bundle.ts +15 -1
  383. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  384. package/src/discovery/sandbox/config-wire.ts +2 -2
  385. package/src/fold/fold.test.ts +56 -0
  386. package/src/fold/foldable-helpers.ts +82 -2
  387. package/src/fold-provenance-exclusion.test.ts +186 -0
  388. package/src/fold-provenance.test.ts +495 -0
  389. package/src/fold-provenance.ts +441 -0
  390. package/src/graph-ops.test.ts +3 -3
  391. package/src/graph-ops.ts +1 -1
  392. package/src/held-elsewhere.test.ts +102 -0
  393. package/src/held-elsewhere.ts +114 -0
  394. package/src/index.ts +3 -0
  395. package/src/kubectl-context.ts +3 -3
  396. package/src/lexicon-config.test.ts +5 -5
  397. package/src/lexicon-config.ts +4 -4
  398. package/src/lexicon-plugin-helpers.ts +1 -1
  399. package/src/lexicon.ts +296 -16
  400. package/src/lifecycle/change-set.test.ts +49 -0
  401. package/src/lifecycle/change-set.ts +67 -0
  402. package/src/lifecycle/converge-ledger.ts +1 -1
  403. package/src/lifecycle/deep-diff.test.ts +267 -14
  404. package/src/lifecycle/deep-diff.ts +217 -14
  405. package/src/lifecycle/deep-observe.test.ts +3 -1
  406. package/src/lifecycle/deep-observe.ts +16 -6
  407. package/src/lifecycle/gate-ledger.test.ts +74 -2
  408. package/src/lifecycle/gate-ledger.ts +154 -36
  409. package/src/lifecycle/index.ts +1 -0
  410. package/src/lifecycle/release-ledger.ts +2 -2
  411. package/src/lifecycle/run-ledger.test.ts +268 -0
  412. package/src/lifecycle/run-ledger.ts +201 -0
  413. package/src/lifecycle/symptoms.ts +3 -3
  414. package/src/lint/config.test.ts +82 -1
  415. package/src/lint/config.ts +112 -3
  416. package/src/lint/pipeline-change-gate.test.ts +2 -2
  417. package/src/lint/policy.test.ts +1 -1
  418. package/src/lint/post-synth.test.ts +31 -0
  419. package/src/lint/post-synth.ts +54 -1
  420. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  421. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  422. package/src/lint/rules/comp/comp.test.ts +27 -1
  423. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -0
  424. package/src/lint/rules/comp/index.ts +3 -3
  425. package/src/lint/rules/op/index.ts +23 -0
  426. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  427. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  428. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  429. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  430. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  431. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  432. package/src/lint/rules/op/support.ts +11 -0
  433. package/src/lint/suppressions.test.ts +163 -0
  434. package/src/lint/suppressions.ts +204 -0
  435. package/src/live-endpoint.ts +1 -1
  436. package/src/op/activities/activity-contracts.ts +66 -0
  437. package/src/op/activities/apply.test.ts +567 -0
  438. package/src/op/activities/apply.ts +662 -0
  439. package/src/op/activities/build.ts +26 -0
  440. package/src/op/activities/converge.test.ts +330 -0
  441. package/src/op/activities/converge.ts +449 -0
  442. package/src/op/activities/env-teardown.test.ts +187 -0
  443. package/src/op/activities/env-teardown.ts +171 -0
  444. package/src/op/activities/guard-validate.test.ts +139 -0
  445. package/src/op/activities/guard-validate.ts +223 -0
  446. package/src/op/activities/http-check.test.ts +48 -0
  447. package/src/op/activities/http-check.ts +55 -0
  448. package/src/op/activities/index.ts +119 -0
  449. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  450. package/src/op/activities/lexicon-upgrade.ts +673 -0
  451. package/src/op/activities/lifecycle.ts +85 -0
  452. package/src/op/activities/pipeline-audit.test.ts +67 -0
  453. package/src/op/activities/pipeline-audit.ts +201 -0
  454. package/src/op/activities/policy.test.ts +19 -0
  455. package/src/op/activities/policy.ts +37 -0
  456. package/src/op/activities/reconcile.test.ts +161 -0
  457. package/src/op/activities/reconcile.ts +354 -0
  458. package/src/op/activities/shell.ts +26 -0
  459. package/src/op/activities/teardown.ts +22 -0
  460. package/src/op/activities/util.ts +6 -0
  461. package/src/op/activities/wait.ts +69 -0
  462. package/src/op/activities/workflow-audit.test.ts +90 -0
  463. package/src/op/activities/workflow-audit.ts +203 -0
  464. package/src/op/activity-contract-registry.test.ts +98 -0
  465. package/src/op/activity-contract-registry.ts +114 -0
  466. package/src/op/activity-contract.test.ts +1 -1
  467. package/src/op/activity-contract.ts +37 -33
  468. package/src/op/activity-failure.ts +34 -0
  469. package/src/op/activity-profiles.test.ts +56 -0
  470. package/src/op/activity-profiles.ts +116 -0
  471. package/src/op/activity-registry.test.ts +37 -28
  472. package/src/op/activity-registry.ts +27 -56
  473. package/src/op/activity-runtime.ts +3 -47
  474. package/src/op/builders-exports.test.ts +0 -49
  475. package/src/op/builders-typed.test.ts +164 -0
  476. package/src/op/builders.ts +125 -41
  477. package/src/op/change-signal.test.ts +117 -0
  478. package/src/op/change-signal.ts +169 -0
  479. package/src/op/composites/apply-op.ts +218 -0
  480. package/src/op/composites/composites.test.ts +379 -0
  481. package/src/op/composites/converge-op.test.ts +168 -0
  482. package/src/op/composites/converge-op.ts +214 -0
  483. package/src/op/composites/index.ts +26 -0
  484. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  485. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  486. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  487. package/src/op/composites/pipeline-audit-op.ts +78 -0
  488. package/src/op/composites/reconcile-op.test.ts +51 -0
  489. package/src/op/composites/reconcile-op.ts +110 -0
  490. package/src/op/composites/watch-op.ts +108 -0
  491. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  492. package/src/op/composites/workflow-audit-op.ts +88 -0
  493. package/src/op/converge-rule.ts +12 -10
  494. package/src/op/cron.test.ts +128 -0
  495. package/src/op/cron.ts +149 -0
  496. package/src/op/discover-root.test.ts +105 -1
  497. package/src/op/discover.test.ts +6 -2
  498. package/src/op/discover.ts +84 -16
  499. package/src/op/duration.ts +21 -0
  500. package/src/op/effect-step.test.ts +29 -14
  501. package/src/op/emulator-lifecycle.ts +1 -2
  502. package/src/op/gate-name.test.ts +65 -0
  503. package/src/op/gate-name.ts +60 -0
  504. package/src/op/gate-summary.ts +84 -0
  505. package/src/op/gate.ts +155 -0
  506. package/src/op/generate-pipeline.test.ts +65 -3
  507. package/src/op/generate-pipeline.ts +19 -2
  508. package/src/op/index.ts +48 -8
  509. package/src/op/local-executor.test.ts +178 -34
  510. package/src/op/local-executor.ts +371 -102
  511. package/src/op/local-output.test.ts +95 -11
  512. package/src/op/local-output.ts +33 -4
  513. package/src/op/op-ir.test.ts +292 -0
  514. package/src/op/op-ir.ts +419 -0
  515. package/src/op/op-verb-class.test.ts +2 -2
  516. package/src/op/op-verb-class.ts +19 -5
  517. package/src/op/op.test.ts +28 -7
  518. package/src/op/operator.test.ts +543 -6
  519. package/src/op/operator.ts +203 -20
  520. package/src/op/receipt-store.ts +1 -1
  521. package/src/op/resource.ts +20 -2
  522. package/src/op/runtime.ts +203 -0
  523. package/src/op/runtimes/local.test.ts +181 -0
  524. package/src/op/runtimes/local.ts +212 -0
  525. package/src/op/step-output-ref.ts +15 -13
  526. package/src/op/types.ts +106 -26
  527. package/src/ownership.ts +1 -1
  528. package/src/provenance.test.ts +6 -0
  529. package/src/provenance.ts +21 -1
  530. package/src/serializer-walker.test.ts +42 -0
  531. package/src/serializer-walker.ts +21 -2
  532. package/src/terraform/aws-resources.test.ts +13 -4
  533. package/src/terraform/bridge.test.ts +22 -9
  534. package/src/terraform/bridge.ts +89 -28
  535. package/src/terraform/carve-provider.ts +38 -2
  536. package/src/terraform/data-source-shape.ts +95 -0
  537. package/src/terraform/graph.ts +35 -7
  538. package/src/terraform/providers/kubernetes.ts +48 -2
  539. package/src/terraform/tier-map.ts +21 -6
  540. package/src/terraform/types.ts +8 -0
  541. package/dist/cli/handlers/op-progress.d.ts +0 -90
  542. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  543. package/dist/cli/handlers/run-client.d.ts +0 -127
  544. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  545. package/dist/cli/handlers/run-report.d.ts +0 -5
  546. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  547. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  548. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  549. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  550. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  551. package/src/cli/handlers/op-progress.test.ts +0 -268
  552. package/src/cli/handlers/op-progress.ts +0 -248
  553. package/src/cli/handlers/run-client.test.ts +0 -82
  554. package/src/cli/handlers/run-client.ts +0 -219
  555. package/src/cli/handlers/run-report.test.ts +0 -62
  556. package/src/cli/handlers/run-report.ts +0 -122
  557. package/src/components/temporal-codegen-loader.ts +0 -41
  558. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -0,0 +1,98 @@
1
+ /**
2
+ * OPS013: a step-output reference (`stepOutput()`/`activity().out`, chant
3
+ * #1290) must name a step that precedes it, in scope (main `phases`, not
4
+ * `onFailure` or nested inside an `EffectStep`), with a registered
5
+ * `ActivityContract` whose `returns` schema the referenced path resolves
6
+ * against. Moved from a hosting lexicon's own TMP013 to core in #2122 (epic
7
+ * #2114 sub-issue 6), so it fires on every project that declares an Op — a
8
+ * project on the local runtime with no lexicons configured included.
9
+ *
10
+ * Same shape as OPS012 (`./ops012-activity-contract.ts`): the generic walk
11
+ * lives in `@intentius/chant/op`'s `validateStepOutputRefs`, over the same
12
+ * contract map this file builds from core's own base-activity contracts
13
+ * (`../../../op/activities/activity-contracts.ts`, #2117). A producer
14
+ * step whose `fn` has no registered contract is flagged (unlike OPS012's
15
+ * args/outcomeAttribute checks, which skip an unregistered `fn` — a
16
+ * reference has nothing to validate against without one, so it can't be
17
+ * silently allowed the way an unchecked arg can). The purely structural half
18
+ * of this check (an unknown step id, or a reference into a later phase) is
19
+ * independent of the contract map's contents entirely — no lexicon needs to
20
+ * be configured, and no activity needs a registered contract at all, for it
21
+ * to fire.
22
+ *
23
+ * Cross-lexicon (chant #2101): an Op step may call an activity any
24
+ * configured lexicon contributes (`terraformPlan`, `k3sInstall`,
25
+ * `flyApply`), and since this check became core-owned it fires on every
26
+ * project that declares an Op. `ctx.activityContracts` carries the contracts
27
+ * every configured lexicon declared, resolved by `loadActivityContracts`
28
+ * (`../../../op/activity-contract-registry.ts`) the same way
29
+ * `loadActivities` resolves the implementations, and is merged over the
30
+ * static table below. A context that carries none — a hand-built one in a
31
+ * test, a caller that has not loaded any — falls back to that table alone,
32
+ * exactly as before.
33
+ *
34
+ * That merge is what makes this rule usable at all on an Op whose steps are
35
+ * a lexicon's: before it, `terraformPlan` feeding `terraformApply` failed
36
+ * `chant build` for the absence of a contract the terraform lexicon does in
37
+ * fact declare, which is why both terraform examples carrying an Op kept it
38
+ * outside `src/` where the example-build harness could not see it.
39
+ *
40
+ * This check is what makes it safe for a lexicon's own serializer to compile
41
+ * every reference it finds unconditionally: `chant build` blocks file output
42
+ * while an error-severity post-synth finding stands, so generated code
43
+ * referencing an unresolved step never reaches disk (a lexicon's own Op
44
+ * serializer also runs the scope-only subset of this check directly —
45
+ * `validateStepOutputRefScope` — as defense-in-depth, chant #1950 pre-merge
46
+ * review finding 2).
47
+ *
48
+ * Cross-contract type compatibility (chant #1950 pre-merge review, finding
49
+ * 3): this rule also compares a producer's declared return type at `path`
50
+ * against the consumer's declared arg type at the same position
51
+ * (`string`/`number`/`boolean`/`object`/`array` only) and flags a mismatch —
52
+ * a string-returning path feeding a number-typed arg, for example.
53
+ * Deliberately shallow: a union, enum, literal, `z.any()`/`z.unknown()`, a
54
+ * transform, or a reference sitting inside an array in `args` all bail
55
+ * silently rather than being reasoned about. See `@intentius/chant/op`'s
56
+ * `step-output-ref.ts` module doc for the full writeup of what this catches
57
+ * and what it defers (to #1288 Stage 2).
58
+ */
59
+
60
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "../../post-synth";
61
+ import { validateStepOutputRefs, mergeActivityContracts, type ActivityContract } from "../../../op";
62
+ import type { OpConfig } from "../../../op";
63
+ import * as contracts from "../../../op/activities/activity-contracts";
64
+ import { isOpEntity } from "./support";
65
+
66
+ const CONTRACTS: Map<string, ActivityContract> = new Map(
67
+ Object.values(contracts).map((c) => [c.name, c]),
68
+ );
69
+
70
+ export const ops013: PostSynthCheck = {
71
+ id: "OPS013",
72
+ description: "A step-output reference must name an in-scope, preceding, contract-validated producer step",
73
+
74
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
75
+ const diagnostics: PostSynthDiagnostic[] = [];
76
+ const activeContracts = mergeActivityContracts(CONTRACTS, ctx.activityContracts);
77
+
78
+ for (const [entityKey, entity] of ctx.entities) {
79
+ if (!isOpEntity(entity)) continue;
80
+ const rec = entity as unknown as Record<string, unknown>;
81
+
82
+ const props = ((entity as { props?: Record<string, unknown> }).props ?? {}) as unknown as OpConfig;
83
+ if (typeof props.name !== "string" || !Array.isArray(props.phases)) continue;
84
+
85
+ for (const issue of validateStepOutputRefs(props, activeContracts)) {
86
+ diagnostics.push({
87
+ checkId: "OPS013",
88
+ severity: "error",
89
+ message: `Op "${issue.opName}", phase "${issue.phase}", step "${issue.fn}": ${issue.message}`,
90
+ entity: entityKey,
91
+ lexicon: typeof rec.lexicon === "string" ? rec.lexicon : undefined,
92
+ });
93
+ }
94
+ }
95
+
96
+ return diagnostics;
97
+ },
98
+ };
@@ -0,0 +1,263 @@
1
+ /**
2
+ * OPS014 tests — ported from a hosting lexicon's own TMP014
3
+ * (#1484, moved to core by #2122).
4
+ */
5
+
6
+ import { describe, test, expect } from "vitest";
7
+ import type { PostSynthContext } from "../../post-synth";
8
+ import { DECLARABLE_MARKER } from "../../../declarable";
9
+ import { when, eq, gt, allOf, run, report } from "../../../op";
10
+ import type { ConvergeRule } from "../../../op";
11
+ import type { ConvergeSymptom } from "../../../lifecycle/symptoms";
12
+ import { ops014 } from "./ops014-converge-rule-refusals";
13
+
14
+ function makeEntity(entityType: string, props: Record<string, unknown>) {
15
+ return {
16
+ [DECLARABLE_MARKER]: true,
17
+ entityType,
18
+ lexicon: "chant",
19
+ kind: "resource",
20
+ props,
21
+ attributes: {},
22
+ };
23
+ }
24
+
25
+ function makeCtxFromEntities(entities: Map<string, unknown>): PostSynthContext {
26
+ return {
27
+ outputs: new Map([["chant", ""]]),
28
+ entities: entities as Map<string, never>,
29
+ buildResult: {
30
+ outputs: new Map([["chant", ""]]),
31
+ entities: entities as Map<string, never>,
32
+ warnings: [],
33
+ errors: [],
34
+ sourceFileCount: 1,
35
+ },
36
+ };
37
+ }
38
+
39
+ function opEntity(name: string, steps: unknown[], entityType = "Chant::Op") {
40
+ return makeEntity(entityType, { name, overview: "test", phases: [{ name: "Phase", steps }] });
41
+ }
42
+
43
+ function convergeOpEntity(
44
+ name: string,
45
+ rules: ConvergeRule<ConvergeSymptom>[],
46
+ opts?: { dial?: "observe" | "reconcile" | "apply"; entityType?: string },
47
+ ) {
48
+ return makeEntity(opts?.entityType ?? "Chant::Op", {
49
+ name,
50
+ overview: "test",
51
+ labels: { Converge: "true", Env: "staging", Dial: opts?.dial ?? "observe" },
52
+ phases: [
53
+ { name: "Observe", steps: [{ kind: "activity", fn: "lifecycleDiff", args: { env: "staging" }, id: "diff" }] },
54
+ { name: "Converge", steps: [{ kind: "activity", fn: "convergeTick", args: { rules } }] },
55
+ ],
56
+ });
57
+ }
58
+
59
+ function readOnlyOpEntity(name: string) {
60
+ return opEntity(name, [{ kind: "activity", fn: "lifecycleDiff", args: { env: "staging" } }]);
61
+ }
62
+
63
+ function mutatingOpEntity(name: string) {
64
+ return opEntity(name, [
65
+ { kind: "activity", fn: "nativeApply", args: { target: "kubectl", env: "staging", output: "dist", deleteMode: "never" } },
66
+ ]);
67
+ }
68
+
69
+ function destructiveOpEntity(name: string, opts?: { gated?: boolean }) {
70
+ const steps: unknown[] = [
71
+ { kind: "activity", fn: "nativeApply", args: { target: "kubectl", env: "staging", output: "dist", deleteMode: "gated" } },
72
+ ];
73
+ const phases = opts?.gated
74
+ ? [
75
+ { name: "Approve", steps: [{ kind: "gate", gate: "approve-x" }] },
76
+ { name: "Apply", steps },
77
+ ]
78
+ : [{ name: "Apply", steps }];
79
+ return makeEntity("Chant::Op", { name, overview: "test", phases });
80
+ }
81
+
82
+ describe("OPS014: converge-rule-refusals", () => {
83
+ test("passes a well-formed rule table dispatching a read-only op under observe", () => {
84
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("watch"), {
85
+ id: "drift-watch",
86
+ why: "Re-check drift with a read-only observation.",
87
+ });
88
+ const ctx = makeCtxFromEntities(new Map([
89
+ ["converge", convergeOpEntity("converge", [rule], { dial: "observe" })],
90
+ ["watch", readOnlyOpEntity("watch")],
91
+ ]));
92
+ expect(ops014.check(ctx)).toHaveLength(0);
93
+ });
94
+
95
+ test("passes a report-only rule table with no dispatch at all", () => {
96
+ const rule = when<ConvergeSymptom>(gt("adoptCount", 0), report("unowned resources present"), {
97
+ id: "adopt-report",
98
+ why: "Unowned resources are reported, never auto-claimed.",
99
+ });
100
+ const ctx = makeCtxFromEntities(new Map([["converge", convergeOpEntity("converge", [rule])]]));
101
+ expect(ops014.check(ctx)).toHaveLength(0);
102
+ });
103
+
104
+ test("errors when a rule has a blank why", () => {
105
+ const badRule = {
106
+ id: "no-why",
107
+ when: { kind: "field-comparison", field: "status", op: "eq", value: "drifted" },
108
+ then: { kind: "report", reason: "x" },
109
+ why: "",
110
+ } as unknown as ConvergeRule<ConvergeSymptom>;
111
+ const ctx = makeCtxFromEntities(new Map([["converge", convergeOpEntity("converge", [badRule])]]));
112
+ const diags = ops014.check(ctx);
113
+ expect(diags.some((d) => d.checkId === "OPS014" && d.message.includes("must carry its why"))).toBe(true);
114
+ });
115
+
116
+ test("errors when a rule's predicate is malformed (outside the evaluable subset)", () => {
117
+ const badRule = {
118
+ id: "bad-predicate",
119
+ when: { kind: "not-a-real-predicate-kind" },
120
+ then: { kind: "report", reason: "x" },
121
+ why: "some reason",
122
+ } as unknown as ConvergeRule<ConvergeSymptom>;
123
+ const ctx = makeCtxFromEntities(new Map([["converge", convergeOpEntity("converge", [badRule])]]));
124
+ const diags = ops014.check(ctx);
125
+ expect(diags.some((d) => d.message.includes("outside the evaluable subset"))).toBe(true);
126
+ });
127
+
128
+ test("errors when run() names an op that doesn't exist", () => {
129
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("does-not-exist"), { id: "drift-apply", why: "Re-apply on drift." });
130
+ const ctx = makeCtxFromEntities(new Map([["converge", convergeOpEntity("converge", [rule], { dial: "apply" })]]));
131
+ const diags = ops014.check(ctx);
132
+ expect(diags.some((d) => d.message.includes('unknown op "does-not-exist"'))).toBe(true);
133
+ });
134
+
135
+ test("errors when a mutating op is dispatched under an observe dial", () => {
136
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("apply-staging"), { id: "drift-apply", why: "Re-apply on drift." });
137
+ const ctx = makeCtxFromEntities(new Map([
138
+ ["converge", convergeOpEntity("converge", [rule], { dial: "observe" })],
139
+ ["apply-staging", mutatingOpEntity("apply-staging")],
140
+ ]));
141
+ const diags = ops014.check(ctx);
142
+ expect(diags.some((d) => d.message.includes("mutating") && d.message.includes('dial "observe"'))).toBe(true);
143
+ });
144
+
145
+ // Finding A (#1954 pre-merge review): issue #1484's own Autonomy table
146
+ // gives `reconcile` × mutating "open PR", not "run directly" — v1 doesn't
147
+ // build the PR-opening channel, so this is refused rather than silently
148
+ // escalated to match `apply`'s authority.
149
+ test("errors when a mutating op is dispatched under a reconcile dial — reconcile's table answer is \"open PR\", not implemented in v1", () => {
150
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("apply-staging"), { id: "drift-apply", why: "Re-apply on drift." });
151
+ const ctx = makeCtxFromEntities(new Map([
152
+ ["converge", convergeOpEntity("converge", [rule], { dial: "reconcile" })],
153
+ ["apply-staging", mutatingOpEntity("apply-staging")],
154
+ ]));
155
+ const diags = ops014.check(ctx);
156
+ expect(diags.some((d) => d.message.includes("mutating") && d.message.includes('dial "reconcile"') && d.message.includes("open PR"))).toBe(true);
157
+ });
158
+
159
+ test("passes a mutating dispatch under an apply dial", () => {
160
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("apply-staging"), { id: "drift-apply", why: "Re-apply on drift." });
161
+ const ctx = makeCtxFromEntities(new Map([
162
+ ["converge", convergeOpEntity("converge", [rule], { dial: "apply" })],
163
+ ["apply-staging", mutatingOpEntity("apply-staging")],
164
+ ]));
165
+ expect(ops014.check(ctx)).toHaveLength(0);
166
+ });
167
+
168
+ // Finding B (#1954 pre-merge review): a destructive dispatch target is
169
+ // refused under every dial, `apply` included — a converge tick is
170
+ // unattended, so a destructive target needs a human's approval before
171
+ // it's attempted, not a gate the tick consults after already committing to
172
+ // running it.
173
+ test("errors when a destructive op is dispatched under a reconcile dial", () => {
174
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("prune-staging"), { id: "drift-prune", why: "Prune drifted resources." });
175
+ const ctx = makeCtxFromEntities(new Map([
176
+ ["converge", convergeOpEntity("converge", [rule], { dial: "reconcile" })],
177
+ ["prune-staging", destructiveOpEntity("prune-staging", { gated: true })],
178
+ ]));
179
+ const diags = ops014.check(ctx);
180
+ expect(diags.some((d) => d.message.includes("destructive") && d.message.includes("refused in v1"))).toBe(true);
181
+ });
182
+
183
+ test("errors when a destructive op is dispatched under apply and has no gate", () => {
184
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("prune-staging"), { id: "drift-prune", why: "Prune drifted resources." });
185
+ const ctx = makeCtxFromEntities(new Map([
186
+ ["converge", convergeOpEntity("converge", [rule], { dial: "apply" })],
187
+ ["prune-staging", destructiveOpEntity("prune-staging", { gated: false })],
188
+ ]));
189
+ const diags = ops014.check(ctx);
190
+ expect(diags.some((d) => d.message.includes("destructive") && d.message.includes("refused in v1"))).toBe(true);
191
+ });
192
+
193
+ test("errors when a destructive op is dispatched under apply even when the target is gated — approval must come before dispatch, not after", () => {
194
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("prune-staging"), { id: "drift-prune", why: "Prune drifted resources." });
195
+ const ctx = makeCtxFromEntities(new Map([
196
+ ["converge", convergeOpEntity("converge", [rule], { dial: "apply" })],
197
+ ["prune-staging", destructiveOpEntity("prune-staging", { gated: true })],
198
+ ]));
199
+ const diags = ops014.check(ctx);
200
+ expect(diags.some((d) => d.message.includes("destructive") && d.message.includes("refused in v1"))).toBe(true);
201
+ });
202
+
203
+ // Finding C (#1954 pre-merge review): adopt safety — "an unowned resource
204
+ // is reported, never auto-claimed" — is enforced, not just documented.
205
+ test("errors when a rule reads adoptCount and dispatches a mutating op", () => {
206
+ const rule = when<ConvergeSymptom>(gt("adoptCount", 0), run("apply-staging"), { id: "adopt-apply", why: "test" });
207
+ const ctx = makeCtxFromEntities(new Map([
208
+ ["converge", convergeOpEntity("converge", [rule], { dial: "apply" })],
209
+ ["apply-staging", mutatingOpEntity("apply-staging")],
210
+ ]));
211
+ const diags = ops014.check(ctx);
212
+ expect(diags.some((d) => d.message.includes('reads "adoptCount"') && d.message.includes("mutating"))).toBe(true);
213
+ });
214
+
215
+ test("errors when adoptCount is read inside a nested allOf/anyOf predicate that dispatches a mutating op", () => {
216
+ const rule = when<ConvergeSymptom>(
217
+ allOf(eq("status", "drifted"), gt("adoptCount", 0)),
218
+ run("apply-staging"),
219
+ { id: "nested-adopt-apply", why: "test" },
220
+ );
221
+ const ctx = makeCtxFromEntities(new Map([
222
+ ["converge", convergeOpEntity("converge", [rule], { dial: "apply" })],
223
+ ["apply-staging", mutatingOpEntity("apply-staging")],
224
+ ]));
225
+ const diags = ops014.check(ctx);
226
+ expect(diags.some((d) => d.message.includes('reads "adoptCount"'))).toBe(true);
227
+ });
228
+
229
+ test("passes a rule that reads adoptCount and dispatches a read-only op", () => {
230
+ const rule = when<ConvergeSymptom>(gt("adoptCount", 0), run("watch"), { id: "adopt-watch", why: "test" });
231
+ const ctx = makeCtxFromEntities(new Map([
232
+ ["converge", convergeOpEntity("converge", [rule], { dial: "apply" })],
233
+ ["watch", readOnlyOpEntity("watch")],
234
+ ]));
235
+ expect(ops014.check(ctx)).toHaveLength(0);
236
+ });
237
+
238
+ test("passes a rule that reads adoptCount and only reports (no dispatch at all)", () => {
239
+ const rule = when<ConvergeSymptom>(gt("adoptCount", 0), report("unowned resources present"), { id: "adopt-report", why: "test" });
240
+ const ctx = makeCtxFromEntities(new Map([["converge", convergeOpEntity("converge", [rule], { dial: "apply" })]]));
241
+ expect(ops014.check(ctx)).toHaveLength(0);
242
+ });
243
+
244
+ test("ignores an Op with no Converge label", () => {
245
+ const ctx = makeCtxFromEntities(new Map([["op", readOnlyOpEntity("op")]]));
246
+ expect(ops014.check(ctx)).toHaveLength(0);
247
+ });
248
+
249
+ test("ignores non-Op entities", () => {
250
+ const ctx = makeCtxFromEntities(new Map([
251
+ ["bucket", makeEntity("AWS::S3::Bucket", { name: "default" })],
252
+ ]));
253
+ expect(ops014.check(ctx)).toHaveLength(0);
254
+ });
255
+
256
+ test("ignores an entity whose entityType isn't Chant::Op", () => {
257
+ const rule = when<ConvergeSymptom>(eq("status", "drifted"), run("does-not-exist"), { id: "drift-apply", why: "Re-apply on drift." });
258
+ const ctx = makeCtxFromEntities(new Map([
259
+ ["converge", convergeOpEntity("converge", [rule], { dial: "apply", entityType: "Legacy::Op" })],
260
+ ]));
261
+ expect(ops014.check(ctx)).toHaveLength(0);
262
+ });
263
+ });
@@ -0,0 +1,179 @@
1
+ /**
2
+ * OPS014: converge-rule-refusals (#1484). Moved from a hosting lexicon's own
3
+ * TMP014 to core in #2122 (epic #2114 sub-issue 6), so it fires on every
4
+ * project that declares a `ConvergeOp`-shaped Op, not only one with that
5
+ * lexicon configured.
6
+ *
7
+ * Cross-Op build-time refusals for a `ConvergeOp`'s rule table — the checks
8
+ * `../../../op/composites/converge-op.ts`'s factory can't make on its own,
9
+ * since they need the *whole* discovered graph of `Chant::Op` entities
10
+ * (`PostSynthContext.entities`), not just the ConvergeOp instance under
11
+ * construction: a sibling `*.op.ts` file's Op may not exist yet at the
12
+ * moment a composite factory runs, the same reason OPS012/OPS013 exist as
13
+ * post-synth checks rather than builder-time throws.
14
+ *
15
+ * A `ConvergeOp`-produced Op is identified by its own
16
+ * `labels.Converge === "true"` marker (mirroring `ApplyOp`'s
17
+ * `{ Apply: "true" }` / `WatchOp`'s `{ Watch: "true" }`); its rule table is
18
+ * read back off the `convergeTick` activity step's `args.rules` (there is no
19
+ * dedicated `OpConfig` field for it — see `../../../op/composites/
20
+ * converge-op.ts`'s doc on why the rule table travels as a step's args
21
+ * instead of a bespoke config field).
22
+ *
23
+ * Flags (pre-merge review of #1954 tightened the last three to match issue
24
+ * #1484's own Autonomy table — see `converge-op.ts`'s module doc):
25
+ * - a rule with no (or a blank) `why` — every rule must carry its
26
+ * rationale (Accessible Ops factor III),
27
+ * - a rule whose predicate isn't in the evaluable subset
28
+ * (`isWellFormedPredicate`, re-validated here as the runtime backstop
29
+ * over a rule table not authored through `when()`),
30
+ * - a `run()` action naming an Op no declared Op entity has as its `name`,
31
+ * - a `run()` action dispatching a `mutating` Op under any dial other than
32
+ * `"apply"` — the issue's Autonomy table gives `reconcile` "open PR", not
33
+ * "run directly"; opening a PR is out of v1 scope (epic #1487's
34
+ * `onDrift`-channel open question), so `reconcile` refuses the mutating
35
+ * dispatch rather than silently escalating it to "run directly",
36
+ * - a `run()` action dispatching a `destructive` Op under any dial,
37
+ * unconditionally — a converge tick runs unattended, on a schedule, with
38
+ * no one watching it fire. A destructive dispatch needs a human's
39
+ * approval *before* it is attempted, not a gate the tick consults after
40
+ * it has already committed to running the op — and `run()` has no way to
41
+ * ask first. (This used to be argued from the local executor's inability
42
+ * to honor a gate at all; as of #2119 a gated op ends the run `gated`
43
+ * rather than being refused, so that argument no longer holds — the
44
+ * refusal stands on the unattended-approval ground instead.) Durable,
45
+ * pre-approved destructive dispatch from a tick is a future design's to
46
+ * own; v1 refuses the target rather than shipping a path that dispatches
47
+ * a destructive op with no human in the loop,
48
+ * - a rule whose predicate reads `adoptCount` while its action is `run()`
49
+ * against a `mutating` Op — adopt safety (never auto-claiming an unowned
50
+ * resource) is enforced here, not merely documented: a rule that reads
51
+ * the adopt signal has no business dispatching a mutation.
52
+ */
53
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "../../post-synth";
54
+ import { classifyOpVerbClass, isWellFormedPredicate, predicateReferencesField } from "../../../op";
55
+ import type { ConvergeRule, OpConfig } from "../../../op";
56
+ import { CONVERGE_SYMPTOM_FIELDS, type ConvergeSymptom } from "../../../lifecycle/symptoms";
57
+ import { isOpEntity } from "./support";
58
+
59
+ function looksLikeOpProps(props: Record<string, unknown>): boolean {
60
+ return typeof props.name === "string" && Array.isArray(props.phases);
61
+ }
62
+
63
+ /** Read a ConvergeOp's rule table back off its `convergeTick` step's `args.rules`. `undefined` if this doesn't look like a ConvergeOp-shaped Op (defensive — never crashes the build over an unexpected shape). */
64
+ function findConvergeRules(props: OpConfig): ConvergeRule<ConvergeSymptom>[] | undefined {
65
+ for (const phase of props.phases) {
66
+ for (const step of phase.steps) {
67
+ if (step.kind === "activity" && step.fn === "convergeTick") {
68
+ const rules = step.args?.rules;
69
+ return Array.isArray(rules) ? (rules as ConvergeRule<ConvergeSymptom>[]) : undefined;
70
+ }
71
+ }
72
+ }
73
+ return undefined;
74
+ }
75
+
76
+ export const ops014: PostSynthCheck = {
77
+ id: "OPS014",
78
+ description:
79
+ "A ConvergeOp rule table: run() must name a declared Op, a mutating dispatch requires dial apply (reconcile opens a PR in a future version, not implemented in v1), a destructive dispatch is refused outright in v1 (a converge tick is unattended and a destructive dispatch needs approval before it's attempted, not a gate consulted after the tick already committed to it), an adoptCount-reading rule may not dispatch a mutation, and every rule must carry its why",
80
+
81
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
82
+ const diagnostics: PostSynthDiagnostic[] = [];
83
+
84
+ const opsByName = new Map<string, OpConfig>();
85
+ for (const entity of ctx.entities.values()) {
86
+ if (!isOpEntity(entity)) continue;
87
+ const rawProps = ((entity as { props?: Record<string, unknown> }).props ?? {}) as Record<string, unknown>;
88
+ if (!looksLikeOpProps(rawProps)) continue;
89
+ const opProps = rawProps as unknown as OpConfig;
90
+ opsByName.set(opProps.name, opProps);
91
+ }
92
+
93
+ for (const [entityKey, entity] of ctx.entities) {
94
+ if (!isOpEntity(entity)) continue;
95
+ const rec = entity as unknown as Record<string, unknown>;
96
+
97
+ const rawProps = ((entity as { props?: Record<string, unknown> }).props ?? {}) as Record<string, unknown>;
98
+ if (!looksLikeOpProps(rawProps)) continue;
99
+ const props = rawProps as unknown as OpConfig;
100
+ if (props.labels?.Converge !== "true") continue;
101
+
102
+ const dial = props.labels?.Dial ?? "observe";
103
+ const rules = findConvergeRules(props);
104
+ if (!rules) continue;
105
+
106
+ for (const rule of rules) {
107
+ const label = typeof rule?.id === "string" ? rule.id : "(unnamed rule)";
108
+ const push = (message: string): void => {
109
+ diagnostics.push({
110
+ checkId: "OPS014",
111
+ severity: "error",
112
+ message: `ConvergeOp "${props.name}", rule "${label}": ${message}`,
113
+ entity: entityKey,
114
+ lexicon: typeof rec.lexicon === "string" ? rec.lexicon : undefined,
115
+ });
116
+ };
117
+
118
+ if (typeof rule?.why !== "string" || rule.why.trim().length === 0) {
119
+ push("every rule must carry its why — refused at build");
120
+ continue;
121
+ }
122
+ if (!isWellFormedPredicate(rule.when, CONVERGE_SYMPTOM_FIELDS)) {
123
+ push("predicate is outside the evaluable subset (build it from eq/neq/gt/gte/lt/lte/truthy/falsy/allOf/anyOf over a known symptom field)");
124
+ continue;
125
+ }
126
+ if (!rule.then || rule.then.kind !== "run") continue;
127
+
128
+ const targetName = rule.then.op;
129
+ const target = opsByName.get(targetName);
130
+ if (!target) {
131
+ push(`references unknown op "${targetName}" — no declared Op has that name`);
132
+ continue;
133
+ }
134
+
135
+ const verbClass = classifyOpVerbClass(target);
136
+
137
+ // Mutating: the issue's Autonomy table gives `reconcile` "open PR",
138
+ // not "run directly" — building that channel is out of v1 scope
139
+ // (epic #1487's onDrift-channel open question), so only `apply`
140
+ // (the dial the issue's table marks "run, gated per op") may free-run
141
+ // a mutating dispatch. Refusing this at build is the honest
142
+ // alternative to silently escalating `reconcile`'s authority to
143
+ // match `apply`.
144
+ if (verbClass === "mutating" && dial !== "apply") {
145
+ const escalationNote =
146
+ dial === "reconcile"
147
+ ? ` — the issue's table answer for "reconcile" is "open PR", which v1 does not implement (see epic #1487's onDrift-channel open question); this Op would otherwise silently run a mutation "reconcile" was never granted`
148
+ : "";
149
+ push(`dispatches "${targetName}" (mutating), but dial "${dial}" only permits a mutating dispatch under "apply"${escalationNote} — report instead, or raise the dial to "apply"`);
150
+ }
151
+
152
+ // Destructive: refused outright, regardless of dial or gate. A
153
+ // converge tick runs unattended on a schedule — nothing is watching
154
+ // it fire — so a destructive dispatch needs a human's approval
155
+ // *before* it is attempted, not a gate the tick consults only after
156
+ // it has already committed to running the op. `run()` has no way to
157
+ // ask first, so the target is refused rather than dispatched.
158
+ if (verbClass === "destructive") {
159
+ push(
160
+ `dispatches "${targetName}" (destructive) — destructive run() targets are refused in v1 under any dial: a converge tick is unattended, and a destructive dispatch needs a human's approval before it is attempted, not a gate consulted after the tick has already committed to running it. Destructive remediation via ConvergeOp arrives once a tick can ask for that approval before it dispatches — until then, remediate manually via a gated ApplyOp/ReconcileOp run.`,
161
+ );
162
+ }
163
+
164
+ // Adopt safety: enforced, not just documented. A rule that reads
165
+ // `adoptCount` — the "unowned resource" signal — has no honest
166
+ // reason to dispatch a mutation; the only sanctioned action for that
167
+ // signal is `report()`. (A destructive target already gets refused
168
+ // above regardless of what its predicate reads.)
169
+ if (verbClass === "mutating" && predicateReferencesField(rule.when, "adoptCount")) {
170
+ push(
171
+ `predicate reads "adoptCount" but dispatches "${targetName}" (mutating) — an unowned resource is reported, never auto-claimed by a dispatched mutation; use report() for a rule that reads the adopt signal`,
172
+ );
173
+ }
174
+ }
175
+ }
176
+
177
+ return diagnostics;
178
+ },
179
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared support for the OPS* Op-model post-synth checks (#2122, epic #2114
3
+ * sub-issue 6) — OPS012 (`./ops012-activity-contract.ts`), OPS013
4
+ * (`./ops013-step-output-ref.ts`) and OPS014
5
+ * (`./ops014-converge-rule-refusals.ts`), ported here from a hosting
6
+ * lexicon's own TMP012/TMP013/TMP014.
7
+ *
8
+ * Op-entity recognition is core's own `isOpEntity` (`../../../op/resource.ts`,
9
+ * #2118) — re-exported here so every OPS* check imports it from one place.
10
+ */
11
+ export { isOpEntity } from "../../../op/resource";