@intentius/chant 0.124.26 → 0.124.28

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 (459) hide show
  1. package/README.md +1 -1
  2. package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
  3. package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
  4. package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  6. package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
  7. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  8. package/dist/__golden__/wave-loop/op-held-on.json +2 -0
  9. package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
  10. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  11. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  12. package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
  13. package/dist/__golden__/wave-loop/op-per-run.json +7 -0
  14. package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
  15. package/dist/__golden__/wave-loop/op-sealed.json +3 -0
  16. package/dist/__golden__/wave-loop/op-spend.json +3 -0
  17. package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
  18. package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
  19. package/dist/change-set-graph.d.ts +56 -0
  20. package/dist/change-set-graph.d.ts.map +1 -0
  21. package/dist/change-set-graph.js +128 -0
  22. package/dist/change-set.d.ts +52 -3
  23. package/dist/change-set.d.ts.map +1 -1
  24. package/dist/change-set.js +35 -4
  25. package/dist/chant-version.d.ts +9 -0
  26. package/dist/chant-version.d.ts.map +1 -0
  27. package/dist/chant-version.js +16 -0
  28. package/dist/cli/commands/build.d.ts +8 -0
  29. package/dist/cli/commands/build.d.ts.map +1 -1
  30. package/dist/cli/commands/build.js +9 -8
  31. package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
  32. package/dist/cli/commands/check-lexicon-docs.js +30 -2
  33. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  34. package/dist/cli/commands/check-lexicon.js +13 -2
  35. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
  36. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
  38. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon.js +17 -8
  40. package/dist/cli/handlers/approve.d.ts.map +1 -1
  41. package/dist/cli/handlers/approve.js +19 -4
  42. package/dist/cli/handlers/build.d.ts.map +1 -1
  43. package/dist/cli/handlers/build.js +28 -0
  44. package/dist/cli/handlers/change-set.d.ts +10 -0
  45. package/dist/cli/handlers/change-set.d.ts.map +1 -1
  46. package/dist/cli/handlers/change-set.js +51 -1
  47. package/dist/cli/handlers/fan-out.d.ts +1 -1
  48. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  49. package/dist/cli/handlers/fan-out.js +14 -3
  50. package/dist/cli/handlers/pr.d.ts +8 -2
  51. package/dist/cli/handlers/pr.d.ts.map +1 -1
  52. package/dist/cli/handlers/pr.js +148 -24
  53. package/dist/cli/handlers/promote.d.ts.map +1 -1
  54. package/dist/cli/handlers/promote.js +256 -21
  55. package/dist/cli/handlers/release.d.ts +34 -0
  56. package/dist/cli/handlers/release.d.ts.map +1 -0
  57. package/dist/cli/handlers/release.js +203 -0
  58. package/dist/cli/handlers/run.d.ts +28 -2
  59. package/dist/cli/handlers/run.d.ts.map +1 -1
  60. package/dist/cli/handlers/run.js +170 -13
  61. package/dist/cli/main.d.ts.map +1 -1
  62. package/dist/cli/main.js +88 -13
  63. package/dist/cli/mcp/workspace-tools.js +1 -1
  64. package/dist/cli/parse-args.d.ts.map +1 -1
  65. package/dist/cli/parse-args.js +49 -3
  66. package/dist/cli/registry.d.ts +29 -3
  67. package/dist/cli/registry.d.ts.map +1 -1
  68. package/dist/cli/run-events.d.ts +29 -0
  69. package/dist/cli/run-events.d.ts.map +1 -0
  70. package/dist/cli/run-events.js +47 -0
  71. package/dist/cli/version.d.ts +3 -5
  72. package/dist/cli/version.d.ts.map +1 -1
  73. package/dist/cli/version.js +3 -13
  74. package/dist/codegen/docs-in-site.d.ts +71 -0
  75. package/dist/codegen/docs-in-site.d.ts.map +1 -0
  76. package/dist/codegen/docs-in-site.js +177 -0
  77. package/dist/codegen/docs-types.d.ts +14 -1
  78. package/dist/codegen/docs-types.d.ts.map +1 -1
  79. package/dist/codegen/docs.d.ts +18 -1
  80. package/dist/codegen/docs.d.ts.map +1 -1
  81. package/dist/codegen/docs.js +40 -7
  82. package/dist/codegen/rehype-base-url.mjs +15 -9
  83. package/dist/components/auto-release.d.ts +4 -1
  84. package/dist/components/auto-release.d.ts.map +1 -1
  85. package/dist/components/auto-release.js +2 -0
  86. package/dist/components/cli-support.d.ts +6 -0
  87. package/dist/components/cli-support.d.ts.map +1 -1
  88. package/dist/components/cli-support.js +10 -9
  89. package/dist/components/driver.d.ts +42 -9
  90. package/dist/components/driver.d.ts.map +1 -1
  91. package/dist/components/driver.js +81 -54
  92. package/dist/components/fan-out-run.d.ts +7 -0
  93. package/dist/components/fan-out-run.d.ts.map +1 -1
  94. package/dist/components/fan-out-run.js +35 -26
  95. package/dist/components/pr-run.d.ts +11 -0
  96. package/dist/components/pr-run.d.ts.map +1 -1
  97. package/dist/components/pr-run.js +20 -1
  98. package/dist/components/promote-order.d.ts +38 -0
  99. package/dist/components/promote-order.d.ts.map +1 -0
  100. package/dist/components/promote-order.js +62 -0
  101. package/dist/components/promote.d.ts +15 -2
  102. package/dist/components/promote.d.ts.map +1 -1
  103. package/dist/components/promote.js +17 -0
  104. package/dist/components/release-compile.d.ts +133 -0
  105. package/dist/components/release-compile.d.ts.map +1 -0
  106. package/dist/components/release-compile.js +376 -0
  107. package/dist/components/release-gate.d.ts +125 -0
  108. package/dist/components/release-gate.d.ts.map +1 -0
  109. package/dist/components/release-gate.js +282 -0
  110. package/dist/components/release-promote.d.ts +96 -0
  111. package/dist/components/release-promote.d.ts.map +1 -0
  112. package/dist/components/release-promote.js +204 -0
  113. package/dist/components/run-progress.d.ts +7 -0
  114. package/dist/components/run-progress.d.ts.map +1 -1
  115. package/dist/components/wave-plan.d.ts +3 -1
  116. package/dist/components/wave-plan.d.ts.map +1 -1
  117. package/dist/components/wave-plan.js +1 -1
  118. package/dist/config.d.ts +7 -0
  119. package/dist/config.d.ts.map +1 -1
  120. package/dist/config.js +1 -0
  121. package/dist/index.d.ts +1 -0
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +2 -0
  124. package/dist/lifecycle/change-set-store.d.ts +67 -0
  125. package/dist/lifecycle/change-set-store.d.ts.map +1 -0
  126. package/dist/lifecycle/change-set-store.js +96 -0
  127. package/dist/lifecycle/commit-sha.d.ts +13 -0
  128. package/dist/lifecycle/commit-sha.d.ts.map +1 -0
  129. package/dist/lifecycle/commit-sha.js +21 -0
  130. package/dist/lifecycle/gate-change-set.d.ts +69 -0
  131. package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
  132. package/dist/lifecycle/gate-change-set.js +106 -0
  133. package/dist/lifecycle/gate-ledger.d.ts +49 -1
  134. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  135. package/dist/lifecycle/gate-ledger.js +18 -0
  136. package/dist/lifecycle/release-cut.d.ts +107 -0
  137. package/dist/lifecycle/release-cut.d.ts.map +1 -0
  138. package/dist/lifecycle/release-cut.js +273 -0
  139. package/dist/lifecycle/release-ledger.d.ts +54 -0
  140. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  141. package/dist/lifecycle/release-ledger.js +53 -20
  142. package/dist/lifecycle/release-store.d.ts +190 -0
  143. package/dist/lifecycle/release-store.d.ts.map +1 -0
  144. package/dist/lifecycle/release-store.js +318 -0
  145. package/dist/lifecycle/run-ledger.d.ts +4 -0
  146. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  147. package/dist/lifecycle/run-ledger.js +2 -0
  148. package/dist/op/activities/change-coverage.d.ts.map +1 -1
  149. package/dist/op/activities/change-coverage.js +4 -2
  150. package/dist/op/activities/source-release.d.ts.map +1 -1
  151. package/dist/op/activities/source-release.js +7 -3
  152. package/dist/op/activities/source-rollback.d.ts.map +1 -1
  153. package/dist/op/activities/source-rollback.js +7 -3
  154. package/dist/op/activity-registry.d.ts +6 -3
  155. package/dist/op/activity-registry.d.ts.map +1 -1
  156. package/dist/op/activity-registry.js +7 -5
  157. package/dist/op/discover.d.ts.map +1 -1
  158. package/dist/op/discover.js +14 -7
  159. package/dist/op/gate-name.d.ts +11 -2
  160. package/dist/op/gate-name.d.ts.map +1 -1
  161. package/dist/op/gate-name.js +12 -3
  162. package/dist/op/gate-run-locator.d.ts +22 -0
  163. package/dist/op/gate-run-locator.d.ts.map +1 -1
  164. package/dist/op/gate-run-locator.js +31 -0
  165. package/dist/op/gate.d.ts +27 -3
  166. package/dist/op/gate.d.ts.map +1 -1
  167. package/dist/op/gate.js +57 -3
  168. package/dist/op/local-executor.d.ts +36 -1
  169. package/dist/op/local-executor.d.ts.map +1 -1
  170. package/dist/op/local-executor.js +158 -52
  171. package/dist/op/op-waves-run.d.ts.map +1 -1
  172. package/dist/op/op-waves-run.js +4 -0
  173. package/dist/op/run-live.d.ts +2 -0
  174. package/dist/op/run-live.d.ts.map +1 -1
  175. package/dist/op/run-live.js +1 -1
  176. package/dist/op/runtime.d.ts +46 -0
  177. package/dist/op/runtime.d.ts.map +1 -1
  178. package/dist/op/runtimes/local.d.ts.map +1 -1
  179. package/dist/op/runtimes/local.js +12 -2
  180. package/dist/point-decision.d.ts +2 -0
  181. package/dist/point-decision.d.ts.map +1 -1
  182. package/dist/point-decision.js +9 -2
  183. package/dist/run-event-store.d.ts +77 -0
  184. package/dist/run-event-store.d.ts.map +1 -0
  185. package/dist/run-event-store.js +204 -0
  186. package/dist/run-event-stream.d.ts +132 -0
  187. package/dist/run-event-stream.d.ts.map +1 -0
  188. package/dist/run-event-stream.js +238 -0
  189. package/dist/run-event.d.ts +267 -0
  190. package/dist/run-event.d.ts.map +1 -0
  191. package/dist/run-event.js +207 -0
  192. package/dist/wave-runtime.mjs +225 -242
  193. package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
  194. package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
  195. package/dist/workspace/__fixtures__/run-event-check.js +59 -0
  196. package/dist/workspace/change-set-show.d.ts +56 -0
  197. package/dist/workspace/change-set-show.d.ts.map +1 -0
  198. package/dist/workspace/change-set-show.js +54 -0
  199. package/dist/workspace/change-set-show.schema.json +102 -0
  200. package/dist/workspace/change-set.schema.json +28 -1
  201. package/dist/workspace/changes-cli.d.ts.map +1 -1
  202. package/dist/workspace/changes-cli.js +10 -1
  203. package/dist/workspace/changes.d.ts +37 -0
  204. package/dist/workspace/changes.d.ts.map +1 -1
  205. package/dist/workspace/changes.js +49 -2
  206. package/dist/workspace/changes.schema.json +135 -0
  207. package/dist/workspace/ci-green-cli.d.ts +19 -4
  208. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  209. package/dist/workspace/ci-green-cli.js +75 -12
  210. package/dist/workspace/ci-green-forge.d.ts +20 -3
  211. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  212. package/dist/workspace/ci-green-forge.js +6 -4
  213. package/dist/workspace/ci-green-workflow.d.ts +11 -0
  214. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  215. package/dist/workspace/ci-green-workflow.js +6 -0
  216. package/dist/workspace/ci-green.d.ts +273 -13
  217. package/dist/workspace/ci-green.d.ts.map +1 -1
  218. package/dist/workspace/ci-green.js +449 -38
  219. package/dist/workspace/ci-last-green.schema.json +32 -2
  220. package/dist/workspace/ci-list.schema.json +525 -0
  221. package/dist/workspace/commit-links.d.ts +85 -0
  222. package/dist/workspace/commit-links.d.ts.map +1 -0
  223. package/dist/workspace/commit-links.js +163 -0
  224. package/dist/workspace/declaration.d.ts +19 -0
  225. package/dist/workspace/declaration.d.ts.map +1 -1
  226. package/dist/workspace/declaration.js +1 -1
  227. package/dist/workspace/declaration.schema.json +12 -0
  228. package/dist/workspace/identity-gate-rule.d.ts +37 -0
  229. package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
  230. package/dist/workspace/identity-gate-rule.js +72 -7
  231. package/dist/workspace/identity.d.ts +12 -0
  232. package/dist/workspace/identity.d.ts.map +1 -1
  233. package/dist/workspace/identity.js +13 -1
  234. package/dist/workspace/intent.d.ts +6 -1
  235. package/dist/workspace/intent.d.ts.map +1 -1
  236. package/dist/workspace/intent.js +51 -1
  237. package/dist/workspace/ls.d.ts +4 -0
  238. package/dist/workspace/ls.d.ts.map +1 -1
  239. package/dist/workspace/ls.js +2 -0
  240. package/dist/workspace/ls.schema.json +3 -1
  241. package/dist/workspace/member-pipeline.d.ts +6 -0
  242. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  243. package/dist/workspace/member-pipeline.js +6 -0
  244. package/dist/workspace/promotion-order.d.ts +41 -0
  245. package/dist/workspace/promotion-order.d.ts.map +1 -0
  246. package/dist/workspace/promotion-order.js +95 -0
  247. package/dist/workspace/reason-codes.d.ts +2 -0
  248. package/dist/workspace/reason-codes.d.ts.map +1 -1
  249. package/dist/workspace/reason-codes.js +4 -1
  250. package/dist/workspace/releases.d.ts +72 -0
  251. package/dist/workspace/releases.d.ts.map +1 -0
  252. package/dist/workspace/releases.js +94 -0
  253. package/dist/workspace/releases.schema.json +391 -0
  254. package/dist/workspace/run-event.schema.json +288 -0
  255. package/dist/workspace/status-gates.d.ts +26 -1
  256. package/dist/workspace/status-gates.d.ts.map +1 -1
  257. package/dist/workspace/status-gates.js +31 -2
  258. package/dist/workspace/status-stewards.d.ts +4 -0
  259. package/dist/workspace/status-stewards.d.ts.map +1 -1
  260. package/dist/workspace/status-stewards.js +2 -0
  261. package/dist/workspace/status.d.ts +39 -0
  262. package/dist/workspace/status.d.ts.map +1 -1
  263. package/dist/workspace/status.js +48 -1
  264. package/dist/workspace/status.schema.json +70 -1
  265. package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
  266. package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
  267. package/dist/workspace/trust/ci-tag-statement.js +123 -0
  268. package/dist/workspace/trust/op-run-statement.d.ts +91 -0
  269. package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
  270. package/dist/workspace/trust/op-run-statement.js +143 -0
  271. package/dist/workspace/trust/rotation.d.ts +18 -2
  272. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  273. package/dist/workspace/trust/rotation.js +65 -16
  274. package/dist/workspace/trust/seal.d.ts +11 -0
  275. package/dist/workspace/trust/seal.d.ts.map +1 -1
  276. package/dist/workspace/trust/seal.js +9 -0
  277. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  278. package/dist/workspace/trust/ssh-commit.js +10 -10
  279. package/package.json +1 -1
  280. package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
  281. package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
  282. package/src/__golden__/wave-loop/fan-out-only.json +6 -0
  283. package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  284. package/src/__golden__/wave-loop/op-cost-member.json +3 -0
  285. package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  286. package/src/__golden__/wave-loop/op-held-on.json +2 -0
  287. package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
  288. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  289. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  290. package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
  291. package/src/__golden__/wave-loop/op-per-run.json +7 -0
  292. package/src/__golden__/wave-loop/op-pr-review.json +7 -0
  293. package/src/__golden__/wave-loop/op-sealed.json +3 -0
  294. package/src/__golden__/wave-loop/op-spend.json +3 -0
  295. package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
  296. package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
  297. package/src/change-set-graph.test.ts +72 -0
  298. package/src/change-set-graph.ts +133 -0
  299. package/src/change-set.ts +76 -6
  300. package/src/chant-version.ts +16 -0
  301. package/src/cli/commands/build.ts +19 -8
  302. package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
  303. package/src/cli/commands/check-lexicon-docs.ts +39 -2
  304. package/src/cli/commands/check-lexicon.ts +14 -2
  305. package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
  306. package/src/cli/commands/init-lexicon.test.ts +25 -0
  307. package/src/cli/commands/init-lexicon.ts +18 -8
  308. package/src/cli/compiled-cli.e2e.test.ts +31 -6
  309. package/src/cli/handlers/approve-release.test.ts +80 -0
  310. package/src/cli/handlers/approve.ts +18 -2
  311. package/src/cli/handlers/build.test.ts +52 -0
  312. package/src/cli/handlers/build.ts +29 -0
  313. package/src/cli/handlers/change-set.ts +49 -1
  314. package/src/cli/handlers/fan-out.test.ts +48 -0
  315. package/src/cli/handlers/fan-out.ts +15 -3
  316. package/src/cli/handlers/operator.test.ts +37 -0
  317. package/src/cli/handlers/pr.test.ts +194 -0
  318. package/src/cli/handlers/pr.ts +168 -34
  319. package/src/cli/handlers/promote.test.ts +389 -5
  320. package/src/cli/handlers/promote.ts +279 -20
  321. package/src/cli/handlers/release-implicit.e2e.test.ts +153 -0
  322. package/src/cli/handlers/release.e2e.test.ts +85 -0
  323. package/src/cli/handlers/release.ts +206 -0
  324. package/src/cli/handlers/run-auto-release.test.ts +71 -0
  325. package/src/cli/handlers/run-events-cmd.test.ts +92 -0
  326. package/src/cli/handlers/run.test.ts +30 -0
  327. package/src/cli/handlers/run.ts +176 -15
  328. package/src/cli/main.test.ts +7 -0
  329. package/src/cli/main.ts +88 -13
  330. package/src/cli/mcp/never-over-mcp.test.ts +2 -0
  331. package/src/cli/mcp/workspace-tools.ts +1 -1
  332. package/src/cli/parse-args.ts +37 -3
  333. package/src/cli/registry.ts +29 -3
  334. package/src/cli/run-events.ts +54 -0
  335. package/src/cli/version.ts +3 -13
  336. package/src/codegen/docs-in-site.test.ts +166 -0
  337. package/src/codegen/docs-in-site.ts +182 -0
  338. package/src/codegen/docs-site/AgentPrompt.astro +26 -1
  339. package/src/codegen/docs-types.ts +14 -1
  340. package/src/codegen/docs.ts +60 -7
  341. package/src/codegen/rehype-base-url.d.mts +3 -1
  342. package/src/codegen/rehype-base-url.mjs +15 -9
  343. package/src/codegen/rehype-base-url.test.ts +34 -0
  344. package/src/components/auto-release.test.ts +21 -0
  345. package/src/components/auto-release.ts +6 -1
  346. package/src/components/cli-support.test.ts +82 -0
  347. package/src/components/cli-support.ts +16 -9
  348. package/src/components/driver.ts +129 -56
  349. package/src/components/fan-out-run.ts +56 -35
  350. package/src/components/pr-run.ts +30 -1
  351. package/src/components/promote-order.test.ts +90 -0
  352. package/src/components/promote-order.ts +75 -0
  353. package/src/components/promote.test.ts +10 -1
  354. package/src/components/promote.ts +26 -1
  355. package/src/components/release-compile.test.ts +147 -0
  356. package/src/components/release-compile.ts +445 -0
  357. package/src/components/release-gate.test.ts +215 -0
  358. package/src/components/release-gate.ts +341 -0
  359. package/src/components/release-promote.test.ts +159 -0
  360. package/src/components/release-promote.ts +232 -0
  361. package/src/components/run-progress.ts +7 -0
  362. package/src/components/wave-plan.ts +1 -1
  363. package/src/config.ts +7 -1
  364. package/src/index.ts +2 -0
  365. package/src/lifecycle/change-set-store.test.ts +217 -0
  366. package/src/lifecycle/change-set-store.ts +112 -0
  367. package/src/lifecycle/commit-sha.ts +23 -0
  368. package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
  369. package/src/lifecycle/gate-change-set.ts +133 -0
  370. package/src/lifecycle/gate-ledger.test.ts +33 -0
  371. package/src/lifecycle/gate-ledger.ts +66 -1
  372. package/src/lifecycle/release-cut-signed.test.ts +180 -0
  373. package/src/lifecycle/release-cut.test.ts +411 -0
  374. package/src/lifecycle/release-cut.ts +367 -0
  375. package/src/lifecycle/release-ledger.test.ts +14 -0
  376. package/src/lifecycle/release-ledger.ts +85 -17
  377. package/src/lifecycle/release-store.ts +423 -0
  378. package/src/lifecycle/run-ledger.test.ts +10 -0
  379. package/src/lifecycle/run-ledger.ts +6 -0
  380. package/src/op/activities/change-coverage.ts +4 -1
  381. package/src/op/activities/source-release.ts +8 -3
  382. package/src/op/activities/source-rollback.ts +8 -4
  383. package/src/op/activity-registry.ts +7 -5
  384. package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
  385. package/src/op/discover-root.test.ts +39 -0
  386. package/src/op/discover.ts +15 -8
  387. package/src/op/gate-name.ts +13 -3
  388. package/src/op/gate-release.test.ts +126 -0
  389. package/src/op/gate-run-locator.test.ts +110 -0
  390. package/src/op/gate-run-locator.ts +35 -0
  391. package/src/op/gate.ts +88 -6
  392. package/src/op/local-executor.ts +207 -52
  393. package/src/op/op-waves-run.ts +4 -0
  394. package/src/op/run-events.test.ts +166 -0
  395. package/src/op/run-live.ts +1 -1
  396. package/src/op/runtime.ts +39 -0
  397. package/src/op/runtimes/local.test.ts +16 -0
  398. package/src/op/runtimes/local.ts +13 -2
  399. package/src/point-decision.ts +12 -2
  400. package/src/run-event-store.test.ts +144 -0
  401. package/src/run-event-store.ts +213 -0
  402. package/src/run-event-stream.test.ts +175 -0
  403. package/src/run-event-stream.ts +306 -0
  404. package/src/run-event.test.ts +277 -0
  405. package/src/run-event.ts +451 -0
  406. package/src/wave-loop-golden.test.ts +9 -0
  407. package/src/workspace/__fixtures__/run-event-check.ts +58 -0
  408. package/src/workspace/ancestry-index.test.ts +93 -0
  409. package/src/workspace/change-set-show.schema.json +102 -0
  410. package/src/workspace/change-set-show.ts +72 -0
  411. package/src/workspace/change-set.schema.json +28 -1
  412. package/src/workspace/changes-cli.ts +10 -1
  413. package/src/workspace/changes.schema.json +135 -0
  414. package/src/workspace/changes.test.ts +1 -1
  415. package/src/workspace/changes.ts +84 -2
  416. package/src/workspace/ci-green-cli.ts +75 -13
  417. package/src/workspace/ci-green-forge.ts +24 -4
  418. package/src/workspace/ci-green-runs.test.ts +242 -0
  419. package/src/workspace/ci-green-signed.test.ts +302 -0
  420. package/src/workspace/ci-green-workflow.test.ts +16 -0
  421. package/src/workspace/ci-green-workflow.ts +12 -0
  422. package/src/workspace/ci-green.test.ts +131 -2
  423. package/src/workspace/ci-green.ts +624 -42
  424. package/src/workspace/ci-last-green.schema.json +32 -2
  425. package/src/workspace/ci-list.schema.json +525 -0
  426. package/src/workspace/commit-links.test.ts +279 -0
  427. package/src/workspace/commit-links.ts +183 -0
  428. package/src/workspace/declaration.schema.json +12 -0
  429. package/src/workspace/declaration.ts +22 -2
  430. package/src/workspace/identity-gate-rule.ts +91 -7
  431. package/src/workspace/identity.ts +14 -1
  432. package/src/workspace/intent.ts +48 -1
  433. package/src/workspace/ls.schema.json +3 -1
  434. package/src/workspace/ls.ts +6 -0
  435. package/src/workspace/member-pipeline.test.ts +17 -0
  436. package/src/workspace/member-pipeline.ts +9 -0
  437. package/src/workspace/promotion-order.ts +97 -0
  438. package/src/workspace/read-contract.e2e.test.ts +4 -1
  439. package/src/workspace/reason-codes.test.ts +2 -1
  440. package/src/workspace/reason-codes.ts +4 -1
  441. package/src/workspace/releases.schema.json +391 -0
  442. package/src/workspace/releases.test.ts +108 -0
  443. package/src/workspace/releases.ts +122 -0
  444. package/src/workspace/run-event.schema.json +288 -0
  445. package/src/workspace/status-contract.test.ts +52 -1
  446. package/src/workspace/status-gates.ts +56 -1
  447. package/src/workspace/status-stewards.ts +6 -0
  448. package/src/workspace/status.schema.json +70 -1
  449. package/src/workspace/status.ts +80 -1
  450. package/src/workspace/trust/ci-tag-statement.ts +151 -0
  451. package/src/workspace/trust/op-run-statement.ts +148 -0
  452. package/src/workspace/trust/rotation.ts +61 -14
  453. package/src/workspace/trust/seal.ts +10 -0
  454. package/src/workspace/trust/ssh-commit.ts +10 -9
  455. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
  456. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
  457. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
  458. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
  459. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
@@ -10,7 +10,7 @@ import { runOpLocally, findPolicyGateStep, OpRunFailure, type StepRecord } from
10
10
  import { approveCommand, describeGateMismatch, gateIsSealed } from "../../op/gate";
11
11
  import { summaryLedgerPrefix, writeGatedRunSummary, type GatedRunSummary } from "../../op/gate-summary";
12
12
  import { createLocalOpRuntime } from "../../op/runtimes/local";
13
- import type { OpRuntimeProvider, OpRunStatus } from "../../op/runtime";
13
+ import type { OpRuntimeProvider, OpRunRecord, OpRunStatus } from "../../op/runtime";
14
14
  import { pointAnswerCommand, renderHuman, renderJson } from "../../op/local-output";
15
15
  import { loadPlugins } from "../plugins";
16
16
  import { recordGateApproval } from "./operator";
@@ -26,11 +26,15 @@ import { LIFECYCLE_LOCAL_NOTE, isLifecycleLocalNote } from "../../lifecycle/loca
26
26
  import { ledgerRecordingOff, noRecordEndNote, noRecordStartNote, resetNoRecordLedger, NO_RECORD_ENV } from "../../lifecycle/no-record";
27
27
  import { renderDriverHuman, renderDriverJson } from "../../components/driver-output";
28
28
  import { ndjsonProgressSink } from "../../components/run-progress";
29
+ import { findStoredRun, readRunEvents } from "../../run-event-store";
30
+ import type { RunEvent } from "../../run-event";
31
+ import { openCliRunEvents, refusesBothStreams, stdoutLine } from "../run-events";
29
32
  import { maybeRecordAutoRelease } from "../../components/auto-release";
30
33
  import { maybePersistBuildManifest } from "../../components/manifest-persistence";
31
34
  import type { DriverComponentResult } from "../../components/driver";
32
- import { isComponentList, type ComponentRunPlan } from "../../components/cli-support";
33
- import type { ReleaseRecord } from "../../lifecycle/release-ledger";
35
+ import { isComponentList, type ComponentRunPlan, type RunComponentsResult } from "../../components/cli-support";
36
+ import { resolveCiRun, type ReleaseRecord, type RunOrigin } from "../../lifecycle/release-ledger";
37
+ import { gateAdmission, gateApprover } from "../../components/promote";
34
38
  import { runOpGenerate } from "./run-generate";
35
39
 
36
40
  /**
@@ -460,6 +464,85 @@ async function runOpLogOnRuntime(ctx: CommandContext, name: string): Promise<num
460
464
  return 0;
461
465
  }
462
466
 
467
+ // ── chant run events <run> ───────────────────────────────────────────────────
468
+
469
+ /** One event as a line for a person: its time, its type, where it is and how it ended. */
470
+ export function humanRunEventLine(e: RunEvent): string {
471
+ const x = e as unknown as Record<string, unknown>;
472
+ const where = [x.component, x.phase, x.step].filter((v): v is string => typeof v === "string").join("/");
473
+ const detail = [where, typeof x.status === "string" ? x.status : "", typeof x.error === "string" ? x.error : ""].filter(Boolean).join(" ");
474
+ return `${String(x.at ?? "")} ${String(x.type).padEnd(16)}${detail}`.trimEnd();
475
+ }
476
+
477
+ /** The exit code a run's verdict maps to: `chant run`'s own (0 ok, 1 failed, 3 waiting on a person). */
478
+ function exitOfSettle(status: unknown): number {
479
+ return status === "ok" ? 0 : status === "gated" || status === "waiting" ? 3 : 1;
480
+ }
481
+
482
+ /**
483
+ * `chant run events <run> [--json] [--follow]` (#4042): the events a run on
484
+ * this host kept, replayed. `<run>` is a run id, or the name of an Op or a
485
+ * component selection, for which the newest stored run stands. A settled run
486
+ * and a run in progress both replay what is in the file; `--follow` goes on
487
+ * from there until the run settles. `--json` prints the stored lines as they
488
+ * were written; without it each event is one line for a person.
489
+ *
490
+ * The exit code is the replayed run's verdict (0, 1, or 3 for a run waiting
491
+ * on a person). A replay of a run still going, and a follow cut short by
492
+ * Ctrl-C, exit 0.
493
+ */
494
+ export async function runOpEvents(ctx: CommandContext): Promise<number> {
495
+ const ref = ctx.args.extraPositional;
496
+ if (!ref) {
497
+ console.error(formatError({ message: "A run id or an Op name is required: chant run events <run>" }));
498
+ return 1;
499
+ }
500
+ if (ctx.args.on !== undefined && ctx.args.on !== "local") {
501
+ console.error(formatError({
502
+ message: `run events reads the events kept on this host, so --on ${ctx.args.on} has none to read`,
503
+ hint: "A run hosted off this host is read once its in-flight state is off-host (#3990).",
504
+ }));
505
+ return 1;
506
+ }
507
+ const cwd = resolve(".");
508
+ let stored = await findStoredRun(cwd, ref);
509
+ if (!stored && ctx.args.follow) {
510
+ // The run may be starting: give it a moment to open its file.
511
+ const deadline = Date.now() + RUN_EVENTS_WAIT_MS;
512
+ while (!stored && Date.now() < deadline) {
513
+ await new Promise((r) => setTimeout(r, 100));
514
+ stored = await findStoredRun(cwd, ref);
515
+ }
516
+ }
517
+ if (!stored) {
518
+ console.error(formatError({
519
+ message: `No events are kept for "${ref}" on this host`,
520
+ hint: "A run keeps its events when it records to the ledger (not under --no-record), and a command other than `chant run <op>` keeps them under --events-json. Chant 0.124.28 and newer write them.",
521
+ }));
522
+ return 1;
523
+ }
524
+
525
+ const controller = new AbortController();
526
+ const onSigint = () => controller.abort();
527
+ process.on("SIGINT", onSigint);
528
+ try {
529
+ const settle = await readRunEvents(
530
+ stored.file,
531
+ (event, line) => {
532
+ if (ctx.args.json) process.stdout.write(line);
533
+ else console.log(humanRunEventLine(event));
534
+ },
535
+ { follow: ctx.args.follow === true, signal: controller.signal },
536
+ );
537
+ return settle ? exitOfSettle((settle as unknown as { status?: unknown }).status) : 0;
538
+ } finally {
539
+ process.removeListener("SIGINT", onSigint);
540
+ }
541
+ }
542
+
543
+ /** How long `--follow` waits for a run's file to appear. */
544
+ const RUN_EVENTS_WAIT_MS = 5000;
545
+
463
546
  // ── chant run approve <op> <gate> ────────────────────────────────────────────
464
547
 
465
548
  /**
@@ -778,6 +861,7 @@ export async function runOp(ctx: CommandContext): Promise<number> {
778
861
  }));
779
862
  return 1;
780
863
  }
864
+ if (refusesBothStreams(ctx.args)) return 1;
781
865
  if (ctx.args.components) return runOpComponents(ctx);
782
866
  if (ctx.args.report) {
783
867
  console.error(formatError({
@@ -791,6 +875,16 @@ export async function runOp(ctx: CommandContext): Promise<number> {
791
875
 
792
876
  // ── Auto-release recording post-run (#597) ──────────────────────────────────
793
877
 
878
+ /**
879
+ * The run id and origin an auto-recorded release names (#4020): the CI run
880
+ * when the process is in CI, so the release and the gate it passed (which
881
+ * records the CI run id) name the same run, else `localId` with the `local`
882
+ * origin.
883
+ */
884
+ export function autoReleaseRun(localId: string): { runId: string; runOrigin: RunOrigin } {
885
+ return resolveCiRun() ?? { runId: localId, runOrigin: { forge: "local" } };
886
+ }
887
+
794
888
  /**
795
889
  * After a successful `chant run --components` (local executor), auto-emit
796
890
  * one release record per successfully deployed component that published a
@@ -831,6 +925,7 @@ export async function recordAutoReleasesForRun(
831
925
  env: string,
832
926
  runId: string,
833
927
  disabled: boolean,
928
+ runOrigin: RunOrigin = { forge: "local" },
834
929
  ): Promise<ReleaseRecord[]> {
835
930
  const recorded: ReleaseRecord[] = [];
836
931
  for (const componentResult of results) {
@@ -842,8 +937,11 @@ export async function recordAutoReleasesForRun(
842
937
  success: true,
843
938
  records: componentResult.records,
844
939
  runId,
845
- // A local-executor id resolves nowhere — say so in a field (#2045).
846
- runOrigin: { forge: "local" },
940
+ // A local-executor id resolves nowhere, and says so in a field (#2045). A run in CI names the CI run.
941
+ runOrigin,
942
+ // #1035, #4020: who approved at the gate and which resolution admitted the deploy.
943
+ ...(gateApprover(componentResult) ? { approver: gateApprover(componentResult) } : {}),
944
+ ...(gateAdmission(componentResult) ? { admission: gateAdmission(componentResult) } : {}),
847
945
  },
848
946
  { disabled },
849
947
  );
@@ -1011,6 +1109,10 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1011
1109
  // `undefined` and every `onProgress?.(...)` call in the driver is a no-op —
1012
1110
  // behavior is byte-for-byte unchanged from before this flag existed.
1013
1111
  const onProgress = ctx.args.progressJson ? ndjsonProgressSink() : undefined;
1112
+ // `--events-json` (#4041): the run-event stream instead. The run is named by
1113
+ // the id its release records carry, so the two join.
1114
+ const release = autoReleaseRun(`local-${Date.now()}`);
1115
+ const events = await openCliRunEvents(ctx.args, { kind: "component", name: selector, runId: release.runId, cwd: projectPath });
1014
1116
  // A list (#3950) pulls in what its components depend on; say which ones and
1015
1117
  // in what order before the first one runs. stderr, so --json and
1016
1118
  // --progress-json keep stdout to themselves.
@@ -1020,14 +1122,25 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1020
1122
  if (plan.added.length > 0) console.error(formatInfo(`added as dependencies: ${plan.added.join(", ")}`));
1021
1123
  }
1022
1124
  : undefined;
1023
- const result = await runtime.runComponents(projectPath, selector, {
1024
- env: ctx.args.env,
1025
- componentOutputs: seededOutputs,
1026
- onProgress,
1027
- sandbox: ctx.args.sandbox,
1028
- buildParams: paramsResolution.provenance,
1029
- ...(onPlan ? { onPlan } : {}),
1030
- });
1125
+ let result: RunComponentsResult;
1126
+ try {
1127
+ result = await runtime.runComponents(projectPath, selector, {
1128
+ env: ctx.args.env,
1129
+ componentOutputs: seededOutputs,
1130
+ onProgress,
1131
+ ...(events ? { onRunEvent: events.emit } : {}),
1132
+ sandbox: ctx.args.sandbox,
1133
+ buildParams: paramsResolution.provenance,
1134
+ ...(onPlan ? { onPlan } : {}),
1135
+ });
1136
+ } catch (err) {
1137
+ events?.settle("failed", err instanceof Error ? err.message : String(err));
1138
+ throw err;
1139
+ }
1140
+ // A run that never reached its components (a bad selector, a cycle) still ends its stream.
1141
+ if (result.success) events?.settle("ok");
1142
+ else if (result.gated) events?.settle("gated");
1143
+ else events?.settle("failed", result.error);
1031
1144
 
1032
1145
  // Dump the accumulated outputs for a downstream job to seed from. Written
1033
1146
  // even on failure (partial outputs) so a resumed run still has what completed.
@@ -1094,7 +1207,7 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1094
1207
  if (result.success && result.run) {
1095
1208
  // #3955: a run that records nothing has no release to report either.
1096
1209
  const disabled = resolveAutoReleaseDisabled(config, ctx.args.noReleaseRecord) || ledgerRecordingOff();
1097
- const recorded = await recordAutoReleasesForRun(result.run.results, env, `local-${Date.now()}`, disabled);
1210
+ const recorded = await recordAutoReleasesForRun(result.run.results, env, release.runId, disabled, release.runOrigin);
1098
1211
  // `--digest-file` (#2602): the releases this run recorded, one
1099
1212
  // `<component>=<digest>` line each, the form `chant components promote
1100
1213
  // --digest` takes. A generated promote job reads it to promote exactly
@@ -1182,6 +1295,15 @@ async function runOpOnResolvedRuntime(ctx: CommandContext): Promise<number> {
1182
1295
  return 1;
1183
1296
  }
1184
1297
 
1298
+ // #4044: a runner key signs the run's record as it settles, so `ci tick`
1299
+ // can count the run. `--key` must be a runner key; the PEM in
1300
+ // CHANT_RUNNER_KEY that is not one leaves the run unsigned, with a warning.
1301
+ const signRecord = await runSignerFor(ctx);
1302
+ if (signRecord === null) {
1303
+ await stewardGate?.release?.();
1304
+ return 1;
1305
+ }
1306
+
1185
1307
  // `--progress-json` streams one NDJSON StepRecord per settled step, fed by
1186
1308
  // whatever the runtime reports through `progress`.
1187
1309
  const progress = ctx.args.progressJson ? ndjsonProgressSink<StepRecord>() : undefined;
@@ -1217,6 +1339,10 @@ async function runOpOnResolvedRuntime(ctx: CommandContext): Promise<number> {
1217
1339
  ...(params ? { params } : {}),
1218
1340
  ...(ctx.args.profile !== undefined ? { profile: ctx.args.profile } : {}),
1219
1341
  progress,
1342
+ // `--events-json` (#4041): the runtime writes the run's events itself,
1343
+ // since it is the one that knows the run's id and where it ran.
1344
+ ...(ctx.args.eventsJson ? { runEvents: stdoutLine } : {}),
1345
+ ...(signRecord ? { signRecord } : {}),
1220
1346
  signal: controller.signal,
1221
1347
  ...(work ? { work } : {}),
1222
1348
  });
@@ -1362,7 +1488,42 @@ async function resolveRunWork(
1362
1488
  export function runOpUnknown(ctx: CommandContext): Promise<number> {
1363
1489
  console.error(formatError({
1364
1490
  message: `Unknown run subcommand: ${ctx.args.extraPositional ?? ctx.args.path}`,
1365
- hint: "Available: chant run <name>, run list, run status, run approve, run cancel, run log",
1491
+ hint: "Available: chant run <name>, run list, run status, run approve, run cancel, run log, run events",
1366
1492
  }));
1367
1493
  return Promise.resolve(1);
1368
1494
  }
1495
+
1496
+ /** The variable a runner key is read from, as PEM, as `chant ci tick` reads it (`RUNNER_KEY_ENV` in ../../workspace/ci-green.ts). */
1497
+ const RUNNER_KEY_ENV = "CHANT_RUNNER_KEY";
1498
+
1499
+ /**
1500
+ * What signs the run's record (#4044): the runner key `--key` names, or the
1501
+ * PEM in `CHANT_RUNNER_KEY`, checked against `.chant/trust.json` at `HEAD`.
1502
+ * Undefined when neither is given. Null, after printing why, when `--key`
1503
+ * is not a runner key; an unusable `CHANT_RUNNER_KEY` only warns, since the
1504
+ * variable may be set for `ci tick` in a job that also runs Ops.
1505
+ */
1506
+ async function runSignerFor(ctx: CommandContext): Promise<((record: OpRunRecord) => NonNullable<OpRunRecord["statement"]>) | undefined | null> {
1507
+ const fromFlag = ctx.args.key !== undefined;
1508
+ let pem: string | undefined;
1509
+ try {
1510
+ pem = fromFlag ? readFileSync(resolve(ctx.args.key!), "utf-8") : process.env[RUNNER_KEY_ENV] || undefined;
1511
+ } catch (err) {
1512
+ console.error(formatError({ message: `--key ${ctx.args.key}: ${err instanceof Error ? err.message : String(err)}` }));
1513
+ return null;
1514
+ }
1515
+ if (pem === undefined) return undefined;
1516
+ try {
1517
+ // Loaded only when a key is given, so a run without one loads no workspace code (level 0, #2525).
1518
+ const { opRunSigner } = await import("../../workspace/ci-green");
1519
+ return opRunSigner(process.cwd(), pem);
1520
+ } catch (err) {
1521
+ const message = err instanceof Error ? err.message : String(err);
1522
+ if (fromFlag) {
1523
+ console.error(formatError({ message: `the run would not be signed: ${message}` }));
1524
+ return null;
1525
+ }
1526
+ console.error(formatWarning({ message: `${RUNNER_KEY_ENV} is set, and the run will not be signed: ${message}` }));
1527
+ return undefined;
1528
+ }
1529
+ }
@@ -16,6 +16,13 @@ describe("parseArgs", () => {
16
16
  expect(() => parseArgs(["graph", "--traffic"])).toThrow(/--traffic needs a level/);
17
17
  });
18
18
 
19
+ test("--before-script is repeatable and keeps its order; --image takes a reference (#4002)", () => {
20
+ const args = parseArgs(["build", "--components", "--generate", "forgejo", "--image", "node:22-bookworm", "--before-script", "npm ci", "--before-script", "-x"]);
21
+ expect(args.image).toBe("node:22-bookworm");
22
+ expect(args.beforeScript).toEqual(["npm ci", "-x"]);
23
+ expect(() => parseArgs(["build", "--before-script"])).toThrow(/--before-script needs a shell line/);
24
+ });
25
+
19
26
  test("--skip-undeclarable reaches live import (#3911)", () => {
20
27
  expect(parseArgs(["import", "--from", "prod"]).skipUndeclarable).toBeUndefined();
21
28
  expect(parseArgs(["import", "--from", "prod", "--skip-undeclarable"]).skipUndeclarable).toBe(true);
package/src/cli/main.ts CHANGED
@@ -114,6 +114,7 @@ const runOpApprove = lazyHandler(runHandlers, (m) => m.runOpApprove);
114
114
  const runOpSignalRenamed = lazyHandler(runHandlers, (m) => m.runOpSignalRenamed);
115
115
  const runOpCancel = lazyHandler(runHandlers, (m) => m.runOpCancel);
116
116
  const runOpLog = lazyHandler(runHandlers, (m) => m.runOpLog);
117
+ const runOpEvents = lazyHandler(runHandlers, (m) => m.runOpEvents);
117
118
  const runWaveHandlers = () => import("./handlers/run-wave");
118
119
  const runOpWaveCommand = lazyHandler(runWaveHandlers, (m) => m.runOpWaveCommand);
119
120
  const operatorHandlers = () => import("./handlers/operator");
@@ -227,22 +228,61 @@ Commands:
227
228
  applied) and path. Read-only.
228
229
 
229
230
  CI:
230
- ci last-green [--json] The newest first-parent commit of ci.green's branch
231
- with a ci/green tag and no ci/revoked tag. Reads local
232
- tags only; fetch them first.
233
- ci tick [--dry-run] [--forge github] Tag each commit within ci.green's
234
- window whose required phases all passed ci/green/<sha>,
235
- and revoke a green one that now fails ci/revoked/<sha>.
236
- Reads check runs from the forge, pushes the tags.
231
+ ci last-green [--json] [--from <rev>] The newest first-parent commit of
232
+ ci.green's branch, or of --from, with a ci/green tag
233
+ and no ci/revoked tag. Reads local tags only; fetch
234
+ them first. With ci.green.signedSince, a tag without a
235
+ valid runner statement is ignored and reported.
236
+ ci list [--json] [--limit <n>] [--since <rev>] [--forge github] [--source checks|runs]
237
+ The first-parent commits of ci.green's branch within
238
+ its window, newest first, each green, revoked or
239
+ undecided with its required phases' results. Reads
240
+ local tags; --forge reads check runs for undecided
241
+ commits, and source runs reads the signed Op runs on
242
+ the local chant/lifecycle. --since replaces the window.
243
+ ci tick [--dry-run] [--forge github] [--source checks|runs] [--key <runner key.pem>]
244
+ Tag each commit within ci.green's window whose
245
+ required phases all passed ci/green/<sha>, and revoke
246
+ a green one that now fails ci/revoked/<sha>. Reads
247
+ check runs from the forge, or with source runs
248
+ (ci.green.source or --source) the Op runs on
249
+ chant/lifecycle that a runner key signed. Pushes the
250
+ tags. --key, or the PEM in CHANT_RUNNER_KEY, signs
251
+ each tag with a runner key.
237
252
  ci workflow [--workflow <name>]... [--output <file>] Write
238
253
  [--chant <command>] [--install <command>]
239
254
  [--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>]
255
+ [--runner-key-secret <NAME>]
240
256
  .github/workflows/chant-ci-green.yml, which runs the
241
257
  tick when a required workflow completes and every 15m.
242
258
  --chant replaces the npx @intentius/chant pin the tick
243
259
  runs; --install adds a step before it. --token-secret,
244
260
  or a GitHub App's id and key, pushes the tags with a
245
261
  token that may tag commits changing workflow files.
262
+ --runner-key-secret hands the tick the runner key it
263
+ signs its tags with.
264
+
265
+ Releases:
266
+ release cut [<label>] [--build <command>] [--param <k=v>] [--params-file <file>]
267
+ [--src <dir>] [-o <file>] [--lexicon <name>] [--sign [<key file>]]
268
+ Cut a release of this member from HEAD, which must
269
+ carry a ci/green tag: compile every environment in
270
+ the config's environments, build the artifacts once,
271
+ and store it on chant/lifecycle under its digest.
272
+ --build runs before each environment's templates are
273
+ read, with CHANT_ENV set; {env} in --params-file is
274
+ replaced by the environment. --sign signs the cut with
275
+ the actor's ssh key; an identity.gates release entry
276
+ requires it. A project with no components releases
277
+ what chant build --env <env> writes: --src <dir> is
278
+ the build root, -o <file> the output (dist/build.yaml),
279
+ --lexicon narrows it.
280
+ release list [--json] Every release of this member with its targets, its
281
+ cuts and the environments that run it.
282
+ release export <label|digest> --env <env> [-o <dir>] [--json]
283
+ Write the files the release recorded for <env>, each
284
+ checked against its digest, at their recorded paths
285
+ under <dir> (default: here), for a deploy to read.
246
286
 
247
287
  Change sets:
248
288
  change-set summary <file> The grouped plan summary of a change-set
@@ -251,6 +291,10 @@ Change sets:
251
291
  replacement, failure and hole named.
252
292
  markdown fits an MR/PR note of --limit
253
293
  characters (default 65536). Read-only.
294
+ change-set show <digest> The change set a pull-request or release gate
295
+ [--member <name>] [--json] bound, stored on
296
+ chant/lifecycle under its digest, each
297
+ entry with its graph node. Read-only.
254
298
 
255
299
  Ops:
256
300
  run <name> Run an Op on the resolved runtime (--on; local by default)
@@ -260,11 +304,18 @@ Ops:
260
304
  its Drift outcome is true (WatchOp failOn: "drift")
261
305
  --no-record: a test run that records nothing on
262
306
  chant/lifecycle and pushes nothing (local runtime)
307
+ --key <runner key.pem>, or the PEM in
308
+ CHANT_RUNNER_KEY: sign the run's record with a runner
309
+ key, so ci tick from runs counts it (#4044)
263
310
  run list List all Ops with the runtime's state for each
264
311
  run status <name> Show the runtime's state for one Op's latest run
265
312
  run approve <op> <gate> Record a gate's resolution and wake the runtime
266
313
  run cancel <name> Cancel the active run (requires --force)
267
314
  run log <name> Show run history for an Op
315
+ run events <run> Replay a run's events kept on this host (a run id, or
316
+ an Op or component name for its newest run). --json
317
+ prints the run-event lines; --follow streams a run in
318
+ progress until run-settle (#4042)
268
319
  run wave --spec <file> --wave <k> Plan, gate and apply wave k of an Op
269
320
  waves spec (#3679): the job a generated waves
270
321
  pipeline runs. --decide plans and decides only;
@@ -299,6 +350,11 @@ Ops:
299
350
  consumer to render live wave/component/phase/step
300
351
  progress instead of tailing raw logs (additive; run
301
352
  semantics/exit code unchanged)
353
+ --events-json: stream the run's events instead, one
354
+ run-event.schema.json line each, with the run id,
355
+ commit and runtime on every line (#4041). Also on
356
+ run <op>, components promote|rollback|redeploy,
357
+ fan-out, pr-plan and pr-apply
302
358
  operator Run scheduled ticks for discovered ConvergeOps
303
359
  locally (#1485): acquire/renew a per-op
304
360
  lease (git ref CAS), tick on --interval, record every
@@ -664,7 +720,13 @@ Workspace (level 1, #2524):
664
720
  the severity (warn by default) and ignore globs. When
665
721
  the declaration at base has writeScope or agents, each
666
722
  commit that writes outside its writer's scope fails
667
- the check (write-scope-member, write-scope-kind)
723
+ the check (write-scope-member, write-scope-kind). Each
724
+ first-parent commit of the range should name its work
725
+ item in a Chant-Record: <work kind>:<id> or a
726
+ Chant-Lease trailer. commit-unlinked is a commit that
727
+ names none, commit-wrongly-linked one whose trailers
728
+ name no work item that exists; both follow the
729
+ severity
668
730
  workspace patch <base>..<head>|<base>...<head>|<commit> [--path <p>...] [--max-bytes <n>] [--json]
669
731
  workspace patch [<commit>] --worktree [--path <p>...] [--max-bytes <n>] [--json]
670
732
  The hunks of a diff, file by file: a range, a work
@@ -787,9 +849,12 @@ Component release ledger + status:
787
849
  byte-for-byte plus a self-describing manifest.json
788
850
  components promote Deploy the digests one environment's release
789
851
  ledger records to another, without a build
790
- (--from <env> --to <env> [--component <name>
791
- [--digest <sha256:...>]]; --dry-run prints the
792
- plan; the target's gates still apply)
852
+ (--to <env> [--from <env>] [--allow-skip]
853
+ [--release <label|digest>]
854
+ [--component <name> [--digest <sha256:...>]];
855
+ --from defaults to the environment before --to
856
+ in the config's environments; --dry-run prints
857
+ the plan; the target's gates still apply)
793
858
  components rollback <env> Redeploy an earlier release of an environment
794
859
  from its recorded digest (--component <name>
795
860
  [--digest <sha256:...>]; defaults to the release
@@ -934,7 +999,7 @@ Options:
934
999
  chant.config.ts's build.sandbox: true; #1045)
935
1000
  --param <name=value> (build, graph, run --components, components fan-out,
936
1001
  components promote, components rollback,
937
- components redeploy) Bind a
1002
+ components redeploy, release cut) Bind a
938
1003
  declared build-time parameter (chant.config.ts's buildParams)
939
1004
  to a value, for source to read as params.<name>
940
1005
  (#1064) instead of process.env — repeatable.
@@ -946,7 +1011,7 @@ Options:
946
1011
  chant.template.json declares instead (#2627).
947
1012
  --params-file <path> (build, graph, run --components, components fan-out,
948
1013
  components promote, components rollback,
949
- components redeploy) JSON file
1014
+ components redeploy, release cut) JSON file
950
1015
  of { "name": value } build-time parameter values
951
1016
  (#1064). Second precedence, after --param.
952
1017
  init --from refuses it: pass --param instead.
@@ -1195,10 +1260,18 @@ export const commandRegistry: CommandDef[] = [
1195
1260
  { name: "carve status", handler: runCarveStatus },
1196
1261
  // #3573 — which commits passed CI, kept as ci/green and ci/revoked tags (ws-103). Imported on first use.
1197
1262
  { name: "ci last-green", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiLastGreen(ctx) },
1263
+ { name: "ci list", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiList(ctx) },
1198
1264
  { name: "ci tick", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiTick(ctx) },
1199
1265
  { name: "ci workflow", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiWorkflow(ctx) },
1266
+ // #4026: the release cut from a green commit (ws-104). Imported on first use.
1267
+ { name: "release cut", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCut(ctx) },
1268
+ { name: "release list", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseList(ctx) },
1269
+ { name: "release export", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseExport(ctx) },
1270
+ { name: "release compile", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCompile(ctx) },
1200
1271
  // #3188 — the grouped plan summary of a change-set document. Reads one file; imported on first use.
1201
1272
  { name: "change-set summary", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/change-set")).runChangeSetSummary(ctx) },
1273
+ // #4022: the change set a gate bound, read from chant/lifecycle by its digest.
1274
+ { name: "change-set show", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/change-set")).runChangeSetShow(ctx) },
1202
1275
  { name: "init", handler: runInit, runsNoConfig: true },
1203
1276
  { name: "init lexicon", handler: runInitLexicon },
1204
1277
  { name: "update", handler: runUpdate },
@@ -1220,6 +1293,7 @@ export const commandRegistry: CommandDef[] = [
1220
1293
  { name: "run signal", handler: runOpSignalRenamed },
1221
1294
  { name: "run cancel", handler: runOpCancel },
1222
1295
  { name: "run log", handler: runOpLog },
1296
+ { name: "run events", handler: runOpEvents },
1223
1297
  { name: "run wave", handler: runOpWaveCommand },
1224
1298
  // #3683 — start the CI jobs that wait at gates whose approval arrived. Imported on first use.
1225
1299
  { name: "run resume", handler: async (ctx) => (await import("./handlers/gate-resume")).runResumeCommand(ctx) },
@@ -1335,6 +1409,7 @@ export const commandRegistry: CommandDef[] = [
1335
1409
  { name: "dev", handler: runDevUnknown },
1336
1410
  { name: "serve", handler: runServeUnknown },
1337
1411
  { name: "components", handler: runComponentsUnknown },
1412
+ { name: "release", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseUnknown(ctx) },
1338
1413
  { name: "workspace", handler: async (ctx) => (await import("../workspace/records-cli")).runWorkspaceUnknown(ctx) },
1339
1414
  ];
1340
1415
 
@@ -217,6 +217,8 @@ describe("neverOverMcp (chant#3447)", () => {
217
217
  const gateOnlyOps: Array<{ op: string; gate: string }> = [
218
218
  { op: "workspace-upgrade", gate: "." },
219
219
  { op: "pr-12", gate: "pr-apply" },
220
+ // #4045: a release gate approves a change to an environment, as a pull request's does.
221
+ { op: "release", gate: "prod-release" },
220
222
  ];
221
223
 
222
224
  for (const { op, gate } of gateOnlyOps) {
@@ -126,7 +126,7 @@ export const workspaceReadTools: ToolDefinition[] = [
126
126
  {
127
127
  name: "workspace-changes",
128
128
  description:
129
- "The forward coverage check over a diff: chant workspace check --changes <range> --json (changes.schema.json, #2773). Each changed path with the current records whose constrains cover it, and a change-uncovered or change-out-of-scope finding for each gap, at the severity the declaration's changes block sets. Returns the document unchanged.",
129
+ "The forward coverage check over a diff: chant workspace check --changes <range> --json (changes.schema.json, #2773). Each changed path with the current records whose constrains cover it, and a change-uncovered or change-out-of-scope finding for each gap, at the severity the declaration's changes block sets. links says whether each trunk commit of the range names its work item, with commit-unlinked and commit-wrongly-linked findings at the same severity. Returns the document unchanged.",
130
130
  inputSchema: {
131
131
  type: "object",
132
132
  properties: {
@@ -48,6 +48,8 @@ const BOOLEAN_FLAGS = new Set([
48
48
  "--local",
49
49
  "--json",
50
50
  "--progress-json",
51
+ "--events-json",
52
+ "--follow",
51
53
  "--update-snapshot",
52
54
  "--update-baseline",
53
55
  "--run-examples",
@@ -71,11 +73,13 @@ const BOOLEAN_FLAGS = new Set([
71
73
  "--current",
72
74
  "--uncommitted",
73
75
  "--allow-code",
76
+ "--allow-skip",
74
77
  "--root-only",
75
78
  "--generated",
76
79
  "--decide",
77
80
  "--record-plans",
78
81
  "--loopback",
82
+ "--artifacts",
79
83
  ]);
80
84
 
81
85
  /**
@@ -191,8 +195,9 @@ export function parseArgs(args: string[]): ParsedArgs {
191
195
  result.overlayAnchor = v;
192
196
  } else if (arg === "--from") {
193
197
  // Shared by `migrate --from <lexicon>`, `import --from <env>`,
194
- // `components promote --from <env>` and `init --from <repo>@<ref>`;
195
- // the commands never run together, so one field carries all four.
198
+ // `components promote --from <env>`, `init --from <repo>@<ref>` and
199
+ // `ci last-green --from <rev>` (#4031); the commands never run
200
+ // together, so one field carries all five.
196
201
  result.migrateFrom = args[++i];
197
202
  } else if (arg === "--kustomize") {
198
203
  // `chant import --kustomize <dir>` (#1548): render the overlay, import
@@ -372,6 +377,10 @@ export function parseArgs(args: string[]): ParsedArgs {
372
377
  result.requireReview = true;
373
378
  } else if (arg === "--pr-loop") {
374
379
  result.prLoop = true;
380
+ } else if (arg === "--before-script") {
381
+ const line = args[++i];
382
+ if (line === undefined || line === "") throw new Error("--before-script needs a shell line: --before-script '<line>'");
383
+ (result.beforeScript ??= []).push(line);
375
384
  } else if (arg === "--resume") {
376
385
  // `chant approve <op> <gate> --resume` (#3683) takes no value; the
377
386
  // fan-out and pull-request apply take the attempt file they resume.
@@ -384,6 +393,11 @@ export function parseArgs(args: string[]): ParsedArgs {
384
393
  result.json = true;
385
394
  } else if (arg === "--progress-json") {
386
395
  result.progressJson = true;
396
+ } else if (arg === "--events-json") {
397
+ result.eventsJson = true;
398
+ } else if (arg === "--follow") {
399
+ // `chant run events <run> --follow` (#4042): tail a run in progress on this host.
400
+ result.follow = true;
387
401
  } else if (arg === "--gated-exit") {
388
402
  // `chant run <op> --gated-exit <code>` (#2243) — remap only the gated
389
403
  // outcome's exit code. Parsed as a number here and range-checked in the
@@ -502,6 +516,10 @@ export function parseArgs(args: string[]): ParsedArgs {
502
516
  } else if (arg === "--app-key-secret") {
503
517
  result.ciAppKeySecret = args[++i];
504
518
  if (!result.ciAppKeySecret || result.ciAppKeySecret.startsWith("-")) throw new Error("--app-key-secret needs the secret holding the App's private key: --app-key-secret <NAME>");
519
+ } else if (arg === "--runner-key-secret") {
520
+ // `chant ci workflow --runner-key-secret <NAME>` (#4031): the secret holding the runner key the tick signs its tags with.
521
+ result.ciRunnerKeySecret = args[++i];
522
+ if (!result.ciRunnerKeySecret || result.ciRunnerKeySecret.startsWith("-")) throw new Error("--runner-key-secret needs the repository secret holding the runner key, such as CHANT_RUNNER_KEY: --runner-key-secret <NAME>");
505
523
  } else if (arg === "--verdict") {
506
524
  // `chant workspace records review <id> --verdict agree|dissent|abstain` (#2670)
507
525
  result.verdict = args[++i];
@@ -588,6 +606,17 @@ export function parseArgs(args: string[]): ParsedArgs {
588
606
  if (result.digest !== undefined) (result.digests ??= []).push(result.digest);
589
607
  } else if (arg === "--release-plan") {
590
608
  result.releasePlanFile = args[++i];
609
+ } else if (arg === "--release") {
610
+ // `chant components promote --release <label|digest>` (#4026)
611
+ result.release = args[++i];
612
+ if (!result.release || result.release.startsWith("-")) throw new Error("--release needs a release's label or digest: --release <label|digest>");
613
+ } else if (arg === "--build") {
614
+ // `chant release cut --build <command>` (#4026)
615
+ result.buildCommand = args[++i];
616
+ if (!result.buildCommand) throw new Error("--build needs a shell command: --build '<command>'");
617
+ } else if (arg === "--artifacts") {
618
+ // `chant release compile --artifacts` (#4026, internal)
619
+ result.artifacts = true;
591
620
  } else if (arg === "--git-sha") {
592
621
  result.gitSha = args[++i];
593
622
  } else if (arg === "--run-id") {
@@ -712,10 +741,15 @@ export function parseArgs(args: string[]): ParsedArgs {
712
741
  // #2550 — `chant workspace upgrade` runs a template's code migrations
713
742
  // only when asked to.
714
743
  result.allowCode = true;
744
+ } else if (arg === "--allow-skip") {
745
+ // #4030 — `chant components promote` skips a step of the member's
746
+ // declared promotion order only when asked to.
747
+ result.allowSkip = true;
715
748
  } else if (arg === "--source") {
716
749
  // #2551 — `chant workspace upgrade --source <repo>[#<member>]`.
717
750
  result.source = args[++i];
718
- if (!result.source || result.source.startsWith("-")) throw new Error("--source needs a template: --source <repo>[#<member>]");
751
+ // #4044: also `chant ci tick|list --source checks|runs`.
752
+ if (!result.source || result.source.startsWith("-")) throw new Error("--source needs a template: --source <repo>[#<member>] for workspace upgrade, or a source for ci tick and ci list: --source checks|runs");
719
753
  } else if (arg === "--tags") {
720
754
  // #2551 — `chant workspace adopt-lineage --tags <glob>`, also hash-index and versions.
721
755
  result.tags = args[++i];