@intentius/chant 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/dist/audit/catalog.d.ts +51 -1
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +20 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +14 -8
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/lineage.d.ts +10 -0
  8. package/dist/audit/lineage.d.ts.map +1 -0
  9. package/dist/audit/prior-art.d.ts +385 -0
  10. package/dist/audit/prior-art.d.ts.map +1 -0
  11. package/dist/audit/proof.d.ts +16 -0
  12. package/dist/audit/proof.d.ts.map +1 -1
  13. package/dist/audit/report-html.d.ts.map +1 -1
  14. package/dist/audit/report-model.d.ts +8 -1
  15. package/dist/audit/report-model.d.ts.map +1 -1
  16. package/dist/audit/report.d.ts.map +1 -1
  17. package/dist/audit/rules-doc.d.ts +3 -0
  18. package/dist/audit/rules-doc.d.ts.map +1 -1
  19. package/dist/audit/secrets.d.ts +15 -0
  20. package/dist/audit/secrets.d.ts.map +1 -1
  21. package/dist/audit/terraform-state.d.ts +46 -0
  22. package/dist/audit/terraform-state.d.ts.map +1 -0
  23. package/dist/build.d.ts +17 -1
  24. package/dist/build.d.ts.map +1 -1
  25. package/dist/claimed-fields.d.ts +161 -0
  26. package/dist/claimed-fields.d.ts.map +1 -0
  27. package/dist/cli/build-params-cli.d.ts +2 -2
  28. package/dist/cli/command-group.d.ts +14 -0
  29. package/dist/cli/command-group.d.ts.map +1 -1
  30. package/dist/cli/commands/audit.d.ts +17 -1
  31. package/dist/cli/commands/audit.d.ts.map +1 -1
  32. package/dist/cli/commands/build.d.ts.map +1 -1
  33. package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
  34. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  35. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  36. package/dist/cli/commands/import.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
  38. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
  40. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  41. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  42. package/dist/cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/commands/lint.d.ts.map +1 -1
  44. package/dist/cli/commands/onboard.d.ts +2 -0
  45. package/dist/cli/commands/onboard.d.ts.map +1 -1
  46. package/dist/cli/handlers/build.d.ts.map +1 -1
  47. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  48. package/dist/cli/handlers/operator.d.ts +82 -16
  49. package/dist/cli/handlers/operator.d.ts.map +1 -1
  50. package/dist/cli/handlers/run.d.ts +49 -42
  51. package/dist/cli/handlers/run.d.ts.map +1 -1
  52. package/dist/cli/main.d.ts +28 -1
  53. package/dist/cli/main.d.ts.map +1 -1
  54. package/dist/cli/mcp/op-tools.d.ts +16 -1
  55. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  56. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  57. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  58. package/dist/cli/registry.d.ts +26 -9
  59. package/dist/cli/registry.d.ts.map +1 -1
  60. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  61. package/dist/components/auto-release.d.ts +10 -20
  62. package/dist/components/auto-release.d.ts.map +1 -1
  63. package/dist/components/capability.d.ts +4 -4
  64. package/dist/components/cli-support.d.ts +20 -21
  65. package/dist/components/cli-support.d.ts.map +1 -1
  66. package/dist/components/component.d.ts +3 -3
  67. package/dist/components/component.d.ts.map +1 -1
  68. package/dist/components/driver-output.d.ts.map +1 -1
  69. package/dist/components/driver.d.ts +66 -36
  70. package/dist/components/driver.d.ts.map +1 -1
  71. package/dist/components/index.d.ts +1 -2
  72. package/dist/components/index.d.ts.map +1 -1
  73. package/dist/components/manifest-persistence.d.ts +1 -9
  74. package/dist/components/manifest-persistence.d.ts.map +1 -1
  75. package/dist/components/run-progress.d.ts +6 -6
  76. package/dist/components/run-progress.d.ts.map +1 -1
  77. package/dist/components/verbs/run-agent.d.ts +9 -15
  78. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  79. package/dist/components/verbs/verify.d.ts +2 -2
  80. package/dist/deep-observation.d.ts +17 -0
  81. package/dist/deep-observation.d.ts.map +1 -1
  82. package/dist/discovery/files.d.ts +4 -4
  83. package/dist/discovery/fold-import.d.ts.map +1 -1
  84. package/dist/discovery/param-deps.d.ts +33 -0
  85. package/dist/discovery/param-deps.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  87. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  88. package/dist/fold/foldable-helpers.d.ts +5 -1
  89. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  90. package/dist/fold-provenance.d.ts +236 -0
  91. package/dist/fold-provenance.d.ts.map +1 -0
  92. package/dist/graph-ops.d.ts +1 -1
  93. package/dist/held-elsewhere.d.ts +85 -0
  94. package/dist/held-elsewhere.d.ts.map +1 -0
  95. package/dist/import/ir-utils.d.ts +1 -1
  96. package/dist/import/ir-utils.d.ts.map +1 -1
  97. package/dist/import/parser.d.ts +33 -0
  98. package/dist/import/parser.d.ts.map +1 -1
  99. package/dist/index.d.ts +3 -0
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/kubectl-context.d.ts +3 -3
  102. package/dist/lexicon-config.d.ts +4 -4
  103. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  104. package/dist/lexicon.d.ts +104 -14
  105. package/dist/lexicon.d.ts.map +1 -1
  106. package/dist/lifecycle/change-set.d.ts +13 -0
  107. package/dist/lifecycle/change-set.d.ts.map +1 -1
  108. package/dist/lifecycle/deep-diff.d.ts +133 -6
  109. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  110. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  111. package/dist/lifecycle/gate-ledger.d.ts +71 -3
  112. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  113. package/dist/lifecycle/index.d.ts +1 -0
  114. package/dist/lifecycle/index.d.ts.map +1 -1
  115. package/dist/lifecycle/release-ledger.d.ts +2 -2
  116. package/dist/lifecycle/run-ledger.d.ts +53 -0
  117. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  118. package/dist/lifecycle/symptoms.d.ts +3 -3
  119. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  120. package/dist/lint/config.d.ts +81 -2
  121. package/dist/lint/config.d.ts.map +1 -1
  122. package/dist/lint/post-synth.d.ts +55 -2
  123. package/dist/lint/post-synth.d.ts.map +1 -1
  124. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  125. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  126. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  127. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  128. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  129. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  130. package/dist/lint/rules/comp/index.d.ts +1 -1
  131. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  132. package/dist/lint/rules/op/index.d.ts +16 -0
  133. package/dist/lint/rules/op/index.d.ts.map +1 -0
  134. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  135. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  136. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  137. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  138. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  139. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  140. package/dist/lint/rules/op/support.d.ts +12 -0
  141. package/dist/lint/rules/op/support.d.ts.map +1 -0
  142. package/dist/lint/suppressions.d.ts +108 -0
  143. package/dist/lint/suppressions.d.ts.map +1 -0
  144. package/dist/live-endpoint.d.ts +1 -1
  145. package/dist/op/activities/activity-contracts.d.ts +54 -0
  146. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  147. package/dist/op/activities/apply.d.ts +262 -0
  148. package/dist/op/activities/apply.d.ts.map +1 -0
  149. package/dist/op/activities/build.d.ts +13 -0
  150. package/dist/op/activities/build.d.ts.map +1 -0
  151. package/dist/op/activities/converge.d.ts +110 -0
  152. package/dist/op/activities/converge.d.ts.map +1 -0
  153. package/dist/op/activities/env-teardown.d.ts +76 -0
  154. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  155. package/dist/op/activities/guard-validate.d.ts +64 -0
  156. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  157. package/dist/op/activities/http-check.d.ts +34 -0
  158. package/dist/op/activities/http-check.d.ts.map +1 -0
  159. package/dist/op/activities/index.d.ts +31 -0
  160. package/dist/op/activities/index.d.ts.map +1 -0
  161. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  162. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  163. package/dist/op/activities/lifecycle.d.ts +42 -0
  164. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  165. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  166. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  167. package/dist/op/activities/policy.d.ts +17 -0
  168. package/dist/op/activities/policy.d.ts.map +1 -0
  169. package/dist/op/activities/reconcile.d.ts +89 -0
  170. package/dist/op/activities/reconcile.d.ts.map +1 -0
  171. package/dist/op/activities/shell.d.ts +13 -0
  172. package/dist/op/activities/shell.d.ts.map +1 -0
  173. package/dist/op/activities/teardown.d.ts +10 -0
  174. package/dist/op/activities/teardown.d.ts.map +1 -0
  175. package/dist/op/activities/util.d.ts +7 -0
  176. package/dist/op/activities/util.d.ts.map +1 -0
  177. package/dist/op/activities/wait.d.ts +28 -0
  178. package/dist/op/activities/wait.d.ts.map +1 -0
  179. package/dist/op/activities/workflow-audit.d.ts +92 -0
  180. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  181. package/dist/op/activity-contract-registry.d.ts +76 -0
  182. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  183. package/dist/op/activity-contract.d.ts +25 -18
  184. package/dist/op/activity-contract.d.ts.map +1 -1
  185. package/dist/op/activity-failure.d.ts +28 -0
  186. package/dist/op/activity-failure.d.ts.map +1 -0
  187. package/dist/op/activity-profiles.d.ts +118 -0
  188. package/dist/op/activity-profiles.d.ts.map +1 -0
  189. package/dist/op/activity-registry.d.ts +22 -38
  190. package/dist/op/activity-registry.d.ts.map +1 -1
  191. package/dist/op/activity-runtime.d.ts +3 -14
  192. package/dist/op/activity-runtime.d.ts.map +1 -1
  193. package/dist/op/builders.d.ts +84 -29
  194. package/dist/op/builders.d.ts.map +1 -1
  195. package/dist/op/composites/apply-op.d.ts +113 -0
  196. package/dist/op/composites/apply-op.d.ts.map +1 -0
  197. package/dist/op/composites/converge-op.d.ts +120 -0
  198. package/dist/op/composites/converge-op.d.ts.map +1 -0
  199. package/dist/op/composites/index.d.ts +26 -0
  200. package/dist/op/composites/index.d.ts.map +1 -0
  201. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  202. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  203. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  204. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  205. package/dist/op/composites/reconcile-op.d.ts +57 -0
  206. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  207. package/dist/op/composites/watch-op.d.ts +60 -0
  208. package/dist/op/composites/watch-op.d.ts.map +1 -0
  209. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  210. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  211. package/dist/op/converge-rule.d.ts +12 -10
  212. package/dist/op/converge-rule.d.ts.map +1 -1
  213. package/dist/op/cron.d.ts +51 -0
  214. package/dist/op/cron.d.ts.map +1 -0
  215. package/dist/op/discover.d.ts +9 -0
  216. package/dist/op/discover.d.ts.map +1 -1
  217. package/dist/op/duration.d.ts +11 -0
  218. package/dist/op/duration.d.ts.map +1 -0
  219. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  220. package/dist/op/gate.d.ts +90 -0
  221. package/dist/op/gate.d.ts.map +1 -0
  222. package/dist/op/generate-pipeline.d.ts +9 -0
  223. package/dist/op/generate-pipeline.d.ts.map +1 -1
  224. package/dist/op/index.d.ts +23 -7
  225. package/dist/op/index.d.ts.map +1 -1
  226. package/dist/op/local-executor.d.ts +114 -28
  227. package/dist/op/local-executor.d.ts.map +1 -1
  228. package/dist/op/local-output.d.ts +11 -1
  229. package/dist/op/local-output.d.ts.map +1 -1
  230. package/dist/op/op-ir.d.ts +184 -0
  231. package/dist/op/op-ir.d.ts.map +1 -0
  232. package/dist/op/op-verb-class.d.ts +10 -2
  233. package/dist/op/op-verb-class.d.ts.map +1 -1
  234. package/dist/op/operator.d.ts +30 -5
  235. package/dist/op/operator.d.ts.map +1 -1
  236. package/dist/op/receipt-store.d.ts +1 -1
  237. package/dist/op/receipt-store.d.ts.map +1 -1
  238. package/dist/op/resource.d.ts +15 -1
  239. package/dist/op/resource.d.ts.map +1 -1
  240. package/dist/op/runtime.d.ts +200 -0
  241. package/dist/op/runtime.d.ts.map +1 -0
  242. package/dist/op/runtimes/local.d.ts +29 -0
  243. package/dist/op/runtimes/local.d.ts.map +1 -0
  244. package/dist/op/step-output-ref.d.ts +15 -13
  245. package/dist/op/step-output-ref.d.ts.map +1 -1
  246. package/dist/op/types.d.ts +79 -27
  247. package/dist/op/types.d.ts.map +1 -1
  248. package/dist/ownership.d.ts +1 -1
  249. package/dist/provenance.d.ts +21 -0
  250. package/dist/provenance.d.ts.map +1 -1
  251. package/dist/serializer-walker.d.ts.map +1 -1
  252. package/dist/stack-output.d.ts +5 -0
  253. package/dist/stack-output.d.ts.map +1 -1
  254. package/package.json +6 -1
  255. package/src/audit/catalog.test.ts +79 -3
  256. package/src/audit/catalog.ts +111 -13
  257. package/src/audit/core.test.ts +26 -0
  258. package/src/audit/core.ts +61 -17
  259. package/src/audit/discover.test.ts +39 -0
  260. package/src/audit/discover.ts +91 -15
  261. package/src/audit/lineage.ts +100 -0
  262. package/src/audit/prior-art.ts +153 -0
  263. package/src/audit/proof.test.ts +67 -0
  264. package/src/audit/proof.ts +70 -0
  265. package/src/audit/report-html.ts +9 -4
  266. package/src/audit/report-model.ts +16 -2
  267. package/src/audit/report.test.ts +33 -0
  268. package/src/audit/report.ts +5 -3
  269. package/src/audit/rules-doc.test.ts +34 -2
  270. package/src/audit/rules-doc.ts +82 -9
  271. package/src/audit/secrets.ts +15 -3
  272. package/src/audit/terraform-state.test.ts +144 -0
  273. package/src/audit/terraform-state.ts +131 -0
  274. package/src/build.ts +31 -1
  275. package/src/claimed-fields.test.ts +145 -0
  276. package/src/claimed-fields.ts +208 -0
  277. package/src/cli/build-params-cli.ts +2 -2
  278. package/src/cli/command-group.ts +14 -0
  279. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  280. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  281. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  282. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  283. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  284. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  285. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  286. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  287. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  288. package/src/cli/commands/audit.test.ts +189 -11
  289. package/src/cli/commands/audit.ts +73 -27
  290. package/src/cli/commands/build.test.ts +292 -9
  291. package/src/cli/commands/build.ts +156 -24
  292. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  293. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  294. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  295. package/src/cli/commands/import.ts +6 -0
  296. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  297. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  298. package/src/cli/commands/init-lexicon.test.ts +97 -2
  299. package/src/cli/commands/init-lexicon.ts +4 -2
  300. package/src/cli/commands/init.test.ts +35 -1
  301. package/src/cli/commands/init.ts +14 -3
  302. package/src/cli/commands/lint.test.ts +48 -0
  303. package/src/cli/commands/lint.ts +123 -0
  304. package/src/cli/commands/onboard.test.ts +75 -0
  305. package/src/cli/commands/onboard.ts +54 -23
  306. package/src/cli/handlers/build.test.ts +81 -1
  307. package/src/cli/handlers/build.ts +15 -1
  308. package/src/cli/handlers/graph.test.ts +3 -3
  309. package/src/cli/handlers/lifecycle.test.ts +195 -12
  310. package/src/cli/handlers/lifecycle.ts +123 -6
  311. package/src/cli/handlers/misc.ts +1 -1
  312. package/src/cli/handlers/operator.test.ts +183 -11
  313. package/src/cli/handlers/operator.ts +260 -55
  314. package/src/cli/handlers/run.test.ts +431 -1287
  315. package/src/cli/handlers/run.ts +365 -915
  316. package/src/cli/main.test.ts +23 -15
  317. package/src/cli/main.ts +88 -33
  318. package/src/cli/mcp/op-tools.ts +147 -88
  319. package/src/cli/mcp/resource-handlers.ts +10 -37
  320. package/src/cli/mcp/resources/context.ts +11 -12
  321. package/src/cli/mcp/server.test.ts +25 -27
  322. package/src/cli/mcp/server.ts +2 -2
  323. package/src/cli/registry.ts +26 -9
  324. package/src/codegen/docs.ts +1 -1
  325. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  326. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  327. package/src/components/README.md +3 -2
  328. package/src/components/auto-release.test.ts +4 -20
  329. package/src/components/auto-release.ts +17 -42
  330. package/src/components/capability.ts +4 -4
  331. package/src/components/cli-support.test.ts +10 -5
  332. package/src/components/cli-support.ts +52 -44
  333. package/src/components/component.schema.json +2 -2
  334. package/src/components/component.ts +3 -3
  335. package/src/components/driver-output.ts +14 -2
  336. package/src/components/driver.test.ts +92 -22
  337. package/src/components/driver.ts +193 -83
  338. package/src/components/index.ts +1 -6
  339. package/src/components/manifest-persistence.test.ts +2 -20
  340. package/src/components/manifest-persistence.ts +4 -24
  341. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  342. package/src/components/run-progress.ts +6 -6
  343. package/src/components/verbs/run-agent.ts +11 -17
  344. package/src/components/verbs/verify.ts +2 -2
  345. package/src/deep-observation.test.ts +75 -0
  346. package/src/deep-observation.ts +48 -0
  347. package/src/discovery/collect.test.ts +3 -3
  348. package/src/discovery/files.ts +4 -4
  349. package/src/discovery/fold-import.ts +105 -2
  350. package/src/discovery/param-deps.test.ts +112 -1
  351. package/src/discovery/param-deps.ts +193 -0
  352. package/src/discovery/sandbox/bundle.ts +15 -1
  353. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  354. package/src/discovery/sandbox/config-wire.ts +2 -2
  355. package/src/fold/fold.test.ts +56 -0
  356. package/src/fold/foldable-helpers.ts +81 -1
  357. package/src/fold-provenance-exclusion.test.ts +186 -0
  358. package/src/fold-provenance.test.ts +495 -0
  359. package/src/fold-provenance.ts +441 -0
  360. package/src/graph-ops.test.ts +2 -2
  361. package/src/graph-ops.ts +1 -1
  362. package/src/held-elsewhere.test.ts +102 -0
  363. package/src/held-elsewhere.ts +114 -0
  364. package/src/import/ir-utils.ts +11 -1
  365. package/src/import/parser.ts +35 -0
  366. package/src/index.ts +3 -0
  367. package/src/kubectl-context.ts +3 -3
  368. package/src/lexicon-config.test.ts +5 -5
  369. package/src/lexicon-config.ts +4 -4
  370. package/src/lexicon-plugin-helpers.ts +1 -1
  371. package/src/lexicon.ts +120 -14
  372. package/src/lifecycle/change-set.test.ts +49 -0
  373. package/src/lifecycle/change-set.ts +67 -0
  374. package/src/lifecycle/converge-ledger.ts +1 -1
  375. package/src/lifecycle/deep-diff.test.ts +267 -14
  376. package/src/lifecycle/deep-diff.ts +217 -14
  377. package/src/lifecycle/deep-observe.test.ts +3 -1
  378. package/src/lifecycle/deep-observe.ts +16 -6
  379. package/src/lifecycle/gate-ledger.test.ts +74 -2
  380. package/src/lifecycle/gate-ledger.ts +143 -36
  381. package/src/lifecycle/index.ts +1 -0
  382. package/src/lifecycle/release-ledger.ts +2 -2
  383. package/src/lifecycle/run-ledger.test.ts +268 -0
  384. package/src/lifecycle/run-ledger.ts +201 -0
  385. package/src/lifecycle/symptoms.ts +3 -3
  386. package/src/lint/config.test.ts +82 -1
  387. package/src/lint/config.ts +112 -3
  388. package/src/lint/policy.test.ts +1 -1
  389. package/src/lint/post-synth.test.ts +31 -0
  390. package/src/lint/post-synth.ts +54 -1
  391. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  392. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  393. package/src/lint/rules/comp/comp.test.ts +1 -1
  394. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
  395. package/src/lint/rules/comp/index.ts +3 -3
  396. package/src/lint/rules/op/index.ts +23 -0
  397. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  398. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  399. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  400. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  401. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  402. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  403. package/src/lint/rules/op/support.ts +11 -0
  404. package/src/lint/suppressions.test.ts +163 -0
  405. package/src/lint/suppressions.ts +204 -0
  406. package/src/live-endpoint.ts +1 -1
  407. package/src/op/activities/activity-contracts.ts +66 -0
  408. package/src/op/activities/apply.test.ts +567 -0
  409. package/src/op/activities/apply.ts +662 -0
  410. package/src/op/activities/build.ts +26 -0
  411. package/src/op/activities/converge.test.ts +330 -0
  412. package/src/op/activities/converge.ts +449 -0
  413. package/src/op/activities/env-teardown.test.ts +187 -0
  414. package/src/op/activities/env-teardown.ts +171 -0
  415. package/src/op/activities/guard-validate.test.ts +139 -0
  416. package/src/op/activities/guard-validate.ts +223 -0
  417. package/src/op/activities/http-check.test.ts +48 -0
  418. package/src/op/activities/http-check.ts +55 -0
  419. package/src/op/activities/index.ts +119 -0
  420. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  421. package/src/op/activities/lexicon-upgrade.ts +673 -0
  422. package/src/op/activities/lifecycle.ts +85 -0
  423. package/src/op/activities/pipeline-audit.test.ts +67 -0
  424. package/src/op/activities/pipeline-audit.ts +201 -0
  425. package/src/op/activities/policy.test.ts +19 -0
  426. package/src/op/activities/policy.ts +37 -0
  427. package/src/op/activities/reconcile.test.ts +81 -0
  428. package/src/op/activities/reconcile.ts +187 -0
  429. package/src/op/activities/shell.ts +26 -0
  430. package/src/op/activities/teardown.ts +22 -0
  431. package/src/op/activities/util.ts +6 -0
  432. package/src/op/activities/wait.ts +69 -0
  433. package/src/op/activities/workflow-audit.test.ts +90 -0
  434. package/src/op/activities/workflow-audit.ts +203 -0
  435. package/src/op/activity-contract-registry.test.ts +98 -0
  436. package/src/op/activity-contract-registry.ts +114 -0
  437. package/src/op/activity-contract.test.ts +1 -1
  438. package/src/op/activity-contract.ts +37 -33
  439. package/src/op/activity-failure.ts +34 -0
  440. package/src/op/activity-profiles.test.ts +56 -0
  441. package/src/op/activity-profiles.ts +116 -0
  442. package/src/op/activity-registry.test.ts +37 -28
  443. package/src/op/activity-registry.ts +27 -56
  444. package/src/op/activity-runtime.ts +3 -47
  445. package/src/op/builders-exports.test.ts +0 -49
  446. package/src/op/builders-typed.test.ts +164 -0
  447. package/src/op/builders.ts +123 -39
  448. package/src/op/composites/apply-op.ts +208 -0
  449. package/src/op/composites/composites.test.ts +366 -0
  450. package/src/op/composites/converge-op.test.ts +168 -0
  451. package/src/op/composites/converge-op.ts +214 -0
  452. package/src/op/composites/index.ts +26 -0
  453. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  454. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  455. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  456. package/src/op/composites/pipeline-audit-op.ts +78 -0
  457. package/src/op/composites/reconcile-op.test.ts +51 -0
  458. package/src/op/composites/reconcile-op.ts +107 -0
  459. package/src/op/composites/watch-op.ts +108 -0
  460. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  461. package/src/op/composites/workflow-audit-op.ts +88 -0
  462. package/src/op/converge-rule.ts +12 -10
  463. package/src/op/cron.test.ts +128 -0
  464. package/src/op/cron.ts +149 -0
  465. package/src/op/discover-root.test.ts +105 -1
  466. package/src/op/discover.test.ts +6 -2
  467. package/src/op/discover.ts +84 -16
  468. package/src/op/duration.ts +21 -0
  469. package/src/op/effect-step.test.ts +26 -11
  470. package/src/op/emulator-lifecycle.ts +1 -2
  471. package/src/op/gate.ts +155 -0
  472. package/src/op/generate-pipeline.test.ts +65 -3
  473. package/src/op/generate-pipeline.ts +19 -2
  474. package/src/op/index.ts +39 -6
  475. package/src/op/local-executor.test.ts +163 -34
  476. package/src/op/local-executor.ts +370 -102
  477. package/src/op/local-output.test.ts +95 -11
  478. package/src/op/local-output.ts +33 -4
  479. package/src/op/op-ir.test.ts +281 -0
  480. package/src/op/op-ir.ts +417 -0
  481. package/src/op/op-verb-class.ts +19 -5
  482. package/src/op/op.test.ts +26 -5
  483. package/src/op/operator.test.ts +175 -6
  484. package/src/op/operator.ts +63 -16
  485. package/src/op/receipt-store.ts +1 -1
  486. package/src/op/resource.ts +20 -2
  487. package/src/op/runtime.ts +203 -0
  488. package/src/op/runtimes/local.test.ts +181 -0
  489. package/src/op/runtimes/local.ts +212 -0
  490. package/src/op/step-output-ref.ts +15 -13
  491. package/src/op/types.ts +84 -24
  492. package/src/ownership.ts +1 -1
  493. package/src/provenance.test.ts +6 -0
  494. package/src/provenance.ts +21 -1
  495. package/src/serializer-walker.test.ts +42 -0
  496. package/src/serializer-walker.ts +21 -2
  497. package/src/stack-output.ts +6 -1
  498. package/dist/cli/handlers/op-progress.d.ts +0 -90
  499. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  500. package/dist/cli/handlers/run-client.d.ts +0 -127
  501. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  502. package/dist/cli/handlers/run-report.d.ts +0 -5
  503. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  504. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  505. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  506. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  507. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  508. package/src/cli/handlers/op-progress.test.ts +0 -268
  509. package/src/cli/handlers/op-progress.ts +0 -248
  510. package/src/cli/handlers/run-client.test.ts +0 -82
  511. package/src/cli/handlers/run-client.ts +0 -219
  512. package/src/cli/handlers/run-report.test.ts +0 -62
  513. package/src/cli/handlers/run-report.ts +0 -122
  514. package/src/components/temporal-codegen-loader.ts +0 -41
  515. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -5,17 +5,17 @@
5
5
  * implementations are a separate later issue (#557). These tests exercise the
6
6
  * driver's generic behavior only: dependency ordering + parallel-safe waves,
7
7
  * sequential vs parallel step execution, `onFailure`/saga rollback in reverse
8
- * order, output wiring (`@Phase.field` and `@<component>.publish.*`), and that
9
- * a `gate` errors on the local executor. Several cases replay the real pilot
8
+ * order, output wiring (`@Phase.field` and `@<component>.publish.*`), and
9
+ * gate-as-fact on the local executor (#2119). Several cases replay the real pilot
10
10
  * declarations (../pilots/*.pilot.ts) as realistic inputs.
11
11
  */
12
12
 
13
13
  import { describe, expect, it } from "vitest";
14
14
  import { CapabilityRegistry, type DeployContext } from "./capability";
15
15
  import { stubCapability } from "./verbs/stub";
16
+ import { memoryGateLedgerPort } from "../op/gate";
16
17
  import {
17
18
  DependencyCycleError,
18
- DriverGateUnsupportedError,
19
19
  DriverRunFailure,
20
20
  UnknownDependencyError,
21
21
  accumulateComponentOutputs,
@@ -210,32 +210,102 @@ describe("runComponentDeploy — sequential vs parallel", () => {
210
210
  });
211
211
  });
212
212
 
213
- describe("runComponentDeploy — gate on the local executor", () => {
214
- it("throws DriverGateUnsupportedError when a phase contains a gate", async () => {
213
+ describe("runComponentDeploy — gate as fact (#2119)", () => {
214
+ const NOW = "2026-09-05T12:00:00.000Z";
215
+
216
+ const gatedComponent = (): DriverComponent => ({
217
+ name: "neo4j-cluster",
218
+ deploy: [
219
+ { phase: "Prepare", steps: [{ kind: "cfn-deploy" }] },
220
+ { phase: "Node 1", steps: [{ kind: "gate", signalName: "approve-node-1" }, { kind: "code-deploy" }] },
221
+ ],
222
+ rollback: [{ phase: "Undo", steps: [{ kind: "cfn-deploy" }] }],
223
+ });
224
+
225
+ function registryWithCalls() {
215
226
  const registry = new CapabilityRegistry();
216
- const component: DriverComponent = {
217
- name: "neo4j-cluster",
218
- deploy: [
219
- {
220
- phase: "Node 1",
221
- steps: [{ kind: "gate", signalName: "approve-node-1" }, { kind: "cfn-deploy" }],
222
- },
223
- ],
224
- };
225
- await expect(
226
- runComponentDeploy(component, { env: "dev", component: "neo4j-cluster" }, registry, {}),
227
- ).rejects.toThrow(DriverGateUnsupportedError);
227
+ const calls: string[] = [];
228
+ const rolledBack: string[] = [];
229
+ for (const kind of ["cfn-deploy", "code-deploy"]) {
230
+ registry.register(
231
+ fakeCapability(kind, {
232
+ run: () => { calls.push(kind); return { ok: true }; },
233
+ rollback: () => { rolledBack.push(kind); },
234
+ }).capability,
235
+ );
236
+ }
237
+ return { registry, calls, rolledBack };
238
+ }
239
+
240
+ it("ends gated, records the pending fact, and runs nothing after the gate", async () => {
241
+ const { registry, calls, rolledBack } = registryWithCalls();
242
+ const port = memoryGateLedgerPort();
243
+ const result = await runComponentDeploy(
244
+ gatedComponent(), { env: "dev", component: "neo4j-cluster" }, registry, {}, undefined,
245
+ { port, now: NOW },
246
+ );
247
+
248
+ expect(result.status).toBe("gated");
249
+ expect(result.ok).toBe(false);
250
+ expect(result.gate).toMatchObject({ op: "neo4j-cluster", gate: "approve-node-1" });
251
+ expect(port.appended).toHaveLength(1);
252
+ expect(calls).toEqual(["cfn-deploy"]);
253
+ // A gate is not a failure: nothing unwinds and no `rollback` phase runs.
254
+ expect(rolledBack).toEqual([]);
255
+ expect(result.records.map((r) => [r.kind, r.status])).toEqual([
256
+ ["cfn-deploy", "ok"],
257
+ ["gate:approve-node-1", "skipped"],
258
+ ["code-deploy", "skipped"],
259
+ ]);
228
260
  });
229
261
 
230
- it("the neo4j pilot's gated Node 1 phase errors locally (matches chant's local-executor behavior)", async () => {
262
+ it("a resolution newer than the pending fact passes the gate and carries the approver", async () => {
263
+ const { registry, calls } = registryWithCalls();
264
+ const port = memoryGateLedgerPort({
265
+ pending: [{
266
+ version: 1, kind: "pending", op: "neo4j-cluster", gate: "approve-node-1",
267
+ timestamp: "2026-09-05T10:00:00.000Z", expiresAt: "2026-09-07T10:00:00.000Z",
268
+ }],
269
+ resolutions: [{
270
+ version: 1, op: "neo4j-cluster", gate: "approve-node-1",
271
+ resolvedBy: "alex", timestamp: "2026-09-05T11:00:00.000Z",
272
+ }],
273
+ });
274
+ const result = await runComponentDeploy(
275
+ gatedComponent(), { env: "dev", component: "neo4j-cluster" }, registry, {}, undefined,
276
+ { port, now: NOW },
277
+ );
278
+
279
+ expect(result.status).toBe("ok");
280
+ expect(calls).toEqual(["cfn-deploy", "code-deploy"]);
281
+ expect(result.records.find((r) => r.kind === "gate:approve-node-1")?.approval)
282
+ .toEqual({ gate: "approve-node-1", resolvedBy: "alex", timestamp: "2026-09-05T11:00:00.000Z" });
283
+ });
284
+
285
+ it("the neo4j pilot's gated Node 1 phase ends the run pending approval", async () => {
231
286
  const registry = new CapabilityRegistry();
232
287
  for (const kind of ["cfn-deploy", "code-deploy", "wait-cluster-healthy"]) {
233
288
  registry.register(fakeCapability(kind, { run: () => ({ ok: true }) }).capability);
234
289
  }
235
290
  const json = projectToJson(neo4jCluster) as unknown as DriverComponent;
236
- await expect(runComponentDeploy(json, { env: "dev", component: "neo4j-cluster" }, registry, {})).rejects.toThrow(
237
- /gate "approve-neo4j-node-1" is not supported/,
291
+ const result = await runComponentDeploy(
292
+ json, { env: "dev", component: "neo4j-cluster" }, registry, {}, undefined,
293
+ { port: memoryGateLedgerPort(), now: NOW },
238
294
  );
295
+ expect(result.status).toBe("gated");
296
+ expect(result.gate?.gate).toBe("approve-neo4j-node-1");
297
+ });
298
+
299
+ it("a gated component stops the whole interpret run without throwing", async () => {
300
+ const { registry } = registryWithCalls();
301
+ const run = await runInterpretDriver([gatedComponent()], registry, {
302
+ env: "dev",
303
+ gates: memoryGateLedgerPort(),
304
+ now: NOW,
305
+ });
306
+ expect(run.status).toBe("gated");
307
+ expect(run.gatedComponent).toBe("neo4j-cluster");
308
+ expect(run.gate?.gate).toBe("approve-node-1");
239
309
  });
240
310
  });
241
311
 
@@ -415,7 +485,7 @@ describe("runComponentDeploy — output wiring", () => {
415
485
 
416
486
  describe("collectComponentOutputs / accumulateComponentOutputs — the shared accumulator (#700)", () => {
417
487
  // Exported so the durable path's `accumulateComponentOutputs` activity
418
- // (lexicons/temporal/src/component-op/activities.ts) captures outputs via
488
+ // (a hosting runtime's own step executor) captures outputs via
419
489
  // the same function `runComponentDeploy` does — the accumulation twin of
420
490
  // the already-shared `resolveStepInput`.
421
491
  const phaseOutputs = {
@@ -446,7 +516,7 @@ describe("collectComponentOutputs / accumulateComponentOutputs — the shared ac
446
516
  publish: { uri: "repo@sha256:abc", digest: "sha256:abc" },
447
517
  });
448
518
  expect(out.other).toEqual({ x: 1 });
449
- // Plain data throughout — survives the Temporal activity JSON boundary unchanged.
519
+ // Plain data throughout — survives an out-of-process JSON boundary unchanged.
450
520
  expect(JSON.parse(JSON.stringify(out))).toEqual(out);
451
521
  });
452
522
 
@@ -11,11 +11,18 @@
11
11
  * component.schema.json (`@Phase.field` prior-step references,
12
12
  * `@<component>.publish.<field>` cross-component artifact references, and
13
13
  * passes through `$env.*`/`stackOutput` values as opaque env config), runs
14
- * `parallel` phases concurrently, rejects a `gate` locally (matching
15
- * ../op/local-executor.ts's `LocalGateUnsupportedError`), and on terminal
16
- * failure unwinds every executed step in reverse via that step's capability
17
- * `rollback`, then runs `onFailure` phases in reverse order (best-effort),
18
- * mirroring the Op local executor's saga semantics.
14
+ * `parallel` phases concurrently, decides a `gate` against the gate ledger
15
+ * (#2119 — the same `../op/gate.ts` the Op executor uses, so `chant run
16
+ * --components` and `chant run <op>` can never disagree about what a gate
17
+ * means), and on terminal failure unwinds every executed step in reverse via
18
+ * that step's capability `rollback`, then runs `onFailure` phases in reverse
19
+ * order (best-effort), mirroring the Op local executor's saga semantics.
20
+ *
21
+ * A gate nobody has approved is not a failure: the component stops there with
22
+ * status `gated`, the pending fact is on the ledger, and no rollback and no
23
+ * `onFailure` phase runs — nothing failed and the steps that did run are the
24
+ * steps the author meant to run before the approval. The next run re-reads the
25
+ * ledger and continues past the gate if `chant approve` has answered it.
19
26
  *
20
27
  * A step whose capability declares no `rollback` is never silently passed
21
28
  * over during unwind: it gets a `"rollback-opted-out"` record (see
@@ -25,15 +32,13 @@
25
32
  * already requires at author time — the driver reports the same fact the
26
33
  * lint rule already made a component author state.
27
34
  *
28
- * This unwind runs entirely in-process: if the process crashes mid-rollback
29
- * on the local executor, nothing resumes it — there is no persisted run state
30
- * to resume from. That is the documented Temporal boundary (see
31
- * docs/components/orchestration.mdx#rollback-comes-free and
32
- * docs/guide/local-vs-temporal.mdx): the *mechanism* (reverse-order unwind
33
- * calling each capability's `rollback`) is capability-agnostic and works
34
- * identically on both executors, but *durable resume of a rollback already in
35
- * progress* is Temporal-only, the same boundary that already applies to
36
- * forward `onFailure` compensation on an Op.
35
+ * This unwind runs entirely in-process: if the process crashes mid-rollback,
36
+ * nothing resumes it — there is no persisted run state to resume from (see
37
+ * docs/components/orchestration.mdx#rollback-comes-free). The *mechanism*
38
+ * (reverse-order unwind calling each capability's `rollback`) is
39
+ * capability-agnostic; *durable resume of a rollback already in progress*
40
+ * would be a hosting runtime's to offer, the same boundary that already
41
+ * applies to forward `onFailure` compensation on an Op.
37
42
  *
38
43
  * Contains zero per-component logic: nothing in this module branches on a
39
44
  * component or capability `kind`/name. All behavior is either generic
@@ -42,8 +47,10 @@
42
47
  */
43
48
 
44
49
  import { topoSort } from "../codegen/topo-sort";
50
+ import { evaluateGate, gitGateLedgerPort, type GateLedgerPort } from "../op/gate";
51
+ import type { PendingGateRecord } from "../lifecycle/gate-ledger";
45
52
  import type { CapabilityRegistry, DeployContext } from "./capability";
46
- import type { RunProgressEvent } from "./run-progress";
53
+ import type { RunProgressEvent, RunProgressStatus } from "./run-progress";
47
54
 
48
55
  export type { RunProgressEvent } from "./run-progress";
49
56
 
@@ -61,7 +68,7 @@ export interface DriverStep {
61
68
  [param: string]: unknown;
62
69
  }
63
70
 
64
- /** A gate step — pauses for an external signal; unsupported on the local executor (schema `Gate`). */
71
+ /** A gate step — a human approval, decided against the gate ledger when the run reaches it (schema `Gate`). */
65
72
  export interface DriverGate {
66
73
  kind: "gate";
67
74
  signalName: string;
@@ -96,20 +103,6 @@ function isPhaseStep(step: DriverStep | DriverGate | DriverPhase): step is Drive
96
103
 
97
104
  // ── Errors ────────────────────────────────────────────────────────────────
98
105
 
99
- /** Thrown when a component's composition contains a `gate` — gates need a durable runtime, matching chant's Op local executor. */
100
- export class DriverGateUnsupportedError extends Error {
101
- constructor(
102
- public readonly component: string,
103
- public readonly signalName: string,
104
- ) {
105
- super(
106
- `component "${component}": gate "${signalName}" is not supported on the local executor — ` +
107
- `gates need a durable runtime. Re-run with a durable (Temporal) backend.`,
108
- );
109
- this.name = "DriverGateUnsupportedError";
110
- }
111
- }
112
-
113
106
  /** Thrown when a dependency cycle is found among `dependsOn` edges. */
114
107
  export class DependencyCycleError extends Error {
115
108
  constructor(public readonly cycle: string[]) {
@@ -147,12 +140,18 @@ export interface DriverStepRecord {
147
140
  durationMs: number;
148
141
  output?: unknown;
149
142
  error?: string;
143
+ /** Set on a `gate` step that passed (#2119): who resolved it, when, and at what address. */
144
+ approval?: { gate: string; resolvedBy: string; timestamp: string; url?: string };
150
145
  }
151
146
 
152
147
  export interface DriverComponentResult {
153
148
  component: string;
154
149
  ok: boolean;
150
+ /** Three-state outcome (#2119) — `gated` is `ok: false` without being a failure: the component stopped at a gate awaiting `chant approve`, and nothing was rolled back. */
151
+ status: "ok" | "fail" | "gated";
155
152
  records: DriverStepRecord[];
153
+ /** Present when `status === "gated"`: the pending fact this component stopped on. */
154
+ gate?: PendingGateRecord;
156
155
  }
157
156
 
158
157
  export interface DriverRunResult {
@@ -162,8 +161,14 @@ export interface DriverRunResult {
162
161
  waves: string[][];
163
162
  results: DriverComponentResult[];
164
163
  ok: boolean;
164
+ /** Three-state outcome (#2119), matching `OpRunResult.status`. */
165
+ status: "ok" | "fail" | "gated";
165
166
  /** Name of the component that terminated the run, if any. */
166
167
  failedComponent?: string;
168
+ /** Name of the component the run stopped at for approval, when `status === "gated"`. */
169
+ gatedComponent?: string;
170
+ /** The pending fact the run stopped on, when `status === "gated"`. */
171
+ gate?: PendingGateRecord;
167
172
  /**
168
173
  * The accumulated cross-component/cross-stack outputs after the run — each
169
174
  * component's `publish` output and, for an applied stack, its `cfn-deploy`
@@ -285,12 +290,11 @@ export function resolveWiring(
285
290
  * Deep-walk an object, resolving every string/stackOutput wiring value found.
286
291
  * Arrays and nested objects are walked too.
287
292
  *
288
- * Exported (not just used internally) so the durable Temporal path
289
- * (`@intentius/chant-lexicon-temporal`'s component workflow codegen, see
290
- * epic #551 #589) can resolve a step's wiring the same way inside a Temporal
291
- * *activity* — the workflow itself only accumulates `phaseOutputs`/
292
- * `componentOutputs` and passes them through; resolution logic stays in one
293
- * place so local and durable execution can never silently diverge.
293
+ * Exported (not just used internally) so a hosting runtime that runs steps
294
+ * out of process can resolve a step's wiring the same way — the orchestrator
295
+ * only accumulates `phaseOutputs`/`componentOutputs` and passes them through;
296
+ * resolution logic stays in one place so local and hosted execution can never
297
+ * silently diverge.
294
298
  */
295
299
  export function resolveStepInput(
296
300
  input: Record<string, unknown>,
@@ -338,6 +342,29 @@ class StepFailure extends Error {
338
342
  }
339
343
  }
340
344
 
345
+ /** Internal: a phase stopped at an unapproved gate (#2119). Carries what ran before it, so the records survive; not a failure, so no unwind follows. */
346
+ class GateStop extends Error {
347
+ constructor(
348
+ public readonly records: DriverStepRecord[],
349
+ public readonly executed: ExecutedStep[],
350
+ public readonly pending: PendingGateRecord,
351
+ ) {
352
+ super(`gate "${pending.gate}" is pending approval`);
353
+ this.name = "GateStop";
354
+ }
355
+ }
356
+
357
+ /** A result status as the progress stream spells it — the only difference is `fail` vs `failed`, which the two vocabularies have always disagreed on. */
358
+ function progressStatus(status: DriverComponentResult["status"]): RunProgressStatus {
359
+ return status === "fail" ? "failed" : status;
360
+ }
361
+
362
+ /** How a component's run reaches the gate ledger — see `../op/gate.ts`. */
363
+ export interface GateContext {
364
+ port: GateLedgerPort;
365
+ now?: string;
366
+ }
367
+
341
368
  /** Run a single capability step. Never throws for a capability-run failure; returns a fail record instead. */
342
369
  async function runCapabilityStep(
343
370
  step: DriverStep,
@@ -381,11 +408,16 @@ async function runCapabilityStep(
381
408
  }
382
409
 
383
410
  /**
384
- * Run a phase's steps. A step may be a `Gate` (rejected — local executor has
385
- * no durable wait) or a nested `Phase` (a fan-out unit; recursed into,
386
- * inheriting `parallel` from its own definition, not its parent's). Steps run
387
- * sequentially unless `phase.parallel` is set, in which case they run via
388
- * `Promise.all`, matching ../op/local-executor.ts's phase semantics.
411
+ * Run a phase's steps. A step may be a `Gate` (decided against the gate
412
+ * ledger, in authored order — a resolution newer than the gate's newest
413
+ * pending fact lets it pass carrying the approver, anything else records the
414
+ * fact and stops the component) or a nested `Phase` (a fan-out unit; recursed
415
+ * into, inheriting `parallel` from its own definition, not its parent's).
416
+ * Steps run sequentially unless `phase.parallel` is set, in which case they run
417
+ * via `Promise.all`, matching ../op/local-executor.ts's phase semantics — and
418
+ * as there, a gate in a parallel phase is decided before the fan-out, since a
419
+ * gate is a read of the ledger rather than work, and starting activities a
420
+ * pending gate is about to strand defeats the point of stopping at it.
389
421
  *
390
422
  * `onProgress`, when supplied, is called with a `phase-start` event before any
391
423
  * step runs, a `step` event around each capability invocation (`"running"`
@@ -402,22 +434,60 @@ async function runPhase(
402
434
  registry: CapabilityRegistry,
403
435
  phaseOutputs: Record<string, Record<string, unknown>>,
404
436
  componentOutputs: Record<string, Record<string, unknown>>,
437
+ gates: GateContext,
405
438
  onProgress?: (event: RunProgressEvent) => void,
406
439
  ): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> {
407
- const gate = phaseDef.steps.find(isGateStep);
408
- if (gate) throw new DriverGateUnsupportedError(ctx.component, gate.signalName);
440
+ /** Decide this phase's gates against the ledger, in authored order, before any of its work starts. Throws `GateStop` at the first one nobody has approved. */
441
+ const decideGates = async (): Promise<DriverStepRecord[]> => {
442
+ const gateRecords: DriverStepRecord[] = [];
443
+ for (const gate of phaseDef.steps.filter(isGateStep)) {
444
+ const start = Date.now();
445
+ const check = await evaluateGate(gates.port, {
446
+ op: ctx.component,
447
+ gate: gate.signalName,
448
+ ...(gate.description ? { description: gate.description } : {}),
449
+ ...(gate.timeout ? { timeout: gate.timeout } : {}),
450
+ ...(gates.now ? { now: gates.now } : {}),
451
+ });
452
+ const base = { component: ctx.component, phase: phaseDef.phase, kind: `gate:${gate.signalName}` };
453
+ if (!check.satisfied) {
454
+ gateRecords.push({ ...base, status: "skipped" as const, durationMs: Date.now() - start });
455
+ for (const skipped of phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s))) {
456
+ gateRecords.push(skippedRecord(skipped));
457
+ }
458
+ throw new GateStop(gateRecords, [], check.pending);
459
+ }
460
+ gateRecords.push({
461
+ ...base,
462
+ status: "ok" as const,
463
+ durationMs: Date.now() - start,
464
+ approval: {
465
+ gate: gate.signalName,
466
+ resolvedBy: check.resolution.resolvedBy,
467
+ timestamp: check.resolution.timestamp,
468
+ ...(check.resolution.url ? { url: check.resolution.url } : {}),
469
+ },
470
+ });
471
+ }
472
+ return gateRecords;
473
+ };
409
474
 
410
475
  const entries = phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s));
411
476
 
412
477
  const runEntry = async (
413
478
  entry: DriverStep | DriverPhase,
414
- ): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[]; failed: boolean }> => {
479
+ ): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[]; failed: boolean; pending?: PendingGateRecord }> => {
415
480
  if (isPhaseStep(entry)) {
416
481
  try {
417
- const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs, onProgress);
482
+ const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs, gates, onProgress);
418
483
  return { ...nested, failed: false };
419
484
  } catch (err) {
420
485
  if (err instanceof StepFailure) return { records: err.records, executed: err.executed, failed: true };
486
+ // A nested fan-out phase's gate stops the whole component, but the
487
+ // records it produced before the gate still belong in the run.
488
+ if (err instanceof GateStop) {
489
+ return { records: err.records, executed: err.executed, failed: false, pending: err.pending };
490
+ }
421
491
  throw err;
422
492
  }
423
493
  }
@@ -448,32 +518,38 @@ async function runPhase(
448
518
  };
449
519
  };
450
520
 
521
+ const skippedRecord = (entry: DriverStep | DriverPhase): DriverStepRecord => ({
522
+ component: ctx.component,
523
+ phase: phaseDef.phase,
524
+ kind: isPhaseStep(entry) ? entry.phase : (entry as DriverStep).kind,
525
+ status: "skipped",
526
+ durationMs: 0,
527
+ });
528
+
451
529
  const runEntries = async (): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> => {
530
+ const gateRecords = await decideGates();
452
531
  if (phaseDef.parallel) {
453
532
  const results = await Promise.all(entries.map(runEntry));
454
- const records = results.flatMap((r) => r.records);
533
+ const records = gateRecords.concat(results.flatMap((r) => r.records));
455
534
  const executed = results.flatMap((r) => r.executed);
535
+ const pending = results.find((r) => r.pending)?.pending;
536
+ if (pending) throw new GateStop(records, executed, pending);
456
537
  if (results.some((r) => r.failed)) throw new StepFailure(records, executed);
457
538
  return { records, executed };
458
539
  }
459
540
 
460
- const records: DriverStepRecord[] = [];
541
+ const records: DriverStepRecord[] = [...gateRecords];
461
542
  const executed: ExecutedStep[] = [];
462
543
  for (let i = 0; i < entries.length; i++) {
463
544
  const result = await runEntry(entries[i]);
464
545
  records.push(...result.records);
465
546
  executed.push(...result.executed);
547
+ if (result.pending) {
548
+ for (const skipped of entries.slice(i + 1)) records.push(skippedRecord(skipped));
549
+ throw new GateStop(records, executed, result.pending);
550
+ }
466
551
  if (result.failed) {
467
- for (const skipped of entries.slice(i + 1)) {
468
- const skippedKind = isPhaseStep(skipped) ? skipped.phase : (skipped as DriverStep).kind;
469
- records.push({
470
- component: ctx.component,
471
- phase: phaseDef.phase,
472
- kind: skippedKind,
473
- status: "skipped",
474
- durationMs: 0,
475
- });
476
- }
552
+ for (const skipped of entries.slice(i + 1)) records.push(skippedRecord(skipped));
477
553
  throw new StepFailure(records, executed);
478
554
  }
479
555
  }
@@ -486,7 +562,9 @@ async function runPhase(
486
562
  onProgress?.({ type: "phase-done", component: ctx.component, phase: phaseDef.phase, status: "ok" });
487
563
  return result;
488
564
  } catch (err) {
489
- if (err instanceof StepFailure) {
565
+ if (err instanceof GateStop) {
566
+ onProgress?.({ type: "phase-done", component: ctx.component, phase: phaseDef.phase, status: "gated" });
567
+ } else if (err instanceof StepFailure) {
490
568
  onProgress?.({ type: "phase-done", component: ctx.component, phase: phaseDef.phase, status: "failed" });
491
569
  }
492
570
  throw err;
@@ -577,6 +655,7 @@ export async function runComponentDeploy(
577
655
  registry: CapabilityRegistry,
578
656
  componentOutputs: Record<string, Record<string, unknown>>,
579
657
  onProgress?: (event: RunProgressEvent) => void,
658
+ gates: GateContext = { port: gitGateLedgerPort() },
580
659
  ): Promise<DriverComponentResult> {
581
660
  const phaseOutputs: Record<string, Record<string, unknown>> = {};
582
661
  const records: DriverStepRecord[] = [];
@@ -584,11 +663,20 @@ export async function runComponentDeploy(
584
663
 
585
664
  try {
586
665
  for (const phaseDef of component.deploy) {
587
- const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, onProgress);
666
+ const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates, onProgress);
588
667
  records.push(...result.records);
589
668
  allExecuted.push(...result.executed);
590
669
  }
591
670
  } catch (err) {
671
+ // A pending gate stops the component where it stands (#2119): no saga
672
+ // unwind and no `rollback` phase. The steps before the gate ran on
673
+ // purpose and are meant to survive the wait for an approval — undoing
674
+ // them would make every gated run a no-op with a rollback attached.
675
+ if (err instanceof GateStop) {
676
+ records.push(...err.records);
677
+ return { component: component.name, ok: false, status: "gated", records, gate: err.pending };
678
+ }
679
+
592
680
  if (err instanceof StepFailure) {
593
681
  records.push(...err.records);
594
682
  allExecuted.push(...err.executed);
@@ -600,24 +688,24 @@ export async function runComponentDeploy(
600
688
 
601
689
  for (const phaseDef of [...(component.rollback ?? [])].reverse()) {
602
690
  try {
603
- const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, onProgress);
691
+ const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates, onProgress);
604
692
  records.push(...result.records);
605
693
  } catch (compErr) {
606
- if (compErr instanceof StepFailure) records.push(...compErr.records);
694
+ if (compErr instanceof StepFailure || compErr instanceof GateStop) records.push(...compErr.records);
607
695
  else throw compErr;
608
696
  }
609
697
  }
610
698
 
611
- return { component: component.name, ok: false, records };
699
+ return { component: component.name, ok: false, status: "fail", records };
612
700
  }
613
701
 
614
702
  // Publish-family and stack outputs become this component's entry in
615
- // `componentOutputs` — through the same exported accumulator the durable
616
- // path's `accumulateComponentOutputs` activity calls (#700), so local and
617
- // Temporal runs can never diverge on what downstream references see.
703
+ // `componentOutputs` — through the same exported accumulator a hosted
704
+ // runtime calls (#700), so local and hosted runs can never diverge on what
705
+ // downstream references see.
618
706
  accumulateComponentOutputs(componentOutputs, component.name, phaseOutputs);
619
707
 
620
- return { component: component.name, ok: true, records };
708
+ return { component: component.name, ok: true, status: "ok", records };
621
709
  }
622
710
 
623
711
  /**
@@ -641,9 +729,8 @@ export async function runComponentDeploy(
641
729
  * the driver free of per-capability branching. Returns `undefined` when the
642
730
  * component exposed nothing.
643
731
  *
644
- * Exported, like `resolveStepInput`, so the durable Temporal path
645
- * (lexicons/temporal/src/component-op/activities.ts) accumulates outputs via
646
- * this exact function rather than re-deriving it (#700). The resolver and the
732
+ * Exported, like `resolveStepInput`, so a hosting runtime accumulates outputs
733
+ * via this exact function rather than re-deriving it (#700). The resolver and the
647
734
  * accumulator are the two halves of one contract; sharing only the resolver
648
735
  * is how the cross-stack gap #699 closed locally could reopen durably.
649
736
  */
@@ -659,11 +746,11 @@ export function collectComponentOutputs(
659
746
  /**
660
747
  * Record a finished component's outputs (`collectComponentOutputs`) into the
661
748
  * shared `componentOutputs` map under the component's own name, merging over
662
- * any seeded entry (`--seed-outputs`, or a durable parent workflow's
663
- * thread-through). Mutates and returns `componentOutputs`, so a Temporal
664
- * activity can hand the updated map back to its workflow over the JSON
665
- * boundary — every value in it is plain activity-result data, so the map is
666
- * serializable by construction. A no-op when the component exposed nothing.
749
+ * any seeded entry (`--seed-outputs`, or a parent orchestrator's
750
+ * thread-through). Mutates and returns `componentOutputs`, so an out-of-process
751
+ * step can hand the updated map back over a JSON boundary — every value in it
752
+ * is plain result data, so the map is serializable by construction. A no-op
753
+ * when the component exposed nothing.
667
754
  */
668
755
  export function accumulateComponentOutputs(
669
756
  componentOutputs: Record<string, Record<string, unknown>>,
@@ -736,6 +823,10 @@ export interface InterpretRunOptions {
736
823
  * byte-for-byte the same as before this option existed.
737
824
  */
738
825
  onProgress?: (event: RunProgressEvent) => void;
826
+ /** Where a `gate` step's facts are read and written (#2119). Defaults to the `chant/lifecycle` orphan branch; a test passes `memoryGateLedgerPort()` from `../op/gate.ts`. */
827
+ gates?: GateLedgerPort;
828
+ /** ISO-8601 "now" for gate decisions, so a test is deterministic. */
829
+ now?: string;
739
830
  }
740
831
 
741
832
  /**
@@ -748,6 +839,11 @@ export interface InterpretRunOptions {
748
839
  * a result with `ok: false`; throws `DriverRunFailure` carrying that result
749
840
  * so callers can choose to inspect or propagate it.
750
841
  *
842
+ * A component that stops at an unapproved gate (#2119) ends the run the same
843
+ * way, with `status: "gated"` and the pending fact on `gate` — but it is
844
+ * *returned*, never thrown, because nothing failed. Downstream waves do not
845
+ * run: a gate is the author saying a human decides before the rest happens.
846
+ *
751
847
  * Zero per-component logic: this function and everything it calls dispatches
752
848
  * purely on the generic `Component`/`Phase`/`Step` shapes and the registry —
753
849
  * no branch anywhere names a specific component or capability.
@@ -761,11 +857,16 @@ export async function runInterpretDriver(
761
857
  const byName = new Map(components.map((c) => [c.name, c]));
762
858
  const componentOutputs: Record<string, Record<string, unknown>> = { ...(options.componentOutputs ?? {}) };
763
859
  const { onProgress } = options;
860
+ const gates: GateContext = {
861
+ port: options.gates ?? gitGateLedgerPort(),
862
+ ...(options.now ? { now: options.now } : {}),
863
+ };
764
864
 
765
865
  onProgress?.({ type: "run-start", waves });
766
866
 
767
867
  const results: DriverComponentResult[] = [];
768
868
  let failedComponent: string | undefined;
869
+ let gated: DriverComponentResult | undefined;
769
870
 
770
871
  waveLoop: for (const [waveIndex, wave] of waves.entries()) {
771
872
  const waveNum = waveIndex + 1;
@@ -780,23 +881,32 @@ export async function runInterpretDriver(
780
881
  registry,
781
882
  componentOutputs,
782
883
  onProgress,
884
+ gates,
783
885
  );
784
- onProgress?.({ type: "component-done", wave: waveNum, component: component.name, status: result.ok ? "ok" : "failed" });
886
+ onProgress?.({ type: "component-done", wave: waveNum, component: component.name, status: progressStatus(result.status) });
785
887
  return result;
786
888
  }),
787
889
  );
788
890
  results.push(...waveResults);
789
- const failed = waveResults.find((r) => !r.ok);
790
- onProgress?.({ type: "wave-done", wave: waveNum, status: failed ? "failed" : "ok" });
791
- if (failed) {
792
- failedComponent = failed.component;
891
+ const stopped = waveResults.find((r) => r.status !== "ok");
892
+ onProgress?.({ type: "wave-done", wave: waveNum, status: progressStatus(stopped?.status ?? "ok") });
893
+ if (stopped) {
894
+ // A failure in the same wave outranks a gate: something is actually
895
+ // broken, and reporting the run as "waiting on a human" would hide it.
896
+ const failure = waveResults.find((r) => r.status === "fail");
897
+ if (failure) failedComponent = failure.component;
898
+ else gated = stopped;
793
899
  break waveLoop;
794
900
  }
795
901
  }
796
902
 
797
- const ok = failedComponent === undefined;
798
- onProgress?.({ type: "run-done", status: ok ? "ok" : "failed" });
799
- const result: DriverRunResult = { order, waves, results, ok, failedComponent, componentOutputs };
800
- if (!ok) throw new DriverRunFailure(result);
903
+ const status: DriverRunResult["status"] = failedComponent ? "fail" : gated ? "gated" : "ok";
904
+ const ok = status === "ok";
905
+ onProgress?.({ type: "run-done", status: progressStatus(status) });
906
+ const result: DriverRunResult = {
907
+ order, waves, results, ok, status, failedComponent, componentOutputs,
908
+ ...(gated ? { gatedComponent: gated.component, gate: gated.gate } : {}),
909
+ };
910
+ if (status === "fail") throw new DriverRunFailure(result);
801
911
  return result;
802
912
  }