@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
@@ -8,10 +8,19 @@
8
8
  */
9
9
 
10
10
  import { copyFileSync, existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, rmSync } from "fs";
11
- import { join } from "path";
11
+ import { dirname, join } from "path";
12
12
  import { fileURLToPath } from "url";
13
13
 
14
14
  import { expandFileMarkers } from "./docs-file-markers";
15
+ import {
16
+ DOCS_SITE_DEPENDENCIES,
17
+ buildSectionGroup,
18
+ hiddenSectionSlugs,
19
+ rebaseImports,
20
+ resolveDocsMode,
21
+ sectionContentDir,
22
+ withQuadrant,
23
+ } from "./docs-in-site";
15
24
  import { readAuthoredPages } from "./docs-pages";
16
25
  import { scanRules, generateRules } from "./docs-rule-scanning";
17
26
  import { buildSidebar } from "./docs-sidebar";
@@ -22,6 +31,14 @@ import type { DocsConfig, DocsResult, ManifestJSON, MetaEntry, SidebarPage } fro
22
31
  export { expandFileMarkers } from "./docs-file-markers";
23
32
  export type { DocsConfig, DocsResult, Quadrant, SidebarPage } from "./docs-types";
24
33
  export { QUADRANTS, QUADRANT_LABELS, readAuthoredPages } from "./docs-pages";
34
+ export {
35
+ DOCS_SITE_DEPENDENCIES,
36
+ buildSectionGroup,
37
+ findChantDocsSite,
38
+ resolveDocsMode,
39
+ sectionContentDir,
40
+ type DocsSiteMode,
41
+ } from "./docs-in-site";
25
42
 
26
43
  // ── Pipeline ───────────────────────────────────────────────────────
27
44
 
@@ -279,6 +296,46 @@ export function writeDocsPages(result: DocsResult, outDir: string): void {
279
296
  }
280
297
  }
281
298
 
299
+ /**
300
+ * Write a lexicon's docs from pipeline results, in the mode
301
+ * {@link resolveDocsMode} picks: inside the chant repository, a section of the
302
+ * main site ({@link writeDocsSection}); anywhere else, a standalone Starlight
303
+ * app ({@link writeStandaloneDocsSite}). Every lexicon's `docs.ts` calls this,
304
+ * so neither they nor the `chant init-lexicon` scaffold choose a mode.
305
+ */
306
+ export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
307
+ const { mode, siteDir } = resolveDocsMode(config);
308
+ if (mode === "in-site" && siteDir) writeDocsSection(config, result, siteDir);
309
+ else writeStandaloneDocsSite(config, result);
310
+ }
311
+
312
+ /**
313
+ * Write a lexicon's pages into the main docs site (chant #3993): every page
314
+ * under `docs/src/content/docs/lexicons/<name>/`, and `_sidebar.json` beside
315
+ * them with the lexicon's sidebar group and its hidden pages. The section is
316
+ * build output, so the directory is replaced whole on every run; a page the
317
+ * pipeline stopped emitting cannot linger. No app files are written.
318
+ */
319
+ export function writeDocsSection(config: DocsConfig, result: DocsResult, siteDir: string): void {
320
+ const sectionDir = sectionContentDir(siteDir, config.name);
321
+ rmSync(sectionDir, { recursive: true, force: true });
322
+ mkdirSync(sectionDir, { recursive: true });
323
+
324
+ const quadrantOf = new Map(result.sidebarPages.map((p) => [`${p.slug}.mdx`, p.quadrant]));
325
+ for (const [filename, content] of result.pages) {
326
+ const filePath = join(sectionDir, filename);
327
+ const pageDir = dirname(filePath);
328
+ mkdirSync(pageDir, { recursive: true });
329
+ const page = rebaseImports(withQuadrant(content, quadrantOf.get(filename)), { outDir: config.outDir, siteDir, pageDir });
330
+ writeFileSync(filePath, page);
331
+ }
332
+
333
+ writeFileSync(
334
+ join(sectionDir, "_sidebar.json"),
335
+ JSON.stringify({ group: buildSectionGroup(config, result), hidden: hiddenSectionSlugs(config, result) }, null, 2) + "\n",
336
+ );
337
+ }
338
+
282
339
  /**
283
340
  * Scaffold a standalone Starlight docs site from pipeline results.
284
341
  *
@@ -286,7 +343,7 @@ export function writeDocsPages(result: DocsResult, outDir: string): void {
286
343
  * pages under src/content/docs/. The resulting directory can be built with
287
344
  * `npm install && npm run build`.
288
345
  */
289
- export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
346
+ export function writeStandaloneDocsSite(config: DocsConfig, result: DocsResult): void {
290
347
  const outDir = config.outDir;
291
348
  const contentDir = join(outDir, "src", "content", "docs");
292
349
 
@@ -350,11 +407,7 @@ export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
350
407
  build: "astro build",
351
408
  preview: "astro preview",
352
409
  },
353
- dependencies: {
354
- "@astrojs/starlight": "^0.37.6",
355
- astro: "^5.6.1",
356
- sharp: "^0.34.2",
357
- },
410
+ dependencies: { ...DOCS_SITE_DEPENDENCIES },
358
411
  },
359
412
  null,
360
413
  2,
@@ -3,6 +3,8 @@ export interface RehypeBaseUrlOptions {
3
3
  base: string;
4
4
  /** Project-wide base used to detect already-correctly-prefixed cross-site links. */
5
5
  projectBase?: string;
6
+ /** Per-page base, by the page's source path; undefined falls back to `base`. */
7
+ baseFor?: (path: string) => string | undefined;
6
8
  }
7
9
 
8
10
  type HastNode = {
@@ -14,4 +16,4 @@ type HastNode = {
14
16
 
15
17
  export default function rehypeBaseUrl(
16
18
  opts: RehypeBaseUrlOptions,
17
- ): (tree: HastNode) => void;
19
+ ): (tree: HastNode, file?: { path?: string }) => void;
@@ -11,9 +11,16 @@
11
11
  * idempotently skips hrefs that already start with the site's own base or the
12
12
  * project-wide base.
13
13
  *
14
+ * `baseFor` lets one site hold sections with their own root (chant #3993): the
15
+ * main docs site builds every lexicon's pages under /chant/lexicons/<name>/,
16
+ * and a lexicon page's `/foo/` has always meant its own lexicon's root, as it
17
+ * did when each lexicon was a separate site. `baseFor(file.path)` returns the
18
+ * base for that page, or undefined to use `base`.
19
+ *
14
20
  * @typedef {Object} RehypeBaseUrlOptions
15
21
  * @property {string} base - Site base, e.g. "/chant" or "/chant/lexicons/aws". Trailing/leading slashes optional.
16
22
  * @property {string} [projectBase] - Project-wide base used to detect already-correctly-prefixed cross-site links.
23
+ * @property {(path: string) => string | undefined} [baseFor] - Per-page base, by the page's source path.
17
24
  */
18
25
 
19
26
  const PROTOCOL_RE = /^[a-z][a-z0-9+.-]*:/i;
@@ -26,16 +33,12 @@ function normalizeBase(value) {
26
33
  * @param {RehypeBaseUrlOptions} opts
27
34
  */
28
35
  export default function rehypeBaseUrl(opts) {
29
- const base = normalizeBase(opts.base);
30
- if (base === "/") {
31
- return () => {};
32
- }
33
- const ownPrefix = base + "/";
36
+ const siteBase = normalizeBase(opts.base);
34
37
  const projectPrefix = opts.projectBase
35
38
  ? normalizeBase(opts.projectBase) + "/"
36
39
  : null;
37
40
 
38
- function rewrite(node) {
41
+ function rewrite(node, base, ownPrefix) {
39
42
  if (
40
43
  node &&
41
44
  node.type === "element" &&
@@ -58,11 +61,14 @@ export default function rehypeBaseUrl(opts) {
58
61
  }
59
62
  }
60
63
  if (node && node.children) {
61
- for (const child of node.children) rewrite(child);
64
+ for (const child of node.children) rewrite(child, base, ownPrefix);
62
65
  }
63
66
  }
64
67
 
65
- return (tree) => {
66
- rewrite(tree);
68
+ return (tree, file) => {
69
+ const own = opts.baseFor && file && typeof file.path === "string" ? opts.baseFor(file.path) : undefined;
70
+ const base = own ? normalizeBase(own) : siteBase;
71
+ if (base === "/") return;
72
+ rewrite(tree, base, base + "/");
67
73
  };
68
74
  }
@@ -159,3 +159,37 @@ describe("rehypeBaseUrl — tree traversal", () => {
159
159
  expect(root.children[0].properties.src).toBe("/foo.png");
160
160
  });
161
161
  });
162
+
163
+ // chant #3993: the main site builds every lexicon's pages under
164
+ // /chant/lexicons/<name>/, and a lexicon page's root-relative link keeps
165
+ // meaning its own lexicon's root, as it did on the lexicon's own site.
166
+ describe("rehypeBaseUrl — per-page base (baseFor)", () => {
167
+ const baseFor = (path: string): string | undefined => {
168
+ const name = path.match(/\/content\/docs\/lexicons\/([^/]+)\//)?.[1];
169
+ return name ? `/chant/lexicons/${name}` : undefined;
170
+ };
171
+ const opts = { base: "/chant", projectBase: "/chant", baseFor };
172
+
173
+ function runAt(path: string | undefined, href: string): string {
174
+ const plugin = rehypeBaseUrl(opts);
175
+ const root = tree(a(href));
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
+ (plugin as any)(root, path === undefined ? undefined : { path });
178
+ return root.children[0].properties.href as string;
179
+ }
180
+
181
+ it("prefixes a lexicon page's root-relative link with its lexicon's base", () => {
182
+ expect(runAt("/repo/docs/src/content/docs/lexicons/aws/composites.mdx", "/intrinsics/")).toBe(
183
+ "/chant/lexicons/aws/intrinsics/",
184
+ );
185
+ });
186
+
187
+ it("leaves a lexicon page's /chant/ link alone", () => {
188
+ expect(runAt("/repo/docs/src/content/docs/lexicons/aws/composites.mdx", "/chant/guide/ops/")).toBe("/chant/guide/ops/");
189
+ });
190
+
191
+ it("uses the site base for every other page", () => {
192
+ expect(runAt("/repo/docs/src/content/docs/guide/ops.mdx", "/lexicons/aws/")).toBe("/chant/lexicons/aws/");
193
+ expect(runAt(undefined, "/lexicons/aws/")).toBe("/chant/lexicons/aws/");
194
+ });
195
+ });
@@ -95,6 +95,27 @@ describe("maybeRecordAutoRelease", () => {
95
95
  expect(pushLifecycleMock).toHaveBeenCalledTimes(1);
96
96
  });
97
97
 
98
+ test("a deploy that passed a gate records the approver and the admission (#4020)", async () => {
99
+ appendReleaseRecordMock.mockResolvedValue({ commit: "c".repeat(40), record: { version: 1 } });
100
+ const admission = { op: "svc", gate: "approve-prod", resolvedAt: "2026-01-01T00:05:00.000Z" };
101
+
102
+ await maybeRecordAutoRelease(
103
+ { component: "svc", env: "prod", success: true, records: [publishRecord("sha256:abc")], runId: "9001", runOrigin: { forge: "github", repo: "acme/app" }, approver: "alice", admission },
104
+ { actor: "alice" },
105
+ );
106
+
107
+ const [input] = appendReleaseRecordMock.mock.calls[0];
108
+ expect(input).toMatchObject({ runId: "9001", runOrigin: { forge: "github", repo: "acme/app" }, approver: "alice", admission });
109
+ });
110
+
111
+ test("an ungated deploy records neither", async () => {
112
+ appendReleaseRecordMock.mockResolvedValue({ commit: "c".repeat(40), record: { version: 1 } });
113
+ await maybeRecordAutoRelease({ component: "svc", env: "prod", success: true, records: [publishRecord("sha256:abc")], runId: "r" }, { actor: "alice" });
114
+ const [input] = appendReleaseRecordMock.mock.calls[0];
115
+ expect(input).not.toHaveProperty("approver");
116
+ expect(input).not.toHaveProperty("admission");
117
+ });
118
+
98
119
  test("a pre-resolved digest takes precedence and is used directly", async () => {
99
120
  appendReleaseRecordMock.mockResolvedValue({
100
121
  commit: "b".repeat(40),
@@ -26,7 +26,7 @@
26
26
  * env vars, timestamp is a real `new Date()` taken at record time).
27
27
  */
28
28
  import { getHeadCommit, pushLifecycle, StaleLifecycleBranchError } from "../lifecycle/git";
29
- import { appendReleaseRecord, InvalidReleaseRecordError, type ReleaseRecord, type RunOrigin } from "../lifecycle/release-ledger";
29
+ import { appendReleaseRecord, InvalidReleaseRecordError, type ReleaseAdmission, type ReleaseRecord, type RunOrigin } from "../lifecycle/release-ledger";
30
30
  import type { DriverStepRecord } from "./driver";
31
31
 
32
32
  /**
@@ -94,6 +94,9 @@ export interface AutoReleaseRunInfo {
94
94
  runId: string;
95
95
  /** The id space `runId` lives in (#2045) — `{ forge: "op" }` for a hosted run, `{ forge: "local" }` for a local-executor mint. Recorded verbatim as the release record's `runOrigin`. */
96
96
  runOrigin?: RunOrigin;
97
+ /** Who approved the deploy at its gate (#1035) and the gate resolution that admitted it (#4020), when it passed one. `recordAutoReleasesForRun` reads both off the component's gate step records. */
98
+ approver?: string;
99
+ admission?: ReleaseAdmission;
97
100
  /** The bypassed capability-profile divergences, when the caller deliberately overrode a deploy-time profile assertion (chant #1244) — recorded verbatim as the release record's `profileOverride`. */
98
101
  profileOverride?: string;
99
102
  /** The deploy's input-side digest, when `digest` is a rendered-content identity (a pinned helm deploy, chant #1242) — recorded as the release record's `inputDigest` so ledger queries can still join on inputs across clusters whose bytes legitimately differ. */
@@ -184,6 +187,8 @@ export async function maybeRecordAutoRelease(
184
187
  ...(run.runOrigin ? { runOrigin: run.runOrigin } : {}),
185
188
  timestamp,
186
189
  actor,
190
+ ...(run.approver ? { approver: run.approver } : {}),
191
+ ...(run.admission ? { admission: run.admission } : {}),
187
192
  ...(run.profileOverride ? { profileOverride: run.profileOverride } : {}),
188
193
  ...(run.inputDigest ? { inputDigest: run.inputDigest } : {}),
189
194
  },
@@ -27,6 +27,8 @@ import {
27
27
  import { CapabilityRegistry, type DeployContext } from "./capability";
28
28
  import { memoryGateLedgerPort } from "../op/gate";
29
29
  import type { DriverComponent, RunProgressEvent } from "./driver";
30
+ import { openRunEventStream } from "../run-event-stream";
31
+ import { checkRunEventLines, stepTrail } from "../workspace/__fixtures__/run-event-check";
30
32
 
31
33
  // A minimal stand-in for the real gitlab lexicon plugin, satisfying
32
34
  // `isLexiconPlugin` (../lexicon.ts) — used only by the `generateComponentsPipeline`
@@ -1106,3 +1108,83 @@ describe("runComponents — onProgress (--progress-json wiring, M3)", () => {
1106
1108
  expect(result.run?.ok).toBe(true);
1107
1109
  });
1108
1110
  });
1111
+
1112
+ describe("runComponents — onRunEvent (`chant run --components --events-json`, #4041)", () => {
1113
+ let testDir: string;
1114
+
1115
+ beforeEach(async () => {
1116
+ testDir = join(tmpdir(), `chant-run-components-events-test-${Date.now()}-${Math.random()}`);
1117
+ await mkdir(testDir, { recursive: true });
1118
+ });
1119
+
1120
+ afterEach(async () => {
1121
+ await rm(testDir, { recursive: true, force: true });
1122
+ });
1123
+
1124
+ /** The run's stream, as `--events-json` opens it. */
1125
+ function stream() {
1126
+ const lines: string[] = [];
1127
+ const s = openRunEventStream(
1128
+ { runId: "local-1", kind: "component", name: "all", gitSha: null, runtime: "local", runOrigin: { forge: "local" } },
1129
+ (l) => lines.push(l),
1130
+ );
1131
+ return { s, lines };
1132
+ }
1133
+
1134
+ test("a two-wave run: schema-valid, waves and components framed, each step started before it settles", async () => {
1135
+ await writeFile(
1136
+ join(testDir, "alb.component.ts"),
1137
+ `export const alb = { name: "shared-alb", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "deploy-thing" }] }] };`,
1138
+ );
1139
+ await writeFile(
1140
+ join(testDir, "svc.component.ts"),
1141
+ `export const svc = { name: "search-service", dependsOn: ["shared-alb"], deploy: [{ phase: "Apply", steps: [{ kind: "deploy-thing" }] }] };`,
1142
+ );
1143
+ const { s, lines } = stream();
1144
+ const result = await runComponents(testDir, "all", { registry: fakeRegistry(), onRunEvent: s.emit });
1145
+
1146
+ expect(result.success).toBe(true);
1147
+ const events = checkRunEventLines(lines);
1148
+ expect(events[0]).toMatchObject({ type: "run-start", waves: [["shared-alb"], ["search-service"]] });
1149
+ expect(events.filter((e) => e.type === "step-start")).toEqual([
1150
+ expect.objectContaining({ component: "shared-alb", phase: "Apply", step: "deploy-thing", index: 0 }),
1151
+ expect.objectContaining({ component: "search-service", phase: "Apply", step: "deploy-thing", index: 0 }),
1152
+ ]);
1153
+ expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "ok" });
1154
+ });
1155
+
1156
+ test("a gate nobody approved: gate-waiting and a gated settle, the step after it not-run, the run gated", async () => {
1157
+ await writeFile(
1158
+ join(testDir, "svc.component.ts"),
1159
+ `export const svc = { name: "svc", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "gate", gate: "prod-ok" }, { kind: "deploy-thing" }] }] };`,
1160
+ );
1161
+ const { s, lines } = stream();
1162
+ const result = await runComponents(testDir, "svc", { registry: fakeRegistry(), gates: memoryGateLedgerPort(), onRunEvent: s.emit });
1163
+
1164
+ expect(result.gated).toBeDefined();
1165
+ const events = checkRunEventLines(lines);
1166
+ expect(stepTrail(events)).toEqual([
1167
+ ["step-start", "gate:prod-ok"],
1168
+ ["gate-waiting", "gate:prod-ok"],
1169
+ ["step-settle", "gate:prod-ok", "gated"],
1170
+ ["step-settle", "deploy-thing", "skipped:not-run"],
1171
+ ]);
1172
+ expect(events.find((e) => e.type === "gate-waiting")).toMatchObject({ component: "svc", op: "svc", gate: "prod-ok", point: null });
1173
+ expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "gated" });
1174
+ });
1175
+
1176
+ test("onProgress beside onRunEvent prints what it printed alone: the older stream is unchanged", async () => {
1177
+ await writeFile(
1178
+ join(testDir, "svc.component.ts"),
1179
+ `export const svc = { name: "svc", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "gate", gate: "prod-ok" }, { kind: "deploy-thing" }] }] };`,
1180
+ );
1181
+ const alone: RunProgressEvent[] = [];
1182
+ await runComponents(testDir, "svc", { registry: fakeRegistry({ failRun: true }), gates: memoryGateLedgerPort(), onProgress: (e) => alone.push(e) });
1183
+ const beside: RunProgressEvent[] = [];
1184
+ const { s } = stream();
1185
+ await runComponents(testDir, "svc", { registry: fakeRegistry({ failRun: true }), gates: memoryGateLedgerPort(), onProgress: (e) => beside.push(e), onRunEvent: s.emit });
1186
+ expect(beside).toEqual(alone);
1187
+ // The gate step, its decision and the not-run step after it are not in the older stream.
1188
+ expect(alone.map((e) => e.type)).toEqual(["run-start", "wave-start", "component-start", "phase-start", "phase-done", "component-done", "wave-done", "run-done"]);
1189
+ });
1190
+ });
@@ -45,6 +45,7 @@ import type { GateDigestMismatch, GateLedgerPort } from "../op/gate";
45
45
  import { gateName } from "../op/gate-name";
46
46
  import { isLexiconPlugin, type LexiconPlugin, type ComponentPipelineOptions } from "../lexicon";
47
47
  import type { RunProgressEvent } from "./run-progress";
48
+ import { joinRunEventSinks, progressSinkOf, type RunEventSink } from "../run-event-stream";
48
49
  import { dirname, relative } from "node:path";
49
50
  import { existsSync, statSync } from "node:fs";
50
51
  import { getHeadCommit } from "../lifecycle/git";
@@ -437,6 +438,11 @@ export interface RunComponentsOptions {
437
438
  * `--progress-json`, in which case nothing changes.
438
439
  */
439
440
  onProgress?: (event: RunProgressEvent) => void;
441
+ /**
442
+ * Where the run's events go (#4041, `--events-json`): run-event bodies,
443
+ * with the same single-wave framing for a single component as `onProgress`.
444
+ */
445
+ onRunEvent?: RunEventSink;
440
446
  /**
441
447
  * chant #1108 — this run's resolved build-time parameter values, resolved
442
448
  * the exact same way `chant build` resolves them
@@ -681,7 +687,7 @@ export async function runComponents(
681
687
  // from a caller (a downstream CI job passing an upstream job's dumped
682
688
  // outputs via `--seed-outputs`); grows as this run's components complete.
683
689
  const seedOutputs = options.componentOutputs ?? {};
684
- const { onProgress } = options;
690
+ const emit = joinRunEventSinks(options.onRunEvent, progressSinkOf(options.onProgress));
685
691
 
686
692
  const gates = options.gates;
687
693
  // #3061: the commit this run deploys, for the workloads' vcs.ref.head.revision. The digest comes
@@ -705,7 +711,7 @@ export async function runComponents(
705
711
  env,
706
712
  releaseIdentity,
707
713
  componentOutputs: seedOutputs,
708
- onProgress,
714
+ ...(emit ? { onRunEvent: emit } : {}),
709
715
  ...(gates ? { gates } : {}),
710
716
  });
711
717
  return {
@@ -741,21 +747,22 @@ export async function runComponents(
741
747
  // `run.waves`/`run.order` below already give it.
742
748
  const componentOutputs = { ...seedOutputs };
743
749
  const componentName = resolvedTargets[0].name;
744
- onProgress?.({ type: "run-start", waves: [selected] });
745
- onProgress?.({ type: "wave-start", wave: 1, components: selected });
746
- onProgress?.({ type: "component-start", wave: 1, component: componentName });
750
+ emit?.({ type: "run-start", waves: [selected] });
751
+ emit?.({ type: "wave-start", wave: 1, components: selected });
752
+ emit?.({ type: "component-start", wave: 1, component: componentName });
747
753
  const componentResult = await runComponentDeploy(
748
754
  resolvedTargets[0],
749
755
  deployContext({ env, releaseIdentity }, componentName),
750
756
  registry,
751
757
  componentOutputs,
752
- onProgress,
758
+ undefined,
753
759
  gates ? { port: gates } : undefined,
760
+ emit,
754
761
  );
755
762
  const status = componentResult.status === "fail" ? "failed" : componentResult.status;
756
- onProgress?.({ type: "component-done", wave: 1, component: componentName, status });
757
- onProgress?.({ type: "wave-done", wave: 1, status });
758
- onProgress?.({ type: "run-done", status });
763
+ emit?.({ type: "component-settle", wave: 1, component: componentName, status });
764
+ emit?.({ type: "wave-settle", wave: 1, status });
765
+ emit?.({ type: "run-settle", status, durationMs: null });
759
766
  const run: DriverRunResult = {
760
767
  order: selected,
761
768
  waves: [selected],