@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
@@ -20,13 +20,19 @@ import {
20
20
  } from "../../op/operator";
21
21
  import { readLease, DEFAULT_LEASE_TTL_MS } from "../../lifecycle/lease";
22
22
  import { readConvergeLedger, type ConvergeTickRecord } from "../../lifecycle/converge-ledger";
23
- import type { GateResolutionRecord } from "../../lifecycle/gate-ledger";
24
- import { appendGateResolution, readGateResolutions, latestResolutionSince, resolveApprovalUrl, isApprovalUrl } from "../../lifecycle/gate-ledger";
23
+ import { readRunLedger } from "../../lifecycle/run-ledger";
24
+ import type { OpRunRecord } from "../../op/runtime";
25
+ import type { GateResolutionRecord, PendingGateRecord } from "../../lifecycle/gate-ledger";
26
+ import {
27
+ appendGateResolution, appendPendingGate, readGateResolutions, readGateLedger,
28
+ latestResolutionSince, latestPendingGate, isPendingGateExpired,
29
+ resolveApprovalUrl, isApprovalUrl,
30
+ } from "../../lifecycle/gate-ledger";
25
31
  import { pushLifecycle } from "../../lifecycle/git";
26
32
  import { formatError, formatWarning, formatSuccess, formatBold, formatInfo } from "../format";
27
33
  import type { CommandContext } from "../registry";
28
34
 
29
- /** Load the activities a local tick needs, the same way `runOpLocal` does: base temporal activities plus whatever the project's configured lexicons contribute. Best-effort on the lexicon list — an unreadable `chant.config.ts` falls back to base activities only. */
35
+ /** Load the activities a local tick needs, the same way a local run does: core's base activities plus whatever the project's configured lexicons contribute. Best-effort on the lexicon list — an unreadable `chant.config.ts` falls back to base activities only. */
30
36
  async function loadOperatorActivities() {
31
37
  let lexicons: string[] = [];
32
38
  try {
@@ -42,7 +48,7 @@ async function loadOperatorActivities() {
42
48
  /**
43
49
  * `chant operator [--env <env>] [--interval <duration>] [--lease-ttl
44
50
  * <duration>] [--once]` — run scheduled ticks for this project's discovered
45
- * ConvergeOps, locally, with no Temporal installed (issue's own worked
51
+ * ConvergeOps, locally, with no service to install (issue's own worked
46
52
  * example). `--once` runs a single round and exits (also the offline test
47
53
  * story, and what a cron/systemd-timer/k8s-CronJob invoker uses instead of
48
54
  * leaving the daemon running); omitted, the daemon loops until Ctrl-C.
@@ -113,11 +119,37 @@ interface OpStatusLine {
113
119
  op: string;
114
120
  env: string;
115
121
  lastTick?: ConvergeTickRecord;
116
- /** `url` (#2028) is the gate's approval surface, when the tick that recorded it knew one — what a renderer points its approve affordance at instead of a shell command. */
117
- pendingGates: { rule: string; op?: string; gate: string; url?: string }[];
122
+ /** `url` (#2028) is the gate's approval surface, when whatever recorded it knew one — what a renderer points its approve affordance at instead of a shell command. `rule` is present only for a gate a converge rule's dispatch reached; a gate an ordinary `chant run` stopped at has none. */
123
+ pendingGates: { rule?: string; op?: string; gate: string; url?: string; expiresAt?: string; description?: string }[];
118
124
  lease?: { holder: string; expiresAt: string };
119
125
  }
120
126
 
127
+ /**
128
+ * Every gate an op is currently standing at, read from its own gate ledger
129
+ * (#2119) — a live (unexpired) pending fact with no resolution newer than it.
130
+ *
131
+ * Ledger-first, so `chant operator status` sees a gate any run recorded, not
132
+ * only one a converge tick's dispatch reached. Before this, a plain `chant run
133
+ * <op>` could not produce a pending gate at all (the executor refused the op
134
+ * outright), so reading the converge ledger's `gated` outcomes was the whole
135
+ * story; now it is one source among the ops' own.
136
+ */
137
+ export async function pendingGatesFor(
138
+ opName: string,
139
+ opts: { cwd?: string; now?: string } = {},
140
+ ): Promise<PendingGateRecord[]> {
141
+ const now = opts.now ?? new Date().toISOString();
142
+ const { resolutions, pending } = await readGateLedger(opName, { cwd: opts.cwd });
143
+ const standing: PendingGateRecord[] = [];
144
+ for (const gate of new Set(pending.map((p) => p.gate))) {
145
+ const latest = latestPendingGate(pending, gate);
146
+ if (!latest || isPendingGateExpired(latest, now)) continue;
147
+ if (latestResolutionSince(resolutions, gate, latest.timestamp)) continue;
148
+ standing.push(latest);
149
+ }
150
+ return standing.sort((a, b) => a.gate.localeCompare(b.gate));
151
+ }
152
+
121
153
  async function statusFor(opName: string, env: string, cwd?: string): Promise<OpStatusLine> {
122
154
  const [{ records }, { record: lease }] = await Promise.all([
123
155
  readConvergeLedger(env, { cwd }),
@@ -127,19 +159,47 @@ async function statusFor(opName: string, env: string, cwd?: string): Promise<OpS
127
159
  const lastTick = ownRecords.at(-1);
128
160
 
129
161
  const pendingGates: OpStatusLine["pendingGates"] = [];
162
+ const seen = new Set<string>();
163
+
164
+ // The dispatched ops this ConvergeOp's last tick recorded as gated, plus the
165
+ // ConvergeOp itself — a gate can sit on either.
166
+ const gateOps = new Set<string>([opName]);
167
+ const ruleFor = new Map<string, string>();
168
+ for (const outcome of lastTick?.outcomes ?? []) {
169
+ if (outcome.action !== "gated" || !outcome.gateName || !outcome.op) continue;
170
+ gateOps.add(outcome.op);
171
+ ruleFor.set(`${outcome.op}\0${outcome.gateName}`, outcome.ruleId);
172
+ }
173
+
174
+ for (const gateOp of [...gateOps].sort()) {
175
+ for (const record of await pendingGatesFor(gateOp, { cwd })) {
176
+ seen.add(`${gateOp}\0${record.gate}`);
177
+ pendingGates.push({
178
+ op: gateOp,
179
+ gate: record.gate,
180
+ expiresAt: record.expiresAt,
181
+ ...(ruleFor.get(`${gateOp}\0${record.gate}`) ? { rule: ruleFor.get(`${gateOp}\0${record.gate}`) } : {}),
182
+ ...(record.description ? { description: record.description } : {}),
183
+ ...(record.url ? { url: record.url } : {}),
184
+ });
185
+ }
186
+ }
187
+
188
+ // A tick that recorded a `gated` outcome before the executor started writing
189
+ // pending facts (#2119) still has nothing in `_gates` to read; fall back to
190
+ // the outcome itself so an in-flight gate isn't dropped on upgrade.
130
191
  if (lastTick) {
131
192
  for (const outcome of lastTick.outcomes) {
132
193
  if (outcome.action !== "gated" || !outcome.gateName || !outcome.op) continue;
194
+ if (seen.has(`${outcome.op}\0${outcome.gateName}`)) continue;
133
195
  const { records: resolutions } = await readGateResolutions(outcome.op, { cwd });
134
- const resolved = latestResolutionSince(resolutions, outcome.gateName, lastTick.timestamp);
135
- if (!resolved) {
136
- pendingGates.push({
137
- rule: outcome.ruleId,
138
- op: outcome.op,
139
- gate: outcome.gateName,
140
- ...(outcome.url ? { url: outcome.url } : {}),
141
- });
142
- }
196
+ if (latestResolutionSince(resolutions, outcome.gateName, lastTick.timestamp)) continue;
197
+ pendingGates.push({
198
+ rule: outcome.ruleId,
199
+ op: outcome.op,
200
+ gate: outcome.gateName,
201
+ ...(outcome.url ? { url: outcome.url } : {}),
202
+ });
143
203
  }
144
204
  }
145
205
 
@@ -152,6 +212,15 @@ async function statusFor(opName: string, env: string, cwd?: string): Promise<OpS
152
212
  };
153
213
  }
154
214
 
215
+ /** One op standing at a gate that no ConvergeOp row above already reported (#2119) — an ordinary `chant run <op>` records these now, so they belong on this page even where no converge tick was ever involved. */
216
+ export interface StandaloneGateLine {
217
+ op: string;
218
+ gate: string;
219
+ description?: string;
220
+ expiresAt: string;
221
+ url?: string;
222
+ }
223
+
155
224
  /**
156
225
  * `chant operator status [--env <env>] [--json]` — last tick, outcome
157
226
  * counts, and pending gates, read from the `chant/lifecycle` orphan branch
@@ -159,22 +228,46 @@ async function statusFor(opName: string, env: string, cwd?: string): Promise<OpS
159
228
  * running: everything here was already durably recorded by whichever
160
229
  * process (this operator, a bare `chant run <op>`, or another machine's
161
230
  * operator) ran the last tick.
231
+ *
232
+ * Pending gates are read from every discovered op's own gate ledger, not only
233
+ * from a converge tick's `gated` outcomes (#2119) — a gate an ordinary `chant
234
+ * run <op>` stopped at is the same standing fact and shows up the same way,
235
+ * under "pending gates" for ops with no converge tick behind them.
162
236
  */
163
237
  export async function runOperatorStatus(ctx: CommandContext): Promise<number> {
164
238
  const { ops, errors } = await discoverConvergeOps({ env: ctx.args.env });
165
239
  for (const err of errors) console.error(formatWarning({ message: err }));
166
240
 
167
- if (ops.length === 0) {
241
+ const rows = await Promise.all(
242
+ ops.map((d) => statusFor(d.config.name, d.config.labels?.Env ?? "unknown")),
243
+ );
244
+
245
+ // Every other discovered op that is standing at a gate. `discoverOps` sees
246
+ // the whole project, so an ApplyOp someone ran by hand this morning is here
247
+ // even though no ConvergeOp ever dispatched it.
248
+ const covered = new Set(rows.flatMap((r) => r.pendingGates.map((g) => `${g.op ?? r.op} ${g.gate}`)));
249
+ const { ops: allOps } = await discoverOps();
250
+ const standalone: StandaloneGateLine[] = [];
251
+ for (const opName of [...allOps.keys()].sort()) {
252
+ for (const record of await pendingGatesFor(opName)) {
253
+ if (covered.has(`${opName} ${record.gate}`)) continue;
254
+ standalone.push({
255
+ op: opName,
256
+ gate: record.gate,
257
+ expiresAt: record.expiresAt,
258
+ ...(record.description ? { description: record.description } : {}),
259
+ ...(record.url ? { url: record.url } : {}),
260
+ });
261
+ }
262
+ }
263
+
264
+ if (ops.length === 0 && standalone.length === 0) {
168
265
  console.error(formatWarning({ message: "No ConvergeOp declarations found" }));
169
266
  return 0;
170
267
  }
171
268
 
172
- const rows = await Promise.all(
173
- ops.map((d) => statusFor(d.config.name, d.config.searchAttributes?.Env ?? "unknown")),
174
- );
175
-
176
269
  if (ctx.args.json) {
177
- console.log(JSON.stringify(rows, null, 2));
270
+ console.log(JSON.stringify(standalone.length > 0 ? { rows, pendingGates: standalone } : rows, null, 2));
178
271
  return 0;
179
272
  }
180
273
 
@@ -190,13 +283,28 @@ export async function runOperatorStatus(ctx: CommandContext): Promise<number> {
190
283
  if (row.pendingGates.length > 0) {
191
284
  console.log(` pending gates:`);
192
285
  for (const g of row.pendingGates) {
193
- console.log(` - ${g.op ?? row.op} gate "${g.gate}" (rule ${g.rule}) — resolve: chant approve ${g.op ?? row.op} ${g.gate}`);
286
+ const op = g.op ?? row.op;
287
+ console.log(
288
+ ` - ${op} gate "${g.gate}"${g.rule ? ` (rule ${g.rule})` : ""} — resolve: chant approve ${op} ${g.gate}`,
289
+ );
290
+ if (g.expiresAt) console.log(` expires: ${g.expiresAt}`);
194
291
  if (g.url) console.log(` approve at: ${g.url}`);
195
292
  }
196
293
  }
197
294
  console.log("");
198
295
  }
199
296
 
297
+ if (standalone.length > 0) {
298
+ console.log(formatBold("pending gates (no converge tick)"));
299
+ for (const g of standalone) {
300
+ console.log(` - ${g.op} gate "${g.gate}" — resolve: chant approve ${g.op} ${g.gate}`);
301
+ if (g.description) console.log(` ${g.description}`);
302
+ console.log(` expires: ${g.expiresAt}`);
303
+ if (g.url) console.log(` approve at: ${g.url}`);
304
+ }
305
+ console.log("");
306
+ }
307
+
200
308
  return 0;
201
309
  }
202
310
 
@@ -205,17 +313,20 @@ export async function runOperatorStatus(ctx: CommandContext): Promise<number> {
205
313
  /** One entry of the merged tick/gate timeline `chant operator log` prints. */
206
314
  export type OperatorLogEntry =
207
315
  | { kind: "tick"; timestamp: string; record: ConvergeTickRecord }
208
- | { kind: "gate-resolution"; timestamp: string; record: GateResolutionRecord };
316
+ | { kind: "gate-resolution"; timestamp: string; record: GateResolutionRecord }
317
+ /** One Op run (#2118), timestamped at the instant it ended — the tick that dispatched it precedes it. */
318
+ | { kind: "run"; timestamp: string; record: OpRunRecord };
209
319
 
210
320
  export interface OperatorLogResult {
211
321
  entries: OperatorLogEntry[];
212
322
  /**
213
- * Unreadable lines behind this answer, per ledger. `readConvergeLedger` and
214
- * `readGateResolutions` both skip a malformed line and count it rather than
215
- * throwing, so a corrupted ledger renders a shorter timeline; without this a
216
- * consumer could not tell that from a genuinely quiet environment.
323
+ * Unreadable lines behind this answer, per ledger. `readConvergeLedger`,
324
+ * `readGateResolutions` and `readRunLedger` all skip a malformed line and
325
+ * count it rather than throwing, so a corrupted ledger renders a shorter
326
+ * timeline; without this a consumer could not tell that from a genuinely
327
+ * quiet environment.
217
328
  */
218
- malformed: { converge: number; gates: number };
329
+ malformed: { converge: number; gates: number; runs: number };
219
330
  }
220
331
 
221
332
  /**
@@ -234,7 +345,7 @@ export async function collectOperatorLog(
234
345
  const sinceMs = opts.since ? new Date(opts.since).getTime() : undefined;
235
346
 
236
347
  const entries: OperatorLogEntry[] = [];
237
- const malformed = { converge: 0, gates: 0 };
348
+ const malformed = { converge: 0, gates: 0, runs: 0 };
238
349
 
239
350
  // One read per distinct environment — several ConvergeOps can share one
240
351
  // `<env>/converge.jsonl`, and re-reading it per op would both cost more and
@@ -263,11 +374,23 @@ export async function collectOperatorLog(
263
374
  }
264
375
  }
265
376
 
377
+ // The runs each discovered Op recorded (#2118). Keyed `<env>/runs__<op>`,
378
+ // so unlike the converge ledger this is one read per (env, op) pair rather
379
+ // than per env — a ConvergeOp's own ticks and its own runs interleave here.
380
+ for (const { name, env } of wanted) {
381
+ const { records, malformed: bad } = await readRunLedger(env, name, { cwd: opts.cwd });
382
+ malformed.runs += bad;
383
+ for (const record of records) {
384
+ entries.push({ kind: "run", timestamp: record.ended, record });
385
+ }
386
+ }
387
+
388
+ // Within one instant: the tick first (it is what dispatched anything else),
389
+ // then the run it dispatched, then the gate resolution against that run.
390
+ const rank: Record<OperatorLogEntry["kind"], number> = { tick: 0, run: 1, "gate-resolution": 2 };
266
391
  let merged = entries.sort((a, b) => {
267
392
  const delta = new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
268
- // A gate resolution recorded in the same instant as a tick reads after it:
269
- // the tick is what made the gate pending in the first place.
270
- return delta !== 0 ? delta : (a.kind === b.kind ? 0 : a.kind === "tick" ? -1 : 1);
393
+ return delta !== 0 ? delta : rank[a.kind] - rank[b.kind];
271
394
  });
272
395
  if (sinceMs !== undefined) merged = merged.filter((e) => new Date(e.timestamp).getTime() >= sinceMs);
273
396
  if (opts.limit !== undefined && merged.length > opts.limit) merged = merged.slice(-opts.limit);
@@ -276,6 +399,17 @@ export async function collectOperatorLog(
276
399
  }
277
400
 
278
401
  function renderLogEntry(entry: OperatorLogEntry): string[] {
402
+ if (entry.kind === "run") {
403
+ const { record } = entry;
404
+ const id = `[${record.id.slice(0, 8)}]`;
405
+ const outcomes = Object.entries(record.outcomes).map(([k, v]) => `${k}=${String(v)}`).join(" ");
406
+ const steps = record.phases.reduce((n, p) => n + p.steps.length, 0);
407
+ return [
408
+ `${record.ended} ${record.op}@${record.env} ${id} run ${record.status} ` +
409
+ `phases=${record.phases.length} steps=${steps}${outcomes ? ` ${outcomes}` : ""}`,
410
+ ];
411
+ }
412
+
279
413
  if (entry.kind === "gate-resolution") {
280
414
  const { record } = entry;
281
415
  return [
@@ -335,7 +469,7 @@ export async function runOperatorLog(ctx: CommandContext): Promise<number> {
335
469
  }
336
470
 
337
471
  const { entries, malformed } = await collectOperatorLog(
338
- ops.map((d) => ({ name: d.config.name, env: d.config.searchAttributes?.Env ?? "unknown" })),
472
+ ops.map((d) => ({ name: d.config.name, env: d.config.labels?.Env ?? "unknown" })),
339
473
  { op: ctx.args.op, since: ctx.args.since, limit: ctx.args.limit },
340
474
  );
341
475
 
@@ -344,16 +478,16 @@ export async function runOperatorLog(ctx: CommandContext): Promise<number> {
344
478
  return 0;
345
479
  }
346
480
 
347
- const unreadable = malformed.converge + malformed.gates;
481
+ const unreadable = malformed.converge + malformed.gates + malformed.runs;
348
482
  if (unreadable > 0) {
349
483
  console.error(formatWarning({
350
484
  message: `${unreadable} ledger line(s) were unreadable and are missing from this timeline ` +
351
- `(converge: ${malformed.converge}, gates: ${malformed.gates})`,
485
+ `(converge: ${malformed.converge}, gates: ${malformed.gates}, runs: ${malformed.runs})`,
352
486
  }));
353
487
  }
354
488
 
355
489
  if (entries.length === 0) {
356
- console.error(formatWarning({ message: "No converge ticks recorded yet" }));
490
+ console.error(formatWarning({ message: "No converge ticks or Op runs recorded yet" }));
357
491
  return 0;
358
492
  }
359
493
 
@@ -366,16 +500,22 @@ export async function runOperatorLog(ctx: CommandContext): Promise<number> {
366
500
  // ── chant approve <op> <gate> ───────────────────────────────────────────────
367
501
 
368
502
  /**
369
- * `chant approve <op> <gate> [--actor <name>] [--note <text>] [--url <url>]`
370
- * — record the
371
- * out-of-band resolution fact for a gate a converge tick recorded as gated
372
- * (issue: "resolution is an out-of-band act that writes the counterpart
373
- * fact"). Per the issue's own leaning on open question 3 ("local trust in
374
- * v1"), this performs no authorization check beyond "you can run `chant
375
- * approve` locally" — see `../../lifecycle/gate-ledger.ts`'s doc for what
376
- * this record does and, just as importantly, does not (yet) do: it does
377
- * not retroactively unblock the gated op's own local dispatch, which the
378
- * local executor still refuses unconditionally.
503
+ * `chant approve <op> <gate> [--actor <name>] [--note <text>] [--url <url>]
504
+ * [--expire]` — record the out-of-band resolution fact for a gate a run
505
+ * stopped at (issue #1485: "resolution is an out-of-band act that writes the
506
+ * counterpart fact"). Per that issue's leaning on open question 3 ("local
507
+ * trust in v1"), this performs no authorization check beyond "you can run
508
+ * `chant approve` locally".
509
+ *
510
+ * Since #2119 this closes the loop rather than just narrating it: the next
511
+ * `chant run <op>` reads the resolution, finds it newer than the gate's
512
+ * pending fact, and walks through.
513
+ *
514
+ * `--expire` is the other half — clear a standing pending fact *without*
515
+ * approving anything, for a gate that was recorded against a run nobody
516
+ * intends to finish. It writes no resolution; it appends a pending fact that
517
+ * is already expired, which supersedes the standing one on an append-only
518
+ * ledger, so the next run decides the gate from scratch.
379
519
  */
380
520
  export async function runApprove(ctx: CommandContext): Promise<number> {
381
521
  const opName = ctx.args.path;
@@ -385,6 +525,75 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
385
525
  return 1;
386
526
  }
387
527
 
528
+ if (ctx.args.expire) {
529
+ const now = new Date().toISOString();
530
+ const standing = latestPendingGate((await readGateLedger(opName)).pending, gate);
531
+ if (!standing || isPendingGateExpired(standing, now)) {
532
+ console.error(formatWarning({
533
+ message: `Gate "${gate}" on "${opName}" has no standing pending fact — nothing to expire`,
534
+ }));
535
+ return 0;
536
+ }
537
+ await appendPendingGate({
538
+ op: opName,
539
+ gate,
540
+ timestamp: now,
541
+ expiresAt: now,
542
+ ...(standing.description ? { description: standing.description } : {}),
543
+ });
544
+ await pushLifecycle().catch(() => undefined);
545
+ console.error(formatSuccess(`Gate "${gate}" on "${opName}" expired at ${now} — not approved`));
546
+ console.error(formatInfo(
547
+ `The next \`chant run ${opName}\` decides this gate from scratch and records a fresh pending fact.`,
548
+ ));
549
+ return 0;
550
+ }
551
+
552
+ // `--approver` wins over `--actor`, the same precedence `chant run approve`
553
+ // has applied since #2126 (`../handlers/run.ts`'s `runOpApprove`). Reading
554
+ // only `--actor` here meant every README that teaches `--approver you`
555
+ // recorded the shell user instead (#2192).
556
+ const outcome = await recordGateApproval(opName, gate, {
557
+ actor: ctx.args.approver ?? ctx.args.actor,
558
+ note: ctx.args.note,
559
+ url: ctx.args.url,
560
+ });
561
+ if (!outcome.ok) return 1;
562
+
563
+ console.error(formatInfo(
564
+ `This records the resolution as a fact; it does not itself re-run anything. ` +
565
+ `Run \`chant run ${opName}\` and it walks through gate "${gate}".`,
566
+ ));
567
+ return 0;
568
+ }
569
+
570
+ /** What a caller supplies alongside the op and gate names. */
571
+ export interface GateApprovalOptions {
572
+ /** `--actor`/`--approver`; falls back to the CI or shell identity. */
573
+ actor?: string;
574
+ /** `--note` — free-text prose. */
575
+ note?: string;
576
+ /** `--url` — the approval surface, validated as absolute http/https. */
577
+ url?: string;
578
+ }
579
+
580
+ export type GateApprovalOutcome =
581
+ | { ok: true; record: GateResolutionRecord }
582
+ | { ok: false };
583
+
584
+ /**
585
+ * The ledger write behind `chant approve` — extracted so `chant run approve`
586
+ * (#2121) records the identical fact before waking the runtime that hosts the
587
+ * gated run, instead of growing a second, drifting writer.
588
+ *
589
+ * Prints the same warning for an undiscovered op and the same success line as
590
+ * `chant approve` always did; the caller adds whatever it does next.
591
+ */
592
+ export async function recordGateApproval(
593
+ opName: string,
594
+ gate: string,
595
+ opts: GateApprovalOptions,
596
+ ): Promise<GateApprovalOutcome> {
388
597
  const { ops } = await discoverOps();
389
598
  if (!ops.has(opName)) {
390
599
  console.error(formatWarning({
@@ -392,18 +601,18 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
392
601
  }));
393
602
  }
394
603
 
395
- const resolvedBy = ctx.args.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER ?? "unknown";
604
+ const resolvedBy = opts.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER ?? "unknown";
396
605
 
397
606
  // #2028: the resolution's link is typed. `--url` wins; otherwise, running
398
607
  // inside the PR/MR job that carries the change is itself the address, the
399
608
  // same env fallback `--actor` uses. `--note` stays free-text prose.
400
- const url = ctx.args.url ?? resolveApprovalUrl();
609
+ const url = opts.url ?? resolveApprovalUrl();
401
610
  if (url && !isApprovalUrl(url)) {
402
611
  console.error(formatError({
403
612
  message: `--url must be an absolute http/https URL (got "${url}")`,
404
613
  hint: "Pass the PR/MR link, or omit --url and put prose in --note.",
405
614
  }));
406
- return 1;
615
+ return { ok: false };
407
616
  }
408
617
 
409
618
  const { record } = await appendGateResolution({
@@ -411,7 +620,7 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
411
620
  gate,
412
621
  resolvedBy,
413
622
  timestamp: new Date().toISOString(),
414
- ...(ctx.args.note ? { note: ctx.args.note } : {}),
623
+ ...(opts.note ? { note: opts.note } : {}),
415
624
  ...(url ? { url } : {}),
416
625
  });
417
626
  await pushLifecycle().catch(() => undefined);
@@ -420,9 +629,5 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
420
629
  `Gate "${gate}" on "${opName}" resolved by ${record.resolvedBy} at ${record.timestamp}` +
421
630
  (record.url ? ` (${record.url})` : ""),
422
631
  ));
423
- console.error(formatInfo(
424
- "This records the resolution as a fact; it does not itself re-run the gated dispatch — " +
425
- "the local executor still refuses any op with a gate. Re-run it with --temporal, or via the PR that carries the change.",
426
- ));
427
- return 0;
632
+ return { ok: true, record };
428
633
  }