@intentius/chant 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (558) hide show
  1. package/dist/audit/catalog.d.ts +51 -1
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +36 -1
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +26 -9
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/lineage.d.ts +10 -0
  8. package/dist/audit/lineage.d.ts.map +1 -0
  9. package/dist/audit/prior-art.d.ts +385 -0
  10. package/dist/audit/prior-art.d.ts.map +1 -0
  11. package/dist/audit/proof.d.ts +16 -0
  12. package/dist/audit/proof.d.ts.map +1 -1
  13. package/dist/audit/report-html.d.ts.map +1 -1
  14. package/dist/audit/report-model.d.ts +8 -1
  15. package/dist/audit/report-model.d.ts.map +1 -1
  16. package/dist/audit/report.d.ts.map +1 -1
  17. package/dist/audit/rules-doc.d.ts +3 -0
  18. package/dist/audit/rules-doc.d.ts.map +1 -1
  19. package/dist/audit/secrets.d.ts +15 -0
  20. package/dist/audit/secrets.d.ts.map +1 -1
  21. package/dist/audit/terraform-state.d.ts +46 -0
  22. package/dist/audit/terraform-state.d.ts.map +1 -0
  23. package/dist/build.d.ts +17 -1
  24. package/dist/build.d.ts.map +1 -1
  25. package/dist/claimed-fields.d.ts +161 -0
  26. package/dist/claimed-fields.d.ts.map +1 -0
  27. package/dist/cli/build-params-cli.d.ts +2 -2
  28. package/dist/cli/command-group.d.ts +14 -0
  29. package/dist/cli/command-group.d.ts.map +1 -1
  30. package/dist/cli/commands/audit.d.ts +17 -1
  31. package/dist/cli/commands/audit.d.ts.map +1 -1
  32. package/dist/cli/commands/build.d.ts.map +1 -1
  33. package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
  34. package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
  35. package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
  36. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
  38. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
  40. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  41. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  42. package/dist/cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/commands/lint.d.ts +13 -0
  44. package/dist/cli/commands/lint.d.ts.map +1 -1
  45. package/dist/cli/commands/onboard.d.ts +2 -0
  46. package/dist/cli/commands/onboard.d.ts.map +1 -1
  47. package/dist/cli/handlers/build.d.ts.map +1 -1
  48. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  49. package/dist/cli/handlers/operator.d.ts +82 -16
  50. package/dist/cli/handlers/operator.d.ts.map +1 -1
  51. package/dist/cli/handlers/run.d.ts +49 -42
  52. package/dist/cli/handlers/run.d.ts.map +1 -1
  53. package/dist/cli/main.d.ts +12 -1
  54. package/dist/cli/main.d.ts.map +1 -1
  55. package/dist/cli/mcp/op-tools.d.ts +16 -1
  56. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  57. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  58. package/dist/cli/mcp/resources/context.d.ts.map +1 -1
  59. package/dist/cli/plugins.d.ts +22 -0
  60. package/dist/cli/plugins.d.ts.map +1 -1
  61. package/dist/cli/registry.d.ts +40 -9
  62. package/dist/cli/registry.d.ts.map +1 -1
  63. package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
  64. package/dist/components/auto-release.d.ts +10 -20
  65. package/dist/components/auto-release.d.ts.map +1 -1
  66. package/dist/components/capability.d.ts +4 -4
  67. package/dist/components/cli-support.d.ts +24 -22
  68. package/dist/components/cli-support.d.ts.map +1 -1
  69. package/dist/components/component.d.ts +21 -6
  70. package/dist/components/component.d.ts.map +1 -1
  71. package/dist/components/driver-output.d.ts.map +1 -1
  72. package/dist/components/driver.d.ts +73 -37
  73. package/dist/components/driver.d.ts.map +1 -1
  74. package/dist/components/index.d.ts +1 -2
  75. package/dist/components/index.d.ts.map +1 -1
  76. package/dist/components/manifest-persistence.d.ts +1 -9
  77. package/dist/components/manifest-persistence.d.ts.map +1 -1
  78. package/dist/components/run-progress.d.ts +6 -6
  79. package/dist/components/run-progress.d.ts.map +1 -1
  80. package/dist/components/verbs/run-agent.d.ts +10 -22
  81. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  82. package/dist/components/verbs/verify.d.ts +2 -2
  83. package/dist/deep-observation.d.ts +17 -0
  84. package/dist/deep-observation.d.ts.map +1 -1
  85. package/dist/detectLexicon.d.ts +13 -0
  86. package/dist/detectLexicon.d.ts.map +1 -1
  87. package/dist/discovery/files.d.ts +4 -4
  88. package/dist/discovery/fold-import.d.ts.map +1 -1
  89. package/dist/discovery/param-deps.d.ts +33 -0
  90. package/dist/discovery/param-deps.d.ts.map +1 -1
  91. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  92. package/dist/discovery/sandbox/config-wire.d.ts +2 -2
  93. package/dist/fold/foldable-helpers.d.ts +5 -1
  94. package/dist/fold/foldable-helpers.d.ts.map +1 -1
  95. package/dist/fold-provenance.d.ts +236 -0
  96. package/dist/fold-provenance.d.ts.map +1 -0
  97. package/dist/graph-ops.d.ts +1 -1
  98. package/dist/held-elsewhere.d.ts +85 -0
  99. package/dist/held-elsewhere.d.ts.map +1 -0
  100. package/dist/index.d.ts +3 -0
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/kubectl-context.d.ts +3 -3
  103. package/dist/lexicon-config.d.ts +4 -4
  104. package/dist/lexicon-plugin-helpers.d.ts +1 -1
  105. package/dist/lexicon.d.ts +273 -16
  106. package/dist/lexicon.d.ts.map +1 -1
  107. package/dist/lifecycle/change-set.d.ts +13 -0
  108. package/dist/lifecycle/change-set.d.ts.map +1 -1
  109. package/dist/lifecycle/deep-diff.d.ts +133 -6
  110. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  111. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  112. package/dist/lifecycle/gate-ledger.d.ts +79 -3
  113. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  114. package/dist/lifecycle/index.d.ts +1 -0
  115. package/dist/lifecycle/index.d.ts.map +1 -1
  116. package/dist/lifecycle/release-ledger.d.ts +2 -2
  117. package/dist/lifecycle/run-ledger.d.ts +53 -0
  118. package/dist/lifecycle/run-ledger.d.ts.map +1 -0
  119. package/dist/lifecycle/symptoms.d.ts +3 -3
  120. package/dist/lifecycle/symptoms.d.ts.map +1 -1
  121. package/dist/lint/config.d.ts +81 -2
  122. package/dist/lint/config.d.ts.map +1 -1
  123. package/dist/lint/post-synth.d.ts +55 -2
  124. package/dist/lint/post-synth.d.ts.map +1 -1
  125. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
  126. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
  127. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
  128. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
  129. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
  130. package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
  131. package/dist/lint/rules/comp/index.d.ts +1 -1
  132. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  133. package/dist/lint/rules/op/index.d.ts +16 -0
  134. package/dist/lint/rules/op/index.d.ts.map +1 -0
  135. package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
  136. package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
  137. package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
  138. package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
  139. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
  140. package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
  141. package/dist/lint/rules/op/support.d.ts +12 -0
  142. package/dist/lint/rules/op/support.d.ts.map +1 -0
  143. package/dist/lint/suppressions.d.ts +108 -0
  144. package/dist/lint/suppressions.d.ts.map +1 -0
  145. package/dist/live-endpoint.d.ts +1 -1
  146. package/dist/op/activities/activity-contracts.d.ts +54 -0
  147. package/dist/op/activities/activity-contracts.d.ts.map +1 -0
  148. package/dist/op/activities/apply.d.ts +262 -0
  149. package/dist/op/activities/apply.d.ts.map +1 -0
  150. package/dist/op/activities/build.d.ts +13 -0
  151. package/dist/op/activities/build.d.ts.map +1 -0
  152. package/dist/op/activities/converge.d.ts +110 -0
  153. package/dist/op/activities/converge.d.ts.map +1 -0
  154. package/dist/op/activities/env-teardown.d.ts +76 -0
  155. package/dist/op/activities/env-teardown.d.ts.map +1 -0
  156. package/dist/op/activities/guard-validate.d.ts +64 -0
  157. package/dist/op/activities/guard-validate.d.ts.map +1 -0
  158. package/dist/op/activities/http-check.d.ts +34 -0
  159. package/dist/op/activities/http-check.d.ts.map +1 -0
  160. package/dist/op/activities/index.d.ts +31 -0
  161. package/dist/op/activities/index.d.ts.map +1 -0
  162. package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
  163. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
  164. package/dist/op/activities/lifecycle.d.ts +42 -0
  165. package/dist/op/activities/lifecycle.d.ts.map +1 -0
  166. package/dist/op/activities/pipeline-audit.d.ts +84 -0
  167. package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
  168. package/dist/op/activities/policy.d.ts +17 -0
  169. package/dist/op/activities/policy.d.ts.map +1 -0
  170. package/dist/op/activities/reconcile.d.ts +149 -0
  171. package/dist/op/activities/reconcile.d.ts.map +1 -0
  172. package/dist/op/activities/shell.d.ts +13 -0
  173. package/dist/op/activities/shell.d.ts.map +1 -0
  174. package/dist/op/activities/teardown.d.ts +10 -0
  175. package/dist/op/activities/teardown.d.ts.map +1 -0
  176. package/dist/op/activities/util.d.ts +7 -0
  177. package/dist/op/activities/util.d.ts.map +1 -0
  178. package/dist/op/activities/wait.d.ts +28 -0
  179. package/dist/op/activities/wait.d.ts.map +1 -0
  180. package/dist/op/activities/workflow-audit.d.ts +92 -0
  181. package/dist/op/activities/workflow-audit.d.ts.map +1 -0
  182. package/dist/op/activity-contract-registry.d.ts +76 -0
  183. package/dist/op/activity-contract-registry.d.ts.map +1 -0
  184. package/dist/op/activity-contract.d.ts +25 -18
  185. package/dist/op/activity-contract.d.ts.map +1 -1
  186. package/dist/op/activity-failure.d.ts +28 -0
  187. package/dist/op/activity-failure.d.ts.map +1 -0
  188. package/dist/op/activity-profiles.d.ts +118 -0
  189. package/dist/op/activity-profiles.d.ts.map +1 -0
  190. package/dist/op/activity-registry.d.ts +22 -38
  191. package/dist/op/activity-registry.d.ts.map +1 -1
  192. package/dist/op/activity-runtime.d.ts +3 -14
  193. package/dist/op/activity-runtime.d.ts.map +1 -1
  194. package/dist/op/builders.d.ts +85 -30
  195. package/dist/op/builders.d.ts.map +1 -1
  196. package/dist/op/change-signal.d.ts +91 -0
  197. package/dist/op/change-signal.d.ts.map +1 -0
  198. package/dist/op/composites/apply-op.d.ts +118 -0
  199. package/dist/op/composites/apply-op.d.ts.map +1 -0
  200. package/dist/op/composites/converge-op.d.ts +120 -0
  201. package/dist/op/composites/converge-op.d.ts.map +1 -0
  202. package/dist/op/composites/index.d.ts +26 -0
  203. package/dist/op/composites/index.d.ts.map +1 -0
  204. package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
  205. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
  206. package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
  207. package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
  208. package/dist/op/composites/reconcile-op.d.ts +57 -0
  209. package/dist/op/composites/reconcile-op.d.ts.map +1 -0
  210. package/dist/op/composites/watch-op.d.ts +60 -0
  211. package/dist/op/composites/watch-op.d.ts.map +1 -0
  212. package/dist/op/composites/workflow-audit-op.d.ts +57 -0
  213. package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
  214. package/dist/op/converge-rule.d.ts +12 -10
  215. package/dist/op/converge-rule.d.ts.map +1 -1
  216. package/dist/op/cron.d.ts +51 -0
  217. package/dist/op/cron.d.ts.map +1 -0
  218. package/dist/op/discover.d.ts +9 -0
  219. package/dist/op/discover.d.ts.map +1 -1
  220. package/dist/op/duration.d.ts +11 -0
  221. package/dist/op/duration.d.ts.map +1 -0
  222. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  223. package/dist/op/gate-name.d.ts +40 -0
  224. package/dist/op/gate-name.d.ts.map +1 -0
  225. package/dist/op/gate-summary.d.ts +43 -0
  226. package/dist/op/gate-summary.d.ts.map +1 -0
  227. package/dist/op/gate.d.ts +90 -0
  228. package/dist/op/gate.d.ts.map +1 -0
  229. package/dist/op/generate-pipeline.d.ts +9 -0
  230. package/dist/op/generate-pipeline.d.ts.map +1 -1
  231. package/dist/op/index.d.ts +29 -9
  232. package/dist/op/index.d.ts.map +1 -1
  233. package/dist/op/local-executor.d.ts +114 -28
  234. package/dist/op/local-executor.d.ts.map +1 -1
  235. package/dist/op/local-output.d.ts +11 -1
  236. package/dist/op/local-output.d.ts.map +1 -1
  237. package/dist/op/op-ir.d.ts +185 -0
  238. package/dist/op/op-ir.d.ts.map +1 -0
  239. package/dist/op/op-verb-class.d.ts +10 -2
  240. package/dist/op/op-verb-class.d.ts.map +1 -1
  241. package/dist/op/operator.d.ts +93 -5
  242. package/dist/op/operator.d.ts.map +1 -1
  243. package/dist/op/receipt-store.d.ts +1 -1
  244. package/dist/op/receipt-store.d.ts.map +1 -1
  245. package/dist/op/resource.d.ts +15 -1
  246. package/dist/op/resource.d.ts.map +1 -1
  247. package/dist/op/runtime.d.ts +200 -0
  248. package/dist/op/runtime.d.ts.map +1 -0
  249. package/dist/op/runtimes/local.d.ts +29 -0
  250. package/dist/op/runtimes/local.d.ts.map +1 -0
  251. package/dist/op/step-output-ref.d.ts +15 -13
  252. package/dist/op/step-output-ref.d.ts.map +1 -1
  253. package/dist/op/types.d.ts +96 -29
  254. package/dist/op/types.d.ts.map +1 -1
  255. package/dist/ownership.d.ts +1 -1
  256. package/dist/provenance.d.ts +21 -0
  257. package/dist/provenance.d.ts.map +1 -1
  258. package/dist/serializer-walker.d.ts.map +1 -1
  259. package/dist/terraform/bridge.d.ts +26 -7
  260. package/dist/terraform/bridge.d.ts.map +1 -1
  261. package/dist/terraform/carve-provider.d.ts +28 -2
  262. package/dist/terraform/carve-provider.d.ts.map +1 -1
  263. package/dist/terraform/data-source-shape.d.ts +77 -0
  264. package/dist/terraform/data-source-shape.d.ts.map +1 -0
  265. package/dist/terraform/graph.d.ts.map +1 -1
  266. package/dist/terraform/providers/kubernetes.d.ts +5 -0
  267. package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
  268. package/dist/terraform/tier-map.d.ts +16 -4
  269. package/dist/terraform/tier-map.d.ts.map +1 -1
  270. package/dist/terraform/types.d.ts +8 -0
  271. package/dist/terraform/types.d.ts.map +1 -1
  272. package/package.json +6 -1
  273. package/src/audit/catalog.test.ts +79 -3
  274. package/src/audit/catalog.ts +111 -13
  275. package/src/audit/core.test.ts +26 -0
  276. package/src/audit/core.ts +84 -18
  277. package/src/audit/discover.test.ts +39 -0
  278. package/src/audit/discover.ts +202 -18
  279. package/src/audit/lineage.ts +100 -0
  280. package/src/audit/prior-art.ts +153 -0
  281. package/src/audit/proof.test.ts +67 -0
  282. package/src/audit/proof.ts +70 -0
  283. package/src/audit/report-html.ts +9 -4
  284. package/src/audit/report-model.ts +16 -2
  285. package/src/audit/report.test.ts +33 -0
  286. package/src/audit/report.ts +5 -3
  287. package/src/audit/rules-doc.test.ts +34 -2
  288. package/src/audit/rules-doc.ts +82 -9
  289. package/src/audit/secrets.ts +15 -3
  290. package/src/audit/terraform-state.test.ts +144 -0
  291. package/src/audit/terraform-state.ts +131 -0
  292. package/src/build.ts +31 -1
  293. package/src/claimed-fields.test.ts +145 -0
  294. package/src/claimed-fields.ts +208 -0
  295. package/src/cli/build-params-cli.ts +2 -2
  296. package/src/cli/command-group.ts +14 -0
  297. package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
  298. package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
  299. package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
  300. package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
  301. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
  302. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
  303. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
  304. package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
  305. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
  306. package/src/cli/commands/audit.test.ts +191 -11
  307. package/src/cli/commands/audit.ts +78 -28
  308. package/src/cli/commands/build.test.ts +330 -9
  309. package/src/cli/commands/build.ts +170 -24
  310. package/src/cli/commands/carve-bridge.test.ts +237 -14
  311. package/src/cli/commands/carve-bridge.ts +21 -17
  312. package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
  313. package/src/cli/commands/check-lexicon-docs.ts +1 -1
  314. package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
  315. package/src/cli/commands/check-lexicon-examples.ts +42 -10
  316. package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
  317. package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
  318. package/src/cli/commands/init-lexicon.test.ts +97 -2
  319. package/src/cli/commands/init-lexicon.ts +4 -2
  320. package/src/cli/commands/init.test.ts +35 -1
  321. package/src/cli/commands/init.ts +14 -3
  322. package/src/cli/commands/lint.test.ts +194 -1
  323. package/src/cli/commands/lint.ts +205 -7
  324. package/src/cli/commands/onboard.test.ts +75 -0
  325. package/src/cli/commands/onboard.ts +54 -23
  326. package/src/cli/handlers/build.test.ts +81 -1
  327. package/src/cli/handlers/build.ts +15 -1
  328. package/src/cli/handlers/graph.test.ts +3 -3
  329. package/src/cli/handlers/lifecycle.test.ts +195 -12
  330. package/src/cli/handlers/lifecycle.ts +123 -6
  331. package/src/cli/handlers/misc.ts +1 -1
  332. package/src/cli/handlers/operator.test.ts +183 -11
  333. package/src/cli/handlers/operator.ts +341 -55
  334. package/src/cli/handlers/run.test.ts +568 -1295
  335. package/src/cli/handlers/run.ts +446 -912
  336. package/src/cli/main.test.ts +22 -16
  337. package/src/cli/main.ts +66 -33
  338. package/src/cli/mcp/op-tools.ts +147 -88
  339. package/src/cli/mcp/resource-handlers.ts +10 -37
  340. package/src/cli/mcp/resources/context.ts +11 -12
  341. package/src/cli/mcp/server.test.ts +25 -27
  342. package/src/cli/mcp/server.ts +2 -2
  343. package/src/cli/plugins.test.ts +68 -2
  344. package/src/cli/plugins.ts +39 -0
  345. package/src/cli/registry.ts +40 -9
  346. package/src/codegen/docs.ts +1 -1
  347. package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
  348. package/src/codegen/generate-post-synth-barrel.ts +1 -1
  349. package/src/components/README.md +4 -3
  350. package/src/components/__fixtures__/neo4j-fanout.json +1 -1
  351. package/src/components/auto-release.test.ts +4 -20
  352. package/src/components/auto-release.ts +17 -42
  353. package/src/components/capability.ts +4 -4
  354. package/src/components/cli-support.test.ts +28 -12
  355. package/src/components/cli-support.ts +60 -47
  356. package/src/components/component-schema.test.ts +18 -2
  357. package/src/components/component.schema.json +18 -5
  358. package/src/components/component.test.ts +2 -2
  359. package/src/components/component.ts +27 -7
  360. package/src/components/config-defaults.test.ts +2 -2
  361. package/src/components/driver-output.ts +14 -2
  362. package/src/components/driver.test.ts +109 -20
  363. package/src/components/driver.ts +201 -84
  364. package/src/components/index.ts +1 -6
  365. package/src/components/manifest-persistence.test.ts +2 -20
  366. package/src/components/manifest-persistence.ts +4 -24
  367. package/src/components/pilots/neo4j-fanout.pilot.ts +3 -3
  368. package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
  369. package/src/components/run-progress.ts +6 -6
  370. package/src/components/verbs/run-agent.test.ts +19 -0
  371. package/src/components/verbs/run-agent.ts +12 -24
  372. package/src/components/verbs/verify.ts +2 -2
  373. package/src/deep-observation.test.ts +75 -0
  374. package/src/deep-observation.ts +48 -0
  375. package/src/detectLexicon.ts +18 -1
  376. package/src/discovery/collect.test.ts +3 -3
  377. package/src/discovery/files.ts +4 -4
  378. package/src/discovery/fold-import.test.ts +1 -1
  379. package/src/discovery/fold-import.ts +105 -2
  380. package/src/discovery/param-deps.test.ts +112 -1
  381. package/src/discovery/param-deps.ts +193 -0
  382. package/src/discovery/sandbox/bundle.ts +15 -1
  383. package/src/discovery/sandbox/config-wire.test.ts +2 -2
  384. package/src/discovery/sandbox/config-wire.ts +2 -2
  385. package/src/fold/fold.test.ts +56 -0
  386. package/src/fold/foldable-helpers.ts +82 -2
  387. package/src/fold-provenance-exclusion.test.ts +186 -0
  388. package/src/fold-provenance.test.ts +495 -0
  389. package/src/fold-provenance.ts +441 -0
  390. package/src/graph-ops.test.ts +3 -3
  391. package/src/graph-ops.ts +1 -1
  392. package/src/held-elsewhere.test.ts +102 -0
  393. package/src/held-elsewhere.ts +114 -0
  394. package/src/index.ts +3 -0
  395. package/src/kubectl-context.ts +3 -3
  396. package/src/lexicon-config.test.ts +5 -5
  397. package/src/lexicon-config.ts +4 -4
  398. package/src/lexicon-plugin-helpers.ts +1 -1
  399. package/src/lexicon.ts +296 -16
  400. package/src/lifecycle/change-set.test.ts +49 -0
  401. package/src/lifecycle/change-set.ts +67 -0
  402. package/src/lifecycle/converge-ledger.ts +1 -1
  403. package/src/lifecycle/deep-diff.test.ts +267 -14
  404. package/src/lifecycle/deep-diff.ts +217 -14
  405. package/src/lifecycle/deep-observe.test.ts +3 -1
  406. package/src/lifecycle/deep-observe.ts +16 -6
  407. package/src/lifecycle/gate-ledger.test.ts +74 -2
  408. package/src/lifecycle/gate-ledger.ts +154 -36
  409. package/src/lifecycle/index.ts +1 -0
  410. package/src/lifecycle/release-ledger.ts +2 -2
  411. package/src/lifecycle/run-ledger.test.ts +268 -0
  412. package/src/lifecycle/run-ledger.ts +201 -0
  413. package/src/lifecycle/symptoms.ts +3 -3
  414. package/src/lint/config.test.ts +82 -1
  415. package/src/lint/config.ts +112 -3
  416. package/src/lint/pipeline-change-gate.test.ts +2 -2
  417. package/src/lint/policy.test.ts +1 -1
  418. package/src/lint/post-synth.test.ts +31 -0
  419. package/src/lint/post-synth.ts +54 -1
  420. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
  421. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
  422. package/src/lint/rules/comp/comp.test.ts +27 -1
  423. package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -0
  424. package/src/lint/rules/comp/index.ts +3 -3
  425. package/src/lint/rules/op/index.ts +23 -0
  426. package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
  427. package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
  428. package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
  429. package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
  430. package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
  431. package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
  432. package/src/lint/rules/op/support.ts +11 -0
  433. package/src/lint/suppressions.test.ts +163 -0
  434. package/src/lint/suppressions.ts +204 -0
  435. package/src/live-endpoint.ts +1 -1
  436. package/src/op/activities/activity-contracts.ts +66 -0
  437. package/src/op/activities/apply.test.ts +567 -0
  438. package/src/op/activities/apply.ts +662 -0
  439. package/src/op/activities/build.ts +26 -0
  440. package/src/op/activities/converge.test.ts +330 -0
  441. package/src/op/activities/converge.ts +449 -0
  442. package/src/op/activities/env-teardown.test.ts +187 -0
  443. package/src/op/activities/env-teardown.ts +171 -0
  444. package/src/op/activities/guard-validate.test.ts +139 -0
  445. package/src/op/activities/guard-validate.ts +223 -0
  446. package/src/op/activities/http-check.test.ts +48 -0
  447. package/src/op/activities/http-check.ts +55 -0
  448. package/src/op/activities/index.ts +119 -0
  449. package/src/op/activities/lexicon-upgrade.test.ts +492 -0
  450. package/src/op/activities/lexicon-upgrade.ts +673 -0
  451. package/src/op/activities/lifecycle.ts +85 -0
  452. package/src/op/activities/pipeline-audit.test.ts +67 -0
  453. package/src/op/activities/pipeline-audit.ts +201 -0
  454. package/src/op/activities/policy.test.ts +19 -0
  455. package/src/op/activities/policy.ts +37 -0
  456. package/src/op/activities/reconcile.test.ts +161 -0
  457. package/src/op/activities/reconcile.ts +354 -0
  458. package/src/op/activities/shell.ts +26 -0
  459. package/src/op/activities/teardown.ts +22 -0
  460. package/src/op/activities/util.ts +6 -0
  461. package/src/op/activities/wait.ts +69 -0
  462. package/src/op/activities/workflow-audit.test.ts +90 -0
  463. package/src/op/activities/workflow-audit.ts +203 -0
  464. package/src/op/activity-contract-registry.test.ts +98 -0
  465. package/src/op/activity-contract-registry.ts +114 -0
  466. package/src/op/activity-contract.test.ts +1 -1
  467. package/src/op/activity-contract.ts +37 -33
  468. package/src/op/activity-failure.ts +34 -0
  469. package/src/op/activity-profiles.test.ts +56 -0
  470. package/src/op/activity-profiles.ts +116 -0
  471. package/src/op/activity-registry.test.ts +37 -28
  472. package/src/op/activity-registry.ts +27 -56
  473. package/src/op/activity-runtime.ts +3 -47
  474. package/src/op/builders-exports.test.ts +0 -49
  475. package/src/op/builders-typed.test.ts +164 -0
  476. package/src/op/builders.ts +125 -41
  477. package/src/op/change-signal.test.ts +117 -0
  478. package/src/op/change-signal.ts +169 -0
  479. package/src/op/composites/apply-op.ts +218 -0
  480. package/src/op/composites/composites.test.ts +379 -0
  481. package/src/op/composites/converge-op.test.ts +168 -0
  482. package/src/op/composites/converge-op.ts +214 -0
  483. package/src/op/composites/index.ts +26 -0
  484. package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
  485. package/src/op/composites/lexicon-upgrade-op.ts +134 -0
  486. package/src/op/composites/pipeline-audit-op.test.ts +25 -0
  487. package/src/op/composites/pipeline-audit-op.ts +78 -0
  488. package/src/op/composites/reconcile-op.test.ts +51 -0
  489. package/src/op/composites/reconcile-op.ts +110 -0
  490. package/src/op/composites/watch-op.ts +108 -0
  491. package/src/op/composites/workflow-audit-op.test.ts +25 -0
  492. package/src/op/composites/workflow-audit-op.ts +88 -0
  493. package/src/op/converge-rule.ts +12 -10
  494. package/src/op/cron.test.ts +128 -0
  495. package/src/op/cron.ts +149 -0
  496. package/src/op/discover-root.test.ts +105 -1
  497. package/src/op/discover.test.ts +6 -2
  498. package/src/op/discover.ts +84 -16
  499. package/src/op/duration.ts +21 -0
  500. package/src/op/effect-step.test.ts +29 -14
  501. package/src/op/emulator-lifecycle.ts +1 -2
  502. package/src/op/gate-name.test.ts +65 -0
  503. package/src/op/gate-name.ts +60 -0
  504. package/src/op/gate-summary.ts +84 -0
  505. package/src/op/gate.ts +155 -0
  506. package/src/op/generate-pipeline.test.ts +65 -3
  507. package/src/op/generate-pipeline.ts +19 -2
  508. package/src/op/index.ts +48 -8
  509. package/src/op/local-executor.test.ts +178 -34
  510. package/src/op/local-executor.ts +371 -102
  511. package/src/op/local-output.test.ts +95 -11
  512. package/src/op/local-output.ts +33 -4
  513. package/src/op/op-ir.test.ts +292 -0
  514. package/src/op/op-ir.ts +419 -0
  515. package/src/op/op-verb-class.test.ts +2 -2
  516. package/src/op/op-verb-class.ts +19 -5
  517. package/src/op/op.test.ts +28 -7
  518. package/src/op/operator.test.ts +543 -6
  519. package/src/op/operator.ts +203 -20
  520. package/src/op/receipt-store.ts +1 -1
  521. package/src/op/resource.ts +20 -2
  522. package/src/op/runtime.ts +203 -0
  523. package/src/op/runtimes/local.test.ts +181 -0
  524. package/src/op/runtimes/local.ts +212 -0
  525. package/src/op/step-output-ref.ts +15 -13
  526. package/src/op/types.ts +106 -26
  527. package/src/ownership.ts +1 -1
  528. package/src/provenance.test.ts +6 -0
  529. package/src/provenance.ts +21 -1
  530. package/src/serializer-walker.test.ts +42 -0
  531. package/src/serializer-walker.ts +21 -2
  532. package/src/terraform/aws-resources.test.ts +13 -4
  533. package/src/terraform/bridge.test.ts +22 -9
  534. package/src/terraform/bridge.ts +89 -28
  535. package/src/terraform/carve-provider.ts +38 -2
  536. package/src/terraform/data-source-shape.ts +95 -0
  537. package/src/terraform/graph.ts +35 -7
  538. package/src/terraform/providers/kubernetes.ts +48 -2
  539. package/src/terraform/tier-map.ts +21 -6
  540. package/src/terraform/types.ts +8 -0
  541. package/dist/cli/handlers/op-progress.d.ts +0 -90
  542. package/dist/cli/handlers/op-progress.d.ts.map +0 -1
  543. package/dist/cli/handlers/run-client.d.ts +0 -127
  544. package/dist/cli/handlers/run-client.d.ts.map +0 -1
  545. package/dist/cli/handlers/run-report.d.ts +0 -5
  546. package/dist/cli/handlers/run-report.d.ts.map +0 -1
  547. package/dist/components/temporal-codegen-loader.d.ts +0 -27
  548. package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
  549. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
  550. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
  551. package/src/cli/handlers/op-progress.test.ts +0 -268
  552. package/src/cli/handlers/op-progress.ts +0 -248
  553. package/src/cli/handlers/run-client.test.ts +0 -82
  554. package/src/cli/handlers/run-client.ts +0 -219
  555. package/src/cli/handlers/run-report.test.ts +0 -62
  556. package/src/cli/handlers/run-report.ts +0 -122
  557. package/src/components/temporal-codegen-loader.ts +0 -41
  558. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
@@ -7,6 +7,9 @@
7
7
  * `loadProfiles` from the project's configured lexicons).
8
8
  */
9
9
  import { loadChantConfig } from "../../config";
10
+ import { build } from "../../build";
11
+ import { isResourceDeclarable } from "../../declarable";
12
+ import { collectBuildRootContributors, collectChangeSubscribers } from "../plugins";
10
13
  import { discoverOps } from "../../op/discover";
11
14
  import { loadActivities, loadProfiles } from "../../op/activity-registry";
12
15
  import { parseDuration } from "../../op/local-executor";
@@ -15,18 +18,27 @@ import {
15
18
  runOperatorRound,
16
19
  runOperatorForever,
17
20
  formatRoundLine,
21
+ formatSignalLine,
18
22
  DEFAULT_OPERATOR_INTERVAL_MS,
23
+ type ChangeSubscriber,
24
+ type OperatorSignalEvent,
19
25
  type OperatorTickEvent,
20
26
  } from "../../op/operator";
21
27
  import { readLease, DEFAULT_LEASE_TTL_MS } from "../../lifecycle/lease";
22
28
  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";
29
+ import { readRunLedger } from "../../lifecycle/run-ledger";
30
+ import type { OpRunRecord } from "../../op/runtime";
31
+ import type { GateResolutionRecord, PendingGateRecord } from "../../lifecycle/gate-ledger";
32
+ import {
33
+ appendGateResolution, appendPendingGate, readGateResolutions, readGateLedger,
34
+ latestResolutionSince, latestPendingGate, isPendingGateExpired,
35
+ resolveApprovalUrl, isApprovalUrl,
36
+ } from "../../lifecycle/gate-ledger";
25
37
  import { pushLifecycle } from "../../lifecycle/git";
26
38
  import { formatError, formatWarning, formatSuccess, formatBold, formatInfo } from "../format";
27
39
  import type { CommandContext } from "../registry";
28
40
 
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. */
41
+ /** 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
42
  async function loadOperatorActivities() {
31
43
  let lexicons: string[] = [];
32
44
  try {
@@ -37,12 +49,82 @@ async function loadOperatorActivities() {
37
49
  return Promise.all([loadActivities(lexicons), loadProfiles()]);
38
50
  }
39
51
 
52
+ /**
53
+ * Bind the change-signal seams (#1981) the daemon will keep subscribed, or an
54
+ * empty list.
55
+ *
56
+ * Three gates, in cost order, so a project that gains nothing from this pays
57
+ * nothing for it:
58
+ *
59
+ * 1. No configured lexicon implements `subscribeChanges`. Return immediately,
60
+ * without loading config or building anything. This is every project today.
61
+ * 2. No `--env`. A subscription resolves the same cluster binding a read
62
+ * does, and there is no binding to resolve without an environment. Said out
63
+ * loud rather than silently skipped, because "why did it not wake" is
64
+ * otherwise unanswerable.
65
+ * 3. The build that supplies the declared entities failed. Warn and fall back
66
+ * to the timer. A subscription is an optimization; a build error here must
67
+ * not stop the operator, which has its own per-tick build inside the tick.
68
+ */
69
+ async function collectOperatorSubscribers(
70
+ ctx: CommandContext,
71
+ env: string | undefined,
72
+ ): Promise<ChangeSubscriber[]> {
73
+ if (!ctx.plugins.some((p) => typeof p.subscribeChanges === "function")) return [];
74
+
75
+ if (!env) {
76
+ console.error(formatWarning({
77
+ message: "a change signal needs an environment to resolve its binding, so this runs on the timer alone",
78
+ hint: "pass --env <env> to let a lexicon's subscribeChanges wake a tick early",
79
+ }));
80
+ return [];
81
+ }
82
+
83
+ const cwd = process.cwd();
84
+ try {
85
+ const { config } = await loadChantConfig(cwd);
86
+ const buildRoots = collectBuildRootContributors(
87
+ ctx.plugins,
88
+ config as unknown as Record<string, unknown>,
89
+ cwd,
90
+ );
91
+ const buildResult = await build(config.sourceDir ?? ".", ctx.serializers, undefined, { buildRoots });
92
+ if (buildResult.errors.length > 0) {
93
+ console.error(formatWarning({
94
+ message: "build failed while scoping the change signal, so this runs on the timer alone",
95
+ }));
96
+ return [];
97
+ }
98
+
99
+ // The declared estate, sliced per lexicon: the same slice
100
+ // `takeSnapshot` hands `describeResources`, and the bound on what any
101
+ // subscription may watch.
102
+ const entities = new Map<string, Map<string, { entityType: string; props: Record<string, unknown> }>>();
103
+ for (const [name, entity] of buildResult.entities) {
104
+ if (!isResourceDeclarable(entity)) continue;
105
+ let perLexicon = entities.get(entity.lexicon);
106
+ if (!perLexicon) entities.set(entity.lexicon, (perLexicon = new Map()));
107
+ perLexicon.set(name, {
108
+ entityType: entity.entityType,
109
+ props: (entity.props != null ? entity.props : {}) as Record<string, unknown>,
110
+ });
111
+ }
112
+
113
+ return collectChangeSubscribers(ctx.plugins, { environment: env, cwd, entities });
114
+ } catch (err) {
115
+ console.error(formatWarning({
116
+ message: `could not scope the change signal, so this runs on the timer alone (${err instanceof Error ? err.message : String(err)})`,
117
+ }));
118
+ return [];
119
+ }
120
+ }
121
+
40
122
  // ── chant operator ──────────────────────────────────────────────────────────
41
123
 
42
124
  /**
43
125
  * `chant operator [--env <env>] [--interval <duration>] [--lease-ttl
44
126
  * <duration>] [--once]` — run scheduled ticks for this project's discovered
45
- * ConvergeOps, locally, with no Temporal installed (issue's own worked
127
+ * ConvergeOps, locally, with no service to install (issue's own worked
46
128
  * example). `--once` runs a single round and exits (also the offline test
47
129
  * story, and what a cron/systemd-timer/k8s-CronJob invoker uses instead of
48
130
  * leaving the daemon running); omitted, the daemon loops until Ctrl-C.
@@ -92,6 +174,9 @@ export async function runOperator(ctx: CommandContext): Promise<number> {
92
174
  console.error(formatInfo(
93
175
  `chant operator: watching ${ops.length} ConvergeOp(s) every ${intervalMs}ms (Ctrl-C to stop)`,
94
176
  ));
177
+ // A change signal (#1981) only ever shortens the sleep above. Every round
178
+ // it wakes is the round the timer would have run.
179
+ const subscribers = await collectOperatorSubscribers(ctx, ctx.args.env);
95
180
  await runOperatorForever({
96
181
  env: ctx.args.env,
97
182
  intervalMs,
@@ -100,6 +185,8 @@ export async function runOperator(ctx: CommandContext): Promise<number> {
100
185
  profiles,
101
186
  signal: controller.signal,
102
187
  onRound: printRound,
188
+ subscribers,
189
+ onSignalEvent: (event: OperatorSignalEvent) => console.error(formatInfo(formatSignalLine(event))),
103
190
  });
104
191
  return 0;
105
192
  } finally {
@@ -113,11 +200,37 @@ interface OpStatusLine {
113
200
  op: string;
114
201
  env: string;
115
202
  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 }[];
203
+ /** `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. */
204
+ pendingGates: { rule?: string; op?: string; gate: string; url?: string; expiresAt?: string; description?: string }[];
118
205
  lease?: { holder: string; expiresAt: string };
119
206
  }
120
207
 
208
+ /**
209
+ * Every gate an op is currently standing at, read from its own gate ledger
210
+ * (#2119) — a live (unexpired) pending fact with no resolution newer than it.
211
+ *
212
+ * Ledger-first, so `chant operator status` sees a gate any run recorded, not
213
+ * only one a converge tick's dispatch reached. Before this, a plain `chant run
214
+ * <op>` could not produce a pending gate at all (the executor refused the op
215
+ * outright), so reading the converge ledger's `gated` outcomes was the whole
216
+ * story; now it is one source among the ops' own.
217
+ */
218
+ export async function pendingGatesFor(
219
+ opName: string,
220
+ opts: { cwd?: string; now?: string } = {},
221
+ ): Promise<PendingGateRecord[]> {
222
+ const now = opts.now ?? new Date().toISOString();
223
+ const { resolutions, pending } = await readGateLedger(opName, { cwd: opts.cwd });
224
+ const standing: PendingGateRecord[] = [];
225
+ for (const gate of new Set(pending.map((p) => p.gate))) {
226
+ const latest = latestPendingGate(pending, gate);
227
+ if (!latest || isPendingGateExpired(latest, now)) continue;
228
+ if (latestResolutionSince(resolutions, gate, latest.timestamp)) continue;
229
+ standing.push(latest);
230
+ }
231
+ return standing.sort((a, b) => a.gate.localeCompare(b.gate));
232
+ }
233
+
121
234
  async function statusFor(opName: string, env: string, cwd?: string): Promise<OpStatusLine> {
122
235
  const [{ records }, { record: lease }] = await Promise.all([
123
236
  readConvergeLedger(env, { cwd }),
@@ -127,19 +240,47 @@ async function statusFor(opName: string, env: string, cwd?: string): Promise<OpS
127
240
  const lastTick = ownRecords.at(-1);
128
241
 
129
242
  const pendingGates: OpStatusLine["pendingGates"] = [];
243
+ const seen = new Set<string>();
244
+
245
+ // The dispatched ops this ConvergeOp's last tick recorded as gated, plus the
246
+ // ConvergeOp itself — a gate can sit on either.
247
+ const gateOps = new Set<string>([opName]);
248
+ const ruleFor = new Map<string, string>();
249
+ for (const outcome of lastTick?.outcomes ?? []) {
250
+ if (outcome.action !== "gated" || !outcome.gateName || !outcome.op) continue;
251
+ gateOps.add(outcome.op);
252
+ ruleFor.set(`${outcome.op}\0${outcome.gateName}`, outcome.ruleId);
253
+ }
254
+
255
+ for (const gateOp of [...gateOps].sort()) {
256
+ for (const record of await pendingGatesFor(gateOp, { cwd })) {
257
+ seen.add(`${gateOp}\0${record.gate}`);
258
+ pendingGates.push({
259
+ op: gateOp,
260
+ gate: record.gate,
261
+ expiresAt: record.expiresAt,
262
+ ...(ruleFor.get(`${gateOp}\0${record.gate}`) ? { rule: ruleFor.get(`${gateOp}\0${record.gate}`) } : {}),
263
+ ...(record.description ? { description: record.description } : {}),
264
+ ...(record.url ? { url: record.url } : {}),
265
+ });
266
+ }
267
+ }
268
+
269
+ // A tick that recorded a `gated` outcome before the executor started writing
270
+ // pending facts (#2119) still has nothing in `_gates` to read; fall back to
271
+ // the outcome itself so an in-flight gate isn't dropped on upgrade.
130
272
  if (lastTick) {
131
273
  for (const outcome of lastTick.outcomes) {
132
274
  if (outcome.action !== "gated" || !outcome.gateName || !outcome.op) continue;
275
+ if (seen.has(`${outcome.op}\0${outcome.gateName}`)) continue;
133
276
  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
- }
277
+ if (latestResolutionSince(resolutions, outcome.gateName, lastTick.timestamp)) continue;
278
+ pendingGates.push({
279
+ rule: outcome.ruleId,
280
+ op: outcome.op,
281
+ gate: outcome.gateName,
282
+ ...(outcome.url ? { url: outcome.url } : {}),
283
+ });
143
284
  }
144
285
  }
145
286
 
@@ -152,6 +293,15 @@ async function statusFor(opName: string, env: string, cwd?: string): Promise<OpS
152
293
  };
153
294
  }
154
295
 
296
+ /** 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. */
297
+ export interface StandaloneGateLine {
298
+ op: string;
299
+ gate: string;
300
+ description?: string;
301
+ expiresAt: string;
302
+ url?: string;
303
+ }
304
+
155
305
  /**
156
306
  * `chant operator status [--env <env>] [--json]` — last tick, outcome
157
307
  * counts, and pending gates, read from the `chant/lifecycle` orphan branch
@@ -159,22 +309,46 @@ async function statusFor(opName: string, env: string, cwd?: string): Promise<OpS
159
309
  * running: everything here was already durably recorded by whichever
160
310
  * process (this operator, a bare `chant run <op>`, or another machine's
161
311
  * operator) ran the last tick.
312
+ *
313
+ * Pending gates are read from every discovered op's own gate ledger, not only
314
+ * from a converge tick's `gated` outcomes (#2119) — a gate an ordinary `chant
315
+ * run <op>` stopped at is the same standing fact and shows up the same way,
316
+ * under "pending gates" for ops with no converge tick behind them.
162
317
  */
163
318
  export async function runOperatorStatus(ctx: CommandContext): Promise<number> {
164
319
  const { ops, errors } = await discoverConvergeOps({ env: ctx.args.env });
165
320
  for (const err of errors) console.error(formatWarning({ message: err }));
166
321
 
167
- if (ops.length === 0) {
322
+ const rows = await Promise.all(
323
+ ops.map((d) => statusFor(d.config.name, d.config.labels?.Env ?? "unknown")),
324
+ );
325
+
326
+ // Every other discovered op that is standing at a gate. `discoverOps` sees
327
+ // the whole project, so an ApplyOp someone ran by hand this morning is here
328
+ // even though no ConvergeOp ever dispatched it.
329
+ const covered = new Set(rows.flatMap((r) => r.pendingGates.map((g) => `${g.op ?? r.op} ${g.gate}`)));
330
+ const { ops: allOps } = await discoverOps();
331
+ const standalone: StandaloneGateLine[] = [];
332
+ for (const opName of [...allOps.keys()].sort()) {
333
+ for (const record of await pendingGatesFor(opName)) {
334
+ if (covered.has(`${opName} ${record.gate}`)) continue;
335
+ standalone.push({
336
+ op: opName,
337
+ gate: record.gate,
338
+ expiresAt: record.expiresAt,
339
+ ...(record.description ? { description: record.description } : {}),
340
+ ...(record.url ? { url: record.url } : {}),
341
+ });
342
+ }
343
+ }
344
+
345
+ if (ops.length === 0 && standalone.length === 0) {
168
346
  console.error(formatWarning({ message: "No ConvergeOp declarations found" }));
169
347
  return 0;
170
348
  }
171
349
 
172
- const rows = await Promise.all(
173
- ops.map((d) => statusFor(d.config.name, d.config.searchAttributes?.Env ?? "unknown")),
174
- );
175
-
176
350
  if (ctx.args.json) {
177
- console.log(JSON.stringify(rows, null, 2));
351
+ console.log(JSON.stringify(standalone.length > 0 ? { rows, pendingGates: standalone } : rows, null, 2));
178
352
  return 0;
179
353
  }
180
354
 
@@ -190,13 +364,28 @@ export async function runOperatorStatus(ctx: CommandContext): Promise<number> {
190
364
  if (row.pendingGates.length > 0) {
191
365
  console.log(` pending gates:`);
192
366
  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}`);
367
+ const op = g.op ?? row.op;
368
+ console.log(
369
+ ` - ${op} gate "${g.gate}"${g.rule ? ` (rule ${g.rule})` : ""} — resolve: chant approve ${op} ${g.gate}`,
370
+ );
371
+ if (g.expiresAt) console.log(` expires: ${g.expiresAt}`);
194
372
  if (g.url) console.log(` approve at: ${g.url}`);
195
373
  }
196
374
  }
197
375
  console.log("");
198
376
  }
199
377
 
378
+ if (standalone.length > 0) {
379
+ console.log(formatBold("pending gates (no converge tick)"));
380
+ for (const g of standalone) {
381
+ console.log(` - ${g.op} gate "${g.gate}" — resolve: chant approve ${g.op} ${g.gate}`);
382
+ if (g.description) console.log(` ${g.description}`);
383
+ console.log(` expires: ${g.expiresAt}`);
384
+ if (g.url) console.log(` approve at: ${g.url}`);
385
+ }
386
+ console.log("");
387
+ }
388
+
200
389
  return 0;
201
390
  }
202
391
 
@@ -205,17 +394,20 @@ export async function runOperatorStatus(ctx: CommandContext): Promise<number> {
205
394
  /** One entry of the merged tick/gate timeline `chant operator log` prints. */
206
395
  export type OperatorLogEntry =
207
396
  | { kind: "tick"; timestamp: string; record: ConvergeTickRecord }
208
- | { kind: "gate-resolution"; timestamp: string; record: GateResolutionRecord };
397
+ | { kind: "gate-resolution"; timestamp: string; record: GateResolutionRecord }
398
+ /** One Op run (#2118), timestamped at the instant it ended — the tick that dispatched it precedes it. */
399
+ | { kind: "run"; timestamp: string; record: OpRunRecord };
209
400
 
210
401
  export interface OperatorLogResult {
211
402
  entries: OperatorLogEntry[];
212
403
  /**
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.
404
+ * Unreadable lines behind this answer, per ledger. `readConvergeLedger`,
405
+ * `readGateResolutions` and `readRunLedger` all skip a malformed line and
406
+ * count it rather than throwing, so a corrupted ledger renders a shorter
407
+ * timeline; without this a consumer could not tell that from a genuinely
408
+ * quiet environment.
217
409
  */
218
- malformed: { converge: number; gates: number };
410
+ malformed: { converge: number; gates: number; runs: number };
219
411
  }
220
412
 
221
413
  /**
@@ -234,7 +426,7 @@ export async function collectOperatorLog(
234
426
  const sinceMs = opts.since ? new Date(opts.since).getTime() : undefined;
235
427
 
236
428
  const entries: OperatorLogEntry[] = [];
237
- const malformed = { converge: 0, gates: 0 };
429
+ const malformed = { converge: 0, gates: 0, runs: 0 };
238
430
 
239
431
  // One read per distinct environment — several ConvergeOps can share one
240
432
  // `<env>/converge.jsonl`, and re-reading it per op would both cost more and
@@ -263,11 +455,23 @@ export async function collectOperatorLog(
263
455
  }
264
456
  }
265
457
 
458
+ // The runs each discovered Op recorded (#2118). Keyed `<env>/runs__<op>`,
459
+ // so unlike the converge ledger this is one read per (env, op) pair rather
460
+ // than per env — a ConvergeOp's own ticks and its own runs interleave here.
461
+ for (const { name, env } of wanted) {
462
+ const { records, malformed: bad } = await readRunLedger(env, name, { cwd: opts.cwd });
463
+ malformed.runs += bad;
464
+ for (const record of records) {
465
+ entries.push({ kind: "run", timestamp: record.ended, record });
466
+ }
467
+ }
468
+
469
+ // Within one instant: the tick first (it is what dispatched anything else),
470
+ // then the run it dispatched, then the gate resolution against that run.
471
+ const rank: Record<OperatorLogEntry["kind"], number> = { tick: 0, run: 1, "gate-resolution": 2 };
266
472
  let merged = entries.sort((a, b) => {
267
473
  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);
474
+ return delta !== 0 ? delta : rank[a.kind] - rank[b.kind];
271
475
  });
272
476
  if (sinceMs !== undefined) merged = merged.filter((e) => new Date(e.timestamp).getTime() >= sinceMs);
273
477
  if (opts.limit !== undefined && merged.length > opts.limit) merged = merged.slice(-opts.limit);
@@ -276,6 +480,17 @@ export async function collectOperatorLog(
276
480
  }
277
481
 
278
482
  function renderLogEntry(entry: OperatorLogEntry): string[] {
483
+ if (entry.kind === "run") {
484
+ const { record } = entry;
485
+ const id = `[${record.id.slice(0, 8)}]`;
486
+ const outcomes = Object.entries(record.outcomes).map(([k, v]) => `${k}=${String(v)}`).join(" ");
487
+ const steps = record.phases.reduce((n, p) => n + p.steps.length, 0);
488
+ return [
489
+ `${record.ended} ${record.op}@${record.env} ${id} run ${record.status} ` +
490
+ `phases=${record.phases.length} steps=${steps}${outcomes ? ` ${outcomes}` : ""}`,
491
+ ];
492
+ }
493
+
279
494
  if (entry.kind === "gate-resolution") {
280
495
  const { record } = entry;
281
496
  return [
@@ -335,7 +550,7 @@ export async function runOperatorLog(ctx: CommandContext): Promise<number> {
335
550
  }
336
551
 
337
552
  const { entries, malformed } = await collectOperatorLog(
338
- ops.map((d) => ({ name: d.config.name, env: d.config.searchAttributes?.Env ?? "unknown" })),
553
+ ops.map((d) => ({ name: d.config.name, env: d.config.labels?.Env ?? "unknown" })),
339
554
  { op: ctx.args.op, since: ctx.args.since, limit: ctx.args.limit },
340
555
  );
341
556
 
@@ -344,16 +559,16 @@ export async function runOperatorLog(ctx: CommandContext): Promise<number> {
344
559
  return 0;
345
560
  }
346
561
 
347
- const unreadable = malformed.converge + malformed.gates;
562
+ const unreadable = malformed.converge + malformed.gates + malformed.runs;
348
563
  if (unreadable > 0) {
349
564
  console.error(formatWarning({
350
565
  message: `${unreadable} ledger line(s) were unreadable and are missing from this timeline ` +
351
- `(converge: ${malformed.converge}, gates: ${malformed.gates})`,
566
+ `(converge: ${malformed.converge}, gates: ${malformed.gates}, runs: ${malformed.runs})`,
352
567
  }));
353
568
  }
354
569
 
355
570
  if (entries.length === 0) {
356
- console.error(formatWarning({ message: "No converge ticks recorded yet" }));
571
+ console.error(formatWarning({ message: "No converge ticks or Op runs recorded yet" }));
357
572
  return 0;
358
573
  }
359
574
 
@@ -366,16 +581,22 @@ export async function runOperatorLog(ctx: CommandContext): Promise<number> {
366
581
  // ── chant approve <op> <gate> ───────────────────────────────────────────────
367
582
 
368
583
  /**
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.
584
+ * `chant approve <op> <gate> [--actor <name>] [--note <text>] [--url <url>]
585
+ * [--expire]` — record the out-of-band resolution fact for a gate a run
586
+ * stopped at (issue #1485: "resolution is an out-of-band act that writes the
587
+ * counterpart fact"). Per that issue's leaning on open question 3 ("local
588
+ * trust in v1"), this performs no authorization check beyond "you can run
589
+ * `chant approve` locally".
590
+ *
591
+ * Since #2119 this closes the loop rather than just narrating it: the next
592
+ * `chant run <op>` reads the resolution, finds it newer than the gate's
593
+ * pending fact, and walks through.
594
+ *
595
+ * `--expire` is the other half — clear a standing pending fact *without*
596
+ * approving anything, for a gate that was recorded against a run nobody
597
+ * intends to finish. It writes no resolution; it appends a pending fact that
598
+ * is already expired, which supersedes the standing one on an append-only
599
+ * ledger, so the next run decides the gate from scratch.
379
600
  */
380
601
  export async function runApprove(ctx: CommandContext): Promise<number> {
381
602
  const opName = ctx.args.path;
@@ -385,6 +606,75 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
385
606
  return 1;
386
607
  }
387
608
 
609
+ if (ctx.args.expire) {
610
+ const now = new Date().toISOString();
611
+ const standing = latestPendingGate((await readGateLedger(opName)).pending, gate);
612
+ if (!standing || isPendingGateExpired(standing, now)) {
613
+ console.error(formatWarning({
614
+ message: `Gate "${gate}" on "${opName}" has no standing pending fact — nothing to expire`,
615
+ }));
616
+ return 0;
617
+ }
618
+ await appendPendingGate({
619
+ op: opName,
620
+ gate,
621
+ timestamp: now,
622
+ expiresAt: now,
623
+ ...(standing.description ? { description: standing.description } : {}),
624
+ });
625
+ await pushLifecycle().catch(() => undefined);
626
+ console.error(formatSuccess(`Gate "${gate}" on "${opName}" expired at ${now} — not approved`));
627
+ console.error(formatInfo(
628
+ `The next \`chant run ${opName}\` decides this gate from scratch and records a fresh pending fact.`,
629
+ ));
630
+ return 0;
631
+ }
632
+
633
+ // `--approver` wins over `--actor`, the same precedence `chant run approve`
634
+ // has applied since #2126 (`../handlers/run.ts`'s `runOpApprove`). Reading
635
+ // only `--actor` here meant every README that teaches `--approver you`
636
+ // recorded the shell user instead (#2192).
637
+ const outcome = await recordGateApproval(opName, gate, {
638
+ actor: ctx.args.approver ?? ctx.args.actor,
639
+ note: ctx.args.note,
640
+ url: ctx.args.url,
641
+ });
642
+ if (!outcome.ok) return 1;
643
+
644
+ console.error(formatInfo(
645
+ `This records the resolution as a fact; it does not itself re-run anything. ` +
646
+ `Run \`chant run ${opName}\` and it walks through gate "${gate}".`,
647
+ ));
648
+ return 0;
649
+ }
650
+
651
+ /** What a caller supplies alongside the op and gate names. */
652
+ export interface GateApprovalOptions {
653
+ /** `--actor`/`--approver`; falls back to the CI or shell identity. */
654
+ actor?: string;
655
+ /** `--note` — free-text prose. */
656
+ note?: string;
657
+ /** `--url` — the approval surface, validated as absolute http/https. */
658
+ url?: string;
659
+ }
660
+
661
+ export type GateApprovalOutcome =
662
+ | { ok: true; record: GateResolutionRecord }
663
+ | { ok: false };
664
+
665
+ /**
666
+ * The ledger write behind `chant approve` — extracted so `chant run approve`
667
+ * (#2121) records the identical fact before waking the runtime that hosts the
668
+ * gated run, instead of growing a second, drifting writer.
669
+ *
670
+ * Prints the same warning for an undiscovered op and the same success line as
671
+ * `chant approve` always did; the caller adds whatever it does next.
672
+ */
673
+ export async function recordGateApproval(
674
+ opName: string,
675
+ gate: string,
676
+ opts: GateApprovalOptions,
677
+ ): Promise<GateApprovalOutcome> {
388
678
  const { ops } = await discoverOps();
389
679
  if (!ops.has(opName)) {
390
680
  console.error(formatWarning({
@@ -392,18 +682,18 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
392
682
  }));
393
683
  }
394
684
 
395
- const resolvedBy = ctx.args.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER ?? "unknown";
685
+ const resolvedBy = opts.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER ?? "unknown";
396
686
 
397
687
  // #2028: the resolution's link is typed. `--url` wins; otherwise, running
398
688
  // inside the PR/MR job that carries the change is itself the address, the
399
689
  // same env fallback `--actor` uses. `--note` stays free-text prose.
400
- const url = ctx.args.url ?? resolveApprovalUrl();
690
+ const url = opts.url ?? resolveApprovalUrl();
401
691
  if (url && !isApprovalUrl(url)) {
402
692
  console.error(formatError({
403
693
  message: `--url must be an absolute http/https URL (got "${url}")`,
404
694
  hint: "Pass the PR/MR link, or omit --url and put prose in --note.",
405
695
  }));
406
- return 1;
696
+ return { ok: false };
407
697
  }
408
698
 
409
699
  const { record } = await appendGateResolution({
@@ -411,7 +701,7 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
411
701
  gate,
412
702
  resolvedBy,
413
703
  timestamp: new Date().toISOString(),
414
- ...(ctx.args.note ? { note: ctx.args.note } : {}),
704
+ ...(opts.note ? { note: opts.note } : {}),
415
705
  ...(url ? { url } : {}),
416
706
  });
417
707
  await pushLifecycle().catch(() => undefined);
@@ -420,9 +710,5 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
420
710
  `Gate "${gate}" on "${opName}" resolved by ${record.resolvedBy} at ${record.timestamp}` +
421
711
  (record.url ? ` (${record.url})` : ""),
422
712
  ));
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;
713
+ return { ok: true, record };
428
714
  }