@intentius/chant 0.124.27 → 0.124.29

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 (681) 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/build-params.d.ts +34 -1
  20. package/dist/build-params.d.ts.map +1 -1
  21. package/dist/build-params.js +31 -1
  22. package/dist/change-set-graph.d.ts +63 -0
  23. package/dist/change-set-graph.d.ts.map +1 -0
  24. package/dist/change-set-graph.js +130 -0
  25. package/dist/change-set.d.ts +62 -3
  26. package/dist/change-set.d.ts.map +1 -1
  27. package/dist/change-set.js +36 -4
  28. package/dist/chant-version.d.ts +9 -0
  29. package/dist/chant-version.d.ts.map +1 -0
  30. package/dist/chant-version.js +16 -0
  31. package/dist/cli/build-params-cli.d.ts +7 -1
  32. package/dist/cli/build-params-cli.d.ts.map +1 -1
  33. package/dist/cli/build-params-cli.js +17 -8
  34. package/dist/cli/commands/build.d.ts +8 -0
  35. package/dist/cli/commands/build.d.ts.map +1 -1
  36. package/dist/cli/commands/build.js +15 -11
  37. package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
  38. package/dist/cli/commands/check-lexicon-docs.js +30 -2
  39. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  40. package/dist/cli/commands/check-lexicon.js +13 -2
  41. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
  42. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
  43. package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
  44. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  45. package/dist/cli/commands/init-lexicon.js +17 -8
  46. package/dist/cli/handlers/approve.d.ts.map +1 -1
  47. package/dist/cli/handlers/approve.js +64 -4
  48. package/dist/cli/handlers/build.d.ts.map +1 -1
  49. package/dist/cli/handlers/build.js +43 -4
  50. package/dist/cli/handlers/change-set.d.ts +10 -0
  51. package/dist/cli/handlers/change-set.d.ts.map +1 -1
  52. package/dist/cli/handlers/change-set.js +51 -1
  53. package/dist/cli/handlers/fan-out.d.ts +10 -1
  54. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  55. package/dist/cli/handlers/fan-out.js +54 -6
  56. package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
  57. package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
  58. package/dist/cli/handlers/lifecycle-gc.js +143 -0
  59. package/dist/cli/handlers/pr.d.ts +16 -2
  60. package/dist/cli/handlers/pr.d.ts.map +1 -1
  61. package/dist/cli/handlers/pr.js +269 -37
  62. package/dist/cli/handlers/promote.d.ts +7 -0
  63. package/dist/cli/handlers/promote.d.ts.map +1 -1
  64. package/dist/cli/handlers/promote.js +440 -28
  65. package/dist/cli/handlers/release.d.ts +91 -0
  66. package/dist/cli/handlers/release.d.ts.map +1 -0
  67. package/dist/cli/handlers/release.js +589 -0
  68. package/dist/cli/handlers/run.d.ts +38 -2
  69. package/dist/cli/handlers/run.d.ts.map +1 -1
  70. package/dist/cli/handlers/run.js +234 -16
  71. package/dist/cli/main.d.ts.map +1 -1
  72. package/dist/cli/main.js +143 -17
  73. package/dist/cli/mcp/workspace-tools.js +1 -1
  74. package/dist/cli/parse-args.d.ts.map +1 -1
  75. package/dist/cli/parse-args.js +73 -5
  76. package/dist/cli/registry.d.ts +43 -5
  77. package/dist/cli/registry.d.ts.map +1 -1
  78. package/dist/cli/run-events.d.ts +30 -0
  79. package/dist/cli/run-events.d.ts.map +1 -0
  80. package/dist/cli/run-events.js +49 -0
  81. package/dist/cli/version.d.ts +3 -5
  82. package/dist/cli/version.d.ts.map +1 -1
  83. package/dist/cli/version.js +3 -13
  84. package/dist/codegen/docs-in-site.d.ts +71 -0
  85. package/dist/codegen/docs-in-site.d.ts.map +1 -0
  86. package/dist/codegen/docs-in-site.js +177 -0
  87. package/dist/codegen/docs-types.d.ts +14 -1
  88. package/dist/codegen/docs-types.d.ts.map +1 -1
  89. package/dist/codegen/docs.d.ts +18 -1
  90. package/dist/codegen/docs.d.ts.map +1 -1
  91. package/dist/codegen/docs.js +40 -7
  92. package/dist/codegen/rehype-base-url.mjs +15 -9
  93. package/dist/components/auto-release.d.ts +6 -1
  94. package/dist/components/auto-release.d.ts.map +1 -1
  95. package/dist/components/auto-release.js +3 -0
  96. package/dist/components/capability.d.ts +9 -0
  97. package/dist/components/capability.d.ts.map +1 -1
  98. package/dist/components/cli-support.d.ts +6 -0
  99. package/dist/components/cli-support.d.ts.map +1 -1
  100. package/dist/components/cli-support.js +10 -9
  101. package/dist/components/driver.d.ts +50 -9
  102. package/dist/components/driver.d.ts.map +1 -1
  103. package/dist/components/driver.js +116 -54
  104. package/dist/components/fan-out-inputs.d.ts +69 -0
  105. package/dist/components/fan-out-inputs.d.ts.map +1 -0
  106. package/dist/components/fan-out-inputs.js +169 -0
  107. package/dist/components/fan-out-record.d.ts +13 -0
  108. package/dist/components/fan-out-record.d.ts.map +1 -1
  109. package/dist/components/fan-out-record.js +16 -0
  110. package/dist/components/fan-out-run.d.ts +7 -0
  111. package/dist/components/fan-out-run.d.ts.map +1 -1
  112. package/dist/components/fan-out-run.js +85 -26
  113. package/dist/components/fan-out.d.ts +4 -0
  114. package/dist/components/fan-out.d.ts.map +1 -1
  115. package/dist/components/pr-pipeline.d.ts +25 -0
  116. package/dist/components/pr-pipeline.d.ts.map +1 -1
  117. package/dist/components/pr-pipeline.js +22 -4
  118. package/dist/components/pr-run.d.ts +11 -0
  119. package/dist/components/pr-run.d.ts.map +1 -1
  120. package/dist/components/pr-run.js +20 -1
  121. package/dist/components/promote-order.d.ts +38 -0
  122. package/dist/components/promote-order.d.ts.map +1 -0
  123. package/dist/components/promote-order.js +62 -0
  124. package/dist/components/promote.d.ts +38 -2
  125. package/dist/components/promote.d.ts.map +1 -1
  126. package/dist/components/promote.js +77 -2
  127. package/dist/components/release-compile.d.ts +142 -0
  128. package/dist/components/release-compile.d.ts.map +1 -0
  129. package/dist/components/release-compile.js +382 -0
  130. package/dist/components/release-gate.d.ts +135 -0
  131. package/dist/components/release-gate.d.ts.map +1 -0
  132. package/dist/components/release-gate.js +288 -0
  133. package/dist/components/release-promote.d.ts +96 -0
  134. package/dist/components/release-promote.d.ts.map +1 -0
  135. package/dist/components/release-promote.js +204 -0
  136. package/dist/components/run-progress.d.ts +7 -0
  137. package/dist/components/run-progress.d.ts.map +1 -1
  138. package/dist/components/wave-plan.d.ts +8 -1
  139. package/dist/components/wave-plan.d.ts.map +1 -1
  140. package/dist/components/wave-plan.js +11 -3
  141. package/dist/config.d.ts +45 -0
  142. package/dist/config.d.ts.map +1 -1
  143. package/dist/config.js +3 -0
  144. package/dist/flag-params.d.ts +54 -0
  145. package/dist/flag-params.d.ts.map +1 -0
  146. package/dist/flag-params.js +130 -0
  147. package/dist/index.d.ts +1 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -0
  150. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  151. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  152. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  153. package/dist/lifecycle/change-set-store.d.ts +68 -0
  154. package/dist/lifecycle/change-set-store.d.ts.map +1 -0
  155. package/dist/lifecycle/change-set-store.js +97 -0
  156. package/dist/lifecycle/commit-sha.d.ts +13 -0
  157. package/dist/lifecycle/commit-sha.d.ts.map +1 -0
  158. package/dist/lifecycle/commit-sha.js +21 -0
  159. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  160. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  161. package/dist/lifecycle/delivery-metrics.js +145 -0
  162. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  163. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  164. package/dist/lifecycle/deploy-outcome.js +101 -0
  165. package/dist/lifecycle/gate-change-set.d.ts +78 -0
  166. package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
  167. package/dist/lifecycle/gate-change-set.js +138 -0
  168. package/dist/lifecycle/gate-ledger.d.ts +51 -4
  169. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  170. package/dist/lifecycle/gate-ledger.js +18 -0
  171. package/dist/lifecycle/git.d.ts +30 -1
  172. package/dist/lifecycle/git.d.ts.map +1 -1
  173. package/dist/lifecycle/git.js +31 -5
  174. package/dist/lifecycle/index.d.ts +4 -0
  175. package/dist/lifecycle/index.d.ts.map +1 -1
  176. package/dist/lifecycle/index.js +4 -0
  177. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  178. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  179. package/dist/lifecycle/mixed-environment.js +154 -0
  180. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  181. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  182. package/dist/lifecycle/publish-attachments.js +179 -0
  183. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  184. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  185. package/dist/lifecycle/publish-credentials.js +69 -0
  186. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  187. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  188. package/dist/lifecycle/publish-declaration.js +83 -0
  189. package/dist/lifecycle/publish-forge.d.ts +53 -0
  190. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  191. package/dist/lifecycle/publish-forge.js +298 -0
  192. package/dist/lifecycle/publish-npm.d.ts +61 -0
  193. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  194. package/dist/lifecycle/publish-npm.js +242 -0
  195. package/dist/lifecycle/publish-oci.d.ts +64 -0
  196. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  197. package/dist/lifecycle/publish-oci.js +383 -0
  198. package/dist/lifecycle/publish-record.d.ts +66 -0
  199. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  200. package/dist/lifecycle/publish-record.js +84 -0
  201. package/dist/lifecycle/publish-targets.d.ts +45 -0
  202. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  203. package/dist/lifecycle/publish-targets.js +85 -0
  204. package/dist/lifecycle/release-cut.d.ts +154 -0
  205. package/dist/lifecycle/release-cut.d.ts.map +1 -0
  206. package/dist/lifecycle/release-cut.js +366 -0
  207. package/dist/lifecycle/release-join.d.ts +95 -0
  208. package/dist/lifecycle/release-join.d.ts.map +1 -0
  209. package/dist/lifecycle/release-join.js +139 -0
  210. package/dist/lifecycle/release-ledger.d.ts +54 -0
  211. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  212. package/dist/lifecycle/release-ledger.js +53 -20
  213. package/dist/lifecycle/release-publish.d.ts +312 -0
  214. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  215. package/dist/lifecycle/release-publish.js +335 -0
  216. package/dist/lifecycle/release-revocation.d.ts +80 -0
  217. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  218. package/dist/lifecycle/release-revocation.js +123 -0
  219. package/dist/lifecycle/release-store.d.ts +200 -0
  220. package/dist/lifecycle/release-store.d.ts.map +1 -0
  221. package/dist/lifecycle/release-store.js +320 -0
  222. package/dist/lifecycle/release-verify.d.ts +61 -0
  223. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  224. package/dist/lifecycle/release-verify.js +92 -0
  225. package/dist/lifecycle/release-workstation.d.ts +91 -0
  226. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  227. package/dist/lifecycle/release-workstation.js +180 -0
  228. package/dist/lifecycle/retention-policy.d.ts +45 -0
  229. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  230. package/dist/lifecycle/retention-policy.js +43 -0
  231. package/dist/lifecycle/retention.d.ts +185 -0
  232. package/dist/lifecycle/retention.d.ts.map +1 -0
  233. package/dist/lifecycle/retention.js +400 -0
  234. package/dist/lifecycle/run-ledger.d.ts +4 -0
  235. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  236. package/dist/lifecycle/run-ledger.js +2 -0
  237. package/dist/op/activities/change-coverage.d.ts.map +1 -1
  238. package/dist/op/activities/change-coverage.js +4 -2
  239. package/dist/op/activities/source-release.d.ts.map +1 -1
  240. package/dist/op/activities/source-release.js +7 -3
  241. package/dist/op/activities/source-rollback.d.ts.map +1 -1
  242. package/dist/op/activities/source-rollback.js +7 -3
  243. package/dist/op/gate-name.d.ts +11 -2
  244. package/dist/op/gate-name.d.ts.map +1 -1
  245. package/dist/op/gate-name.js +12 -3
  246. package/dist/op/gate-run-locator.d.ts +22 -0
  247. package/dist/op/gate-run-locator.d.ts.map +1 -1
  248. package/dist/op/gate-run-locator.js +31 -0
  249. package/dist/op/gate.d.ts +27 -3
  250. package/dist/op/gate.d.ts.map +1 -1
  251. package/dist/op/gate.js +57 -3
  252. package/dist/op/local-executor.d.ts +36 -1
  253. package/dist/op/local-executor.d.ts.map +1 -1
  254. package/dist/op/local-executor.js +158 -52
  255. package/dist/op/op-waves-run.d.ts.map +1 -1
  256. package/dist/op/op-waves-run.js +4 -0
  257. package/dist/op/run-live.d.ts +2 -0
  258. package/dist/op/run-live.d.ts.map +1 -1
  259. package/dist/op/run-live.js +1 -1
  260. package/dist/op/runtime.d.ts +46 -0
  261. package/dist/op/runtime.d.ts.map +1 -1
  262. package/dist/op/runtimes/local.d.ts.map +1 -1
  263. package/dist/op/runtimes/local.js +8 -0
  264. package/dist/plan-summary.d.ts.map +1 -1
  265. package/dist/plan-summary.js +8 -2
  266. package/dist/point-decision.d.ts +2 -0
  267. package/dist/point-decision.d.ts.map +1 -1
  268. package/dist/point-decision.js +9 -2
  269. package/dist/pr-loop.d.ts +7 -0
  270. package/dist/pr-loop.d.ts.map +1 -1
  271. package/dist/pr-loop.js +23 -4
  272. package/dist/pr-report-url.d.ts +82 -0
  273. package/dist/pr-report-url.d.ts.map +1 -0
  274. package/dist/pr-report-url.js +174 -0
  275. package/dist/provenance.d.ts +2 -2
  276. package/dist/provenance.d.ts.map +1 -1
  277. package/dist/run-event-store.d.ts +104 -0
  278. package/dist/run-event-store.d.ts.map +1 -0
  279. package/dist/run-event-store.js +251 -0
  280. package/dist/run-event-stream.d.ts +133 -0
  281. package/dist/run-event-stream.d.ts.map +1 -0
  282. package/dist/run-event-stream.js +242 -0
  283. package/dist/run-event.d.ts +285 -0
  284. package/dist/run-event.d.ts.map +1 -0
  285. package/dist/run-event.js +209 -0
  286. package/dist/wave-runtime.mjs +262 -226
  287. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  288. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  289. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  290. package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
  291. package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
  292. package/dist/workspace/__fixtures__/run-event-check.js +59 -0
  293. package/dist/workspace/change-set-show.d.ts +56 -0
  294. package/dist/workspace/change-set-show.d.ts.map +1 -0
  295. package/dist/workspace/change-set-show.js +54 -0
  296. package/dist/workspace/change-set-show.schema.json +102 -0
  297. package/dist/workspace/change-set.schema.json +32 -1
  298. package/dist/workspace/changes-cli.d.ts.map +1 -1
  299. package/dist/workspace/changes-cli.js +10 -1
  300. package/dist/workspace/changes.d.ts +37 -0
  301. package/dist/workspace/changes.d.ts.map +1 -1
  302. package/dist/workspace/changes.js +49 -2
  303. package/dist/workspace/changes.schema.json +135 -0
  304. package/dist/workspace/check.schema.json +4 -2
  305. package/dist/workspace/checks/records.d.ts +9 -1
  306. package/dist/workspace/checks/records.d.ts.map +1 -1
  307. package/dist/workspace/checks/records.js +35 -1
  308. package/dist/workspace/checks.d.ts +1 -0
  309. package/dist/workspace/checks.d.ts.map +1 -1
  310. package/dist/workspace/checks.js +2 -1
  311. package/dist/workspace/ci-green-cli.d.ts +19 -4
  312. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  313. package/dist/workspace/ci-green-cli.js +75 -12
  314. package/dist/workspace/ci-green-forge.d.ts +20 -3
  315. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  316. package/dist/workspace/ci-green-forge.js +6 -4
  317. package/dist/workspace/ci-green-workflow.d.ts +11 -0
  318. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  319. package/dist/workspace/ci-green-workflow.js +6 -0
  320. package/dist/workspace/ci-green.d.ts +281 -13
  321. package/dist/workspace/ci-green.d.ts.map +1 -1
  322. package/dist/workspace/ci-green.js +456 -38
  323. package/dist/workspace/ci-last-green.schema.json +32 -2
  324. package/dist/workspace/ci-list.schema.json +525 -0
  325. package/dist/workspace/commit-links.d.ts +85 -0
  326. package/dist/workspace/commit-links.d.ts.map +1 -0
  327. package/dist/workspace/commit-links.js +163 -0
  328. package/dist/workspace/declaration.d.ts +35 -0
  329. package/dist/workspace/declaration.d.ts.map +1 -1
  330. package/dist/workspace/declaration.js +27 -1
  331. package/dist/workspace/declaration.schema.json +76 -0
  332. package/dist/workspace/declared-kinds.d.ts +16 -0
  333. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  334. package/dist/workspace/declared-kinds.js +25 -0
  335. package/dist/workspace/delivery-metrics.d.ts +91 -0
  336. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  337. package/dist/workspace/delivery-metrics.js +217 -0
  338. package/dist/workspace/delivery-metrics.schema.json +519 -0
  339. package/dist/workspace/flags-read.d.ts +37 -0
  340. package/dist/workspace/flags-read.d.ts.map +1 -0
  341. package/dist/workspace/flags-read.js +103 -0
  342. package/dist/workspace/flags.d.ts +100 -0
  343. package/dist/workspace/flags.d.ts.map +1 -0
  344. package/dist/workspace/flags.js +98 -0
  345. package/dist/workspace/graph-cli.js +1 -1
  346. package/dist/workspace/identity-gate-rule.d.ts +37 -0
  347. package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
  348. package/dist/workspace/identity-gate-rule.js +72 -7
  349. package/dist/workspace/identity.d.ts +12 -0
  350. package/dist/workspace/identity.d.ts.map +1 -1
  351. package/dist/workspace/identity.js +13 -1
  352. package/dist/workspace/intent-cli.d.ts +11 -2
  353. package/dist/workspace/intent-cli.d.ts.map +1 -1
  354. package/dist/workspace/intent-cli.js +54 -5
  355. package/dist/workspace/intent-trace.d.ts +219 -0
  356. package/dist/workspace/intent-trace.d.ts.map +1 -0
  357. package/dist/workspace/intent-trace.js +314 -0
  358. package/dist/workspace/intent.d.ts +36 -7
  359. package/dist/workspace/intent.d.ts.map +1 -1
  360. package/dist/workspace/intent.js +192 -22
  361. package/dist/workspace/intent.schema.json +702 -6
  362. package/dist/workspace/ls.d.ts +4 -0
  363. package/dist/workspace/ls.d.ts.map +1 -1
  364. package/dist/workspace/ls.js +2 -0
  365. package/dist/workspace/ls.schema.json +3 -1
  366. package/dist/workspace/member-pipeline.d.ts +6 -0
  367. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  368. package/dist/workspace/member-pipeline.js +6 -0
  369. package/dist/workspace/overlap.d.ts +146 -0
  370. package/dist/workspace/overlap.d.ts.map +1 -0
  371. package/dist/workspace/overlap.js +276 -0
  372. package/dist/workspace/promotion-order.d.ts +41 -0
  373. package/dist/workspace/promotion-order.d.ts.map +1 -0
  374. package/dist/workspace/promotion-order.js +95 -0
  375. package/dist/workspace/reason-codes.d.ts +4 -0
  376. package/dist/workspace/reason-codes.d.ts.map +1 -1
  377. package/dist/workspace/reason-codes.js +7 -1
  378. package/dist/workspace/records.d.ts +15 -2
  379. package/dist/workspace/records.d.ts.map +1 -1
  380. package/dist/workspace/records.js +32 -0
  381. package/dist/workspace/records.schema.json +21 -2
  382. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  383. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  384. package/dist/workspace/releases.d.ts +81 -0
  385. package/dist/workspace/releases.d.ts.map +1 -0
  386. package/dist/workspace/releases.js +103 -0
  387. package/dist/workspace/releases.schema.json +513 -0
  388. package/dist/workspace/run-event.schema.json +295 -0
  389. package/dist/workspace/status-gates.d.ts +34 -1
  390. package/dist/workspace/status-gates.d.ts.map +1 -1
  391. package/dist/workspace/status-gates.js +31 -2
  392. package/dist/workspace/status-stewards.d.ts +4 -0
  393. package/dist/workspace/status-stewards.d.ts.map +1 -1
  394. package/dist/workspace/status-stewards.js +2 -0
  395. package/dist/workspace/status.d.ts +117 -0
  396. package/dist/workspace/status.d.ts.map +1 -1
  397. package/dist/workspace/status.js +218 -4
  398. package/dist/workspace/status.schema.json +268 -2
  399. package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
  400. package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
  401. package/dist/workspace/trust/ci-tag-statement.js +123 -0
  402. package/dist/workspace/trust/op-run-statement.d.ts +91 -0
  403. package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
  404. package/dist/workspace/trust/op-run-statement.js +143 -0
  405. package/dist/workspace/trust/rotation.d.ts +18 -2
  406. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  407. package/dist/workspace/trust/rotation.js +65 -16
  408. package/dist/workspace/trust/seal.d.ts +11 -0
  409. package/dist/workspace/trust/seal.d.ts.map +1 -1
  410. package/dist/workspace/trust/seal.js +9 -0
  411. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  412. package/dist/workspace/trust/ssh-commit.js +10 -10
  413. package/dist/workspace/wip.d.ts.map +1 -1
  414. package/dist/workspace/wip.js +11 -3
  415. package/package.json +1 -1
  416. package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
  417. package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
  418. package/src/__golden__/wave-loop/fan-out-only.json +6 -0
  419. package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  420. package/src/__golden__/wave-loop/op-cost-member.json +3 -0
  421. package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  422. package/src/__golden__/wave-loop/op-held-on.json +2 -0
  423. package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
  424. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  425. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  426. package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
  427. package/src/__golden__/wave-loop/op-per-run.json +7 -0
  428. package/src/__golden__/wave-loop/op-pr-review.json +7 -0
  429. package/src/__golden__/wave-loop/op-sealed.json +3 -0
  430. package/src/__golden__/wave-loop/op-spend.json +3 -0
  431. package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
  432. package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
  433. package/src/build-params.test.ts +46 -0
  434. package/src/build-params.ts +55 -1
  435. package/src/change-set-graph.test.ts +72 -0
  436. package/src/change-set-graph.ts +141 -0
  437. package/src/change-set.ts +87 -6
  438. package/src/chant-version.ts +16 -0
  439. package/src/cli/build-params-cli.ts +22 -9
  440. package/src/cli/commands/build.ts +25 -11
  441. package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
  442. package/src/cli/commands/check-lexicon-docs.ts +39 -2
  443. package/src/cli/commands/check-lexicon.ts +14 -2
  444. package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
  445. package/src/cli/commands/init-lexicon.test.ts +25 -0
  446. package/src/cli/commands/init-lexicon.ts +18 -8
  447. package/src/cli/handlers/approve-release.test.ts +84 -0
  448. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  449. package/src/cli/handlers/approve.ts +65 -2
  450. package/src/cli/handlers/build.test.ts +67 -0
  451. package/src/cli/handlers/build.ts +46 -4
  452. package/src/cli/handlers/change-set.ts +49 -1
  453. package/src/cli/handlers/fan-out.test.ts +228 -1
  454. package/src/cli/handlers/fan-out.ts +53 -6
  455. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  456. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  457. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  458. package/src/cli/handlers/operator.test.ts +37 -0
  459. package/src/cli/handlers/pr.test.ts +341 -0
  460. package/src/cli/handlers/pr.ts +292 -47
  461. package/src/cli/handlers/promote.test.ts +669 -7
  462. package/src/cli/handlers/promote.ts +464 -29
  463. package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
  464. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  465. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  466. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  467. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  468. package/src/cli/handlers/release.e2e.test.ts +199 -0
  469. package/src/cli/handlers/release.ts +593 -0
  470. package/src/cli/handlers/run-auto-release.test.ts +80 -0
  471. package/src/cli/handlers/run-events-cmd.test.ts +92 -0
  472. package/src/cli/handlers/run-release.test.ts +55 -0
  473. package/src/cli/handlers/run.test.ts +30 -0
  474. package/src/cli/handlers/run.ts +243 -18
  475. package/src/cli/main.test.ts +8 -0
  476. package/src/cli/main.ts +143 -17
  477. package/src/cli/mcp/never-over-mcp.test.ts +2 -0
  478. package/src/cli/mcp/workspace-tools.ts +1 -1
  479. package/src/cli/parse-args.ts +56 -5
  480. package/src/cli/registry.ts +43 -5
  481. package/src/cli/run-events.ts +57 -0
  482. package/src/cli/version.ts +3 -13
  483. package/src/codegen/docs-in-site.test.ts +166 -0
  484. package/src/codegen/docs-in-site.ts +182 -0
  485. package/src/codegen/docs-site/AgentPrompt.astro +26 -1
  486. package/src/codegen/docs-types.ts +14 -1
  487. package/src/codegen/docs.ts +60 -7
  488. package/src/codegen/rehype-base-url.d.mts +3 -1
  489. package/src/codegen/rehype-base-url.mjs +15 -9
  490. package/src/codegen/rehype-base-url.test.ts +34 -0
  491. package/src/components/auto-release.test.ts +30 -0
  492. package/src/components/auto-release.ts +9 -1
  493. package/src/components/capability.ts +9 -0
  494. package/src/components/cli-support.test.ts +82 -0
  495. package/src/components/cli-support.ts +16 -9
  496. package/src/components/driver.test.ts +24 -0
  497. package/src/components/driver.ts +165 -56
  498. package/src/components/fan-out-inputs.test.ts +85 -0
  499. package/src/components/fan-out-inputs.ts +192 -0
  500. package/src/components/fan-out-record.test.ts +10 -0
  501. package/src/components/fan-out-record.ts +19 -0
  502. package/src/components/fan-out-run.ts +104 -35
  503. package/src/components/fan-out.ts +4 -0
  504. package/src/components/pr-pipeline.ts +35 -4
  505. package/src/components/pr-run.ts +30 -1
  506. package/src/components/promote-order.test.ts +90 -0
  507. package/src/components/promote-order.ts +75 -0
  508. package/src/components/promote.test.ts +10 -1
  509. package/src/components/promote.ts +94 -3
  510. package/src/components/release-compile.test.ts +147 -0
  511. package/src/components/release-compile.ts +459 -0
  512. package/src/components/release-gate.test.ts +215 -0
  513. package/src/components/release-gate.ts +355 -0
  514. package/src/components/release-promote.test.ts +159 -0
  515. package/src/components/release-promote.ts +232 -0
  516. package/src/components/run-progress.ts +7 -0
  517. package/src/components/wave-plan.ts +16 -3
  518. package/src/config.ts +15 -1
  519. package/src/flag-params.test.ts +144 -0
  520. package/src/flag-params.ts +134 -0
  521. package/src/index.ts +2 -0
  522. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  523. package/src/lifecycle/change-set-store.test.ts +217 -0
  524. package/src/lifecycle/change-set-store.ts +113 -0
  525. package/src/lifecycle/commit-sha.ts +23 -0
  526. package/src/lifecycle/delivery-metrics.ts +263 -0
  527. package/src/lifecycle/deploy-outcome.ts +147 -0
  528. package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
  529. package/src/lifecycle/gate-change-set.ts +164 -0
  530. package/src/lifecycle/gate-ledger.test.ts +33 -0
  531. package/src/lifecycle/gate-ledger.ts +68 -3
  532. package/src/lifecycle/git.ts +61 -8
  533. package/src/lifecycle/index.ts +4 -0
  534. package/src/lifecycle/mixed-environment.test.ts +97 -0
  535. package/src/lifecycle/mixed-environment.ts +204 -0
  536. package/src/lifecycle/publish-attachments.ts +196 -0
  537. package/src/lifecycle/publish-credentials.ts +84 -0
  538. package/src/lifecycle/publish-declaration.ts +95 -0
  539. package/src/lifecycle/publish-forge.ts +325 -0
  540. package/src/lifecycle/publish-npm.ts +259 -0
  541. package/src/lifecycle/publish-oci.ts +396 -0
  542. package/src/lifecycle/publish-record.ts +113 -0
  543. package/src/lifecycle/publish-resume.test.ts +168 -0
  544. package/src/lifecycle/publish-targets.test.ts +494 -0
  545. package/src/lifecycle/publish-targets.ts +100 -0
  546. package/src/lifecycle/release-cut-signed.test.ts +180 -0
  547. package/src/lifecycle/release-cut.test.ts +483 -0
  548. package/src/lifecycle/release-cut.ts +503 -0
  549. package/src/lifecycle/release-flag.test.ts +112 -0
  550. package/src/lifecycle/release-join.test.ts +68 -0
  551. package/src/lifecycle/release-join.ts +182 -0
  552. package/src/lifecycle/release-ledger.test.ts +14 -0
  553. package/src/lifecycle/release-ledger.ts +85 -17
  554. package/src/lifecycle/release-publish.test.ts +225 -0
  555. package/src/lifecycle/release-publish.ts +568 -0
  556. package/src/lifecycle/release-revocation.ts +147 -0
  557. package/src/lifecycle/release-store.ts +434 -0
  558. package/src/lifecycle/release-verify.test.ts +110 -0
  559. package/src/lifecycle/release-verify.ts +120 -0
  560. package/src/lifecycle/release-workstation.test.ts +204 -0
  561. package/src/lifecycle/release-workstation.ts +216 -0
  562. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  563. package/src/lifecycle/retention-policy.ts +63 -0
  564. package/src/lifecycle/retention.test.ts +363 -0
  565. package/src/lifecycle/retention.ts +508 -0
  566. package/src/lifecycle/run-ledger.test.ts +10 -0
  567. package/src/lifecycle/run-ledger.ts +6 -0
  568. package/src/op/activities/change-coverage.ts +4 -1
  569. package/src/op/activities/source-release.ts +8 -3
  570. package/src/op/activities/source-rollback.ts +8 -4
  571. package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
  572. package/src/op/gate-name.ts +13 -3
  573. package/src/op/gate-release.test.ts +126 -0
  574. package/src/op/gate-run-locator.test.ts +110 -0
  575. package/src/op/gate-run-locator.ts +35 -0
  576. package/src/op/gate.ts +88 -6
  577. package/src/op/local-executor.ts +207 -52
  578. package/src/op/op-waves-run.ts +4 -0
  579. package/src/op/run-events.test.ts +166 -0
  580. package/src/op/run-live.ts +1 -1
  581. package/src/op/runtime.ts +39 -0
  582. package/src/op/runtimes/local.test.ts +16 -0
  583. package/src/op/runtimes/local.ts +9 -0
  584. package/src/plan-summary.test.ts +20 -0
  585. package/src/plan-summary.ts +8 -1
  586. package/src/point-decision.ts +12 -2
  587. package/src/pr-loop.ts +25 -4
  588. package/src/pr-report-url.test.ts +131 -0
  589. package/src/pr-report-url.ts +209 -0
  590. package/src/provenance.ts +2 -2
  591. package/src/run-event-store.test.ts +144 -0
  592. package/src/run-event-store.ts +273 -0
  593. package/src/run-event-stream.test.ts +175 -0
  594. package/src/run-event-stream.ts +309 -0
  595. package/src/run-event.test.ts +277 -0
  596. package/src/run-event.ts +471 -0
  597. package/src/wave-loop-golden.test.ts +9 -0
  598. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  599. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  600. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  601. package/src/workspace/__fixtures__/run-event-check.ts +58 -0
  602. package/src/workspace/ancestry-index.test.ts +93 -0
  603. package/src/workspace/change-set-show.schema.json +102 -0
  604. package/src/workspace/change-set-show.ts +72 -0
  605. package/src/workspace/change-set.schema.json +32 -1
  606. package/src/workspace/changes-cli.ts +10 -1
  607. package/src/workspace/changes.schema.json +135 -0
  608. package/src/workspace/changes.test.ts +1 -1
  609. package/src/workspace/changes.ts +84 -2
  610. package/src/workspace/check.schema.json +4 -2
  611. package/src/workspace/checks/records.ts +35 -1
  612. package/src/workspace/checks.ts +2 -1
  613. package/src/workspace/ci-green-cli.ts +75 -13
  614. package/src/workspace/ci-green-forge.ts +24 -4
  615. package/src/workspace/ci-green-runs.test.ts +242 -0
  616. package/src/workspace/ci-green-signed.test.ts +302 -0
  617. package/src/workspace/ci-green-workflow.test.ts +16 -0
  618. package/src/workspace/ci-green-workflow.ts +12 -0
  619. package/src/workspace/ci-green.test.ts +131 -2
  620. package/src/workspace/ci-green.ts +635 -42
  621. package/src/workspace/ci-last-green.schema.json +32 -2
  622. package/src/workspace/ci-list.schema.json +525 -0
  623. package/src/workspace/commit-links.test.ts +279 -0
  624. package/src/workspace/commit-links.ts +183 -0
  625. package/src/workspace/declaration.schema.json +76 -0
  626. package/src/workspace/declaration.test.ts +28 -0
  627. package/src/workspace/declaration.ts +60 -2
  628. package/src/workspace/declared-kinds.ts +40 -1
  629. package/src/workspace/delivery-metrics.schema.json +519 -0
  630. package/src/workspace/delivery-metrics.test.ts +236 -0
  631. package/src/workspace/delivery-metrics.ts +286 -0
  632. package/src/workspace/flags-read.ts +121 -0
  633. package/src/workspace/flags.test.ts +141 -0
  634. package/src/workspace/flags.ts +157 -0
  635. package/src/workspace/graph-cli.ts +1 -1
  636. package/src/workspace/identity-gate-rule.ts +91 -7
  637. package/src/workspace/identity.ts +14 -1
  638. package/src/workspace/intent-cli.ts +46 -5
  639. package/src/workspace/intent-trace.test.ts +173 -0
  640. package/src/workspace/intent-trace.ts +484 -0
  641. package/src/workspace/intent.schema.json +702 -6
  642. package/src/workspace/intent.ts +196 -26
  643. package/src/workspace/ls.schema.json +3 -1
  644. package/src/workspace/ls.ts +6 -0
  645. package/src/workspace/member-pipeline.test.ts +17 -0
  646. package/src/workspace/member-pipeline.ts +9 -0
  647. package/src/workspace/older-records.test.ts +412 -0
  648. package/src/workspace/overlap.test.ts +165 -0
  649. package/src/workspace/overlap.ts +340 -0
  650. package/src/workspace/promotion-order.ts +97 -0
  651. package/src/workspace/read-contract.e2e.test.ts +5 -1
  652. package/src/workspace/reason-codes.test.ts +7 -1
  653. package/src/workspace/reason-codes.ts +7 -1
  654. package/src/workspace/record-assets.test.ts +2 -1
  655. package/src/workspace/records-contract.e2e.test.ts +2 -1
  656. package/src/workspace/records.schema.json +21 -2
  657. package/src/workspace/records.ts +36 -2
  658. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  659. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  660. package/src/workspace/releases.schema.json +513 -0
  661. package/src/workspace/releases.test.ts +108 -0
  662. package/src/workspace/releases.ts +133 -0
  663. package/src/workspace/run-event.schema.json +295 -0
  664. package/src/workspace/status-contract.test.ts +81 -1
  665. package/src/workspace/status-gates.ts +64 -1
  666. package/src/workspace/status-mixed.test.ts +152 -0
  667. package/src/workspace/status-revoked.test.ts +153 -0
  668. package/src/workspace/status-stewards.ts +6 -0
  669. package/src/workspace/status.schema.json +268 -2
  670. package/src/workspace/status.ts +334 -4
  671. package/src/workspace/trust/ci-tag-statement.ts +151 -0
  672. package/src/workspace/trust/op-run-statement.ts +148 -0
  673. package/src/workspace/trust/rotation.ts +61 -14
  674. package/src/workspace/trust/seal.ts +10 -0
  675. package/src/workspace/trust/ssh-commit.ts +10 -9
  676. package/src/workspace/wip.ts +11 -2
  677. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
  678. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
  679. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
  680. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
  681. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
@@ -0,0 +1,80 @@
1
+ /**
2
+ * What `recordAutoReleasesForRun` hands the release record (#4020): the run
3
+ * and its origin, and for a component that passed a gate, who approved it and
4
+ * which resolution admitted it.
5
+ */
6
+ import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
7
+
8
+ const maybeRecordAutoReleaseMock = vi.fn();
9
+ vi.mock("../../components/auto-release", async () => {
10
+ const actual = await vi.importActual<typeof import("../../components/auto-release")>("../../components/auto-release");
11
+ return { ...actual, maybeRecordAutoRelease: (...args: unknown[]) => maybeRecordAutoReleaseMock(...args) };
12
+ });
13
+ vi.mock("../../components/manifest-persistence", async () => {
14
+ const actual = await vi.importActual<typeof import("../../components/manifest-persistence")>("../../components/manifest-persistence");
15
+ return { ...actual, maybePersistBuildManifest: async () => ({ persisted: false, reason: "no-manifest" }) };
16
+ });
17
+
18
+ import { autoReleaseRun, recordAutoReleasesForRun } from "./run";
19
+ import type { DriverComponentResult } from "../../components/driver";
20
+
21
+ const gatedResult = {
22
+ component: "api",
23
+ ok: true,
24
+ status: "ok",
25
+ records: [
26
+ { component: "api", phase: "Deploy", kind: "gate:approve-prod", status: "ok", durationMs: 1, approval: { gate: "approve-prod", resolvedBy: "alice", timestamp: "2026-10-16T10:05:00.000Z" } },
27
+ { component: "api", phase: "Deploy", kind: "apply", status: "ok", durationMs: 1 },
28
+ ],
29
+ } as unknown as DriverComponentResult;
30
+ const plainResult = { component: "worker", ok: true, status: "ok", records: [] } as unknown as DriverComponentResult;
31
+
32
+ beforeEach(() => {
33
+ maybeRecordAutoReleaseMock.mockReset().mockResolvedValue({ recorded: false, reason: "no-digest" });
34
+ });
35
+ afterEach(() => vi.unstubAllEnvs());
36
+
37
+ describe("recordAutoReleasesForRun", () => {
38
+ test("a component that passed a gate records its approver and admission, one that did not records neither", async () => {
39
+ await recordAutoReleasesForRun([gatedResult, plainResult], "prod", "9001", false, { forge: "github", repo: "acme/app" });
40
+ const [gated, plain] = maybeRecordAutoReleaseMock.mock.calls.map((c) => c[0]);
41
+ expect(gated).toMatchObject({
42
+ component: "api", runId: "9001", runOrigin: { forge: "github", repo: "acme/app" },
43
+ approver: "alice", admission: { op: "api", gate: "approve-prod", resolvedAt: "2026-10-16T10:05:00.000Z" },
44
+ });
45
+ expect(plain).not.toHaveProperty("approver");
46
+ expect(plain).not.toHaveProperty("admission");
47
+ });
48
+
49
+ test("a run under --release records the release digest on each component, one without it records none (#4043)", async () => {
50
+ const release = `jcs1-sha256:${"c".repeat(64)}`;
51
+ await recordAutoReleasesForRun([plainResult], "dev", "9001", false, { forge: "local" }, release);
52
+ expect(maybeRecordAutoReleaseMock.mock.calls[0][0]).toMatchObject({ releaseDigest: release });
53
+ maybeRecordAutoReleaseMock.mockClear();
54
+ await recordAutoReleasesForRun([plainResult], "dev", "9001", false, { forge: "local" });
55
+ expect(maybeRecordAutoReleaseMock.mock.calls[0][0]).not.toHaveProperty("releaseDigest");
56
+ });
57
+
58
+ test("the origin defaults to local", async () => {
59
+ await recordAutoReleasesForRun([plainResult], "prod", "local-1", false);
60
+ expect(maybeRecordAutoReleaseMock.mock.calls[0][0]).toMatchObject({ runId: "local-1", runOrigin: { forge: "local" } });
61
+ });
62
+ });
63
+
64
+ describe("autoReleaseRun", () => {
65
+ test("in CI it names the CI run, so the release and the gate it passed name one run", () => {
66
+ vi.stubEnv("GITHUB_RUN_ID", "9001");
67
+ vi.stubEnv("GITHUB_REPOSITORY", "acme/app");
68
+ vi.stubEnv("GITHUB_SERVER_URL", "https://github.example");
69
+ expect(autoReleaseRun("local-1")).toEqual({
70
+ runId: "9001",
71
+ runOrigin: { forge: "github", repo: "acme/app", url: "https://github.example/acme/app/actions/runs/9001" },
72
+ });
73
+ });
74
+
75
+ test("outside CI it keeps the local id", () => {
76
+ vi.stubEnv("GITHUB_RUN_ID", "");
77
+ vi.stubEnv("CI_PIPELINE_ID", "");
78
+ expect(autoReleaseRun("local-1")).toEqual({ runId: "local-1", runOrigin: { forge: "local" } });
79
+ });
80
+ });
@@ -0,0 +1,92 @@
1
+ /** #4042: `chant run events <run> [--json] [--follow]`. */
2
+
3
+ import { execFileSync } from "node:child_process";
4
+ import { mkdtempSync, realpathSync, rmSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
8
+ import type { ParsedArgs } from "../registry";
9
+ import { runOpEvents } from "./run";
10
+ import { openRunEventStore } from "../../run-event-store";
11
+ import { openRunEventStream, runEventRunOf } from "../../run-event-stream";
12
+
13
+ function args(o: Partial<ParsedArgs>): ParsedArgs {
14
+ return { command: "run", path: ".", format: "", fix: false, watch: false, verbose: false, help: false, live: false, ...o };
15
+ }
16
+
17
+ let dir: string;
18
+ let cwd: string;
19
+ let out: string[];
20
+ beforeEach(() => {
21
+ cwd = process.cwd();
22
+ vi.stubEnv("CHANT_RUN_EVENTS_DIR", "");
23
+ dir = realpathSync(mkdtempSync(join(tmpdir(), "run-events-cmd-")));
24
+ execFileSync("git", ["init", "-q", "-b", "main"], { cwd: dir });
25
+ execFileSync("git", ["-c", "user.email=t@t", "-c", "user.name=t", "commit", "-q", "--allow-empty", "-m", "init"], { cwd: dir });
26
+ process.chdir(dir);
27
+ out = [];
28
+ vi.spyOn(process.stdout, "write").mockImplementation((s: string | Uint8Array) => (out.push(String(s)), true));
29
+ vi.spyOn(console, "log").mockImplementation((s: string) => void out.push(`${s}\n`));
30
+ vi.spyOn(console, "error").mockImplementation(() => {});
31
+ });
32
+ afterEach(() => {
33
+ vi.restoreAllMocks();
34
+ vi.unstubAllEnvs();
35
+ process.chdir(cwd);
36
+ rmSync(dir, { recursive: true, force: true });
37
+ });
38
+
39
+ async function stream(runId: string, name = "deploy-api") {
40
+ const store = (await openRunEventStore(dir, runId))!;
41
+ return openRunEventStream(runEventRunOf({ kind: "op", name, runId }), store.write);
42
+ }
43
+
44
+ describe("chant run events", () => {
45
+ test("--json replays a settled run's lines and exits with its verdict", async () => {
46
+ const s = await stream("r-done");
47
+ s.emit({ type: "run-start", waves: null });
48
+ s.settle("failed", "boom");
49
+ const exit = await runOpEvents({ args: args({ extraPositional: "r-done", json: true }), plugins: [], serializers: [] });
50
+ expect(exit).toBe(1);
51
+ const events = out.join("").trim().split("\n").map((l) => JSON.parse(l));
52
+ expect(events.map((e) => e.type)).toEqual(["run-start", "run-settle"]);
53
+ expect(events[1]).toMatchObject({ status: "failed", error: "boom" });
54
+ });
55
+
56
+ test("--json replays a run in progress up to now and exits 0", async () => {
57
+ const s = await stream("r-live");
58
+ s.emit({ type: "run-start", waves: null });
59
+ s.emit({ type: "phase-start", component: null, phase: "Build" });
60
+ const exit = await runOpEvents({ args: args({ extraPositional: "deploy-api", json: true }), plugins: [], serializers: [] });
61
+ expect(exit).toBe(0);
62
+ expect(out.join("").trim().split("\n").map((l) => JSON.parse(l).type)).toEqual(["run-start", "phase-start"]);
63
+ });
64
+
65
+ test("--follow prints each event of a run in progress, then the verdict", async () => {
66
+ const s = await stream("r-follow");
67
+ s.emit({ type: "run-start", waves: null });
68
+ const done = runOpEvents({ args: args({ extraPositional: "r-follow", json: true, follow: true }), plugins: [], serializers: [] });
69
+ await new Promise((r) => setTimeout(r, 150));
70
+ expect(out.join("").trim().split("\n")).toHaveLength(1);
71
+ s.emit({ type: "phase-start", component: null, phase: "Build" });
72
+ s.emit({ type: "phase-settle", component: null, phase: "Build", status: "gated" });
73
+ s.settle("gated");
74
+ expect(await done).toBe(3);
75
+ expect(out.join("").trim().split("\n").map((l) => JSON.parse(l).type)).toEqual(["run-start", "phase-start", "phase-settle", "run-settle"]);
76
+ });
77
+
78
+ test("without --json each event is one line for a person", async () => {
79
+ const s = await stream("r-human");
80
+ s.emit({ type: "run-start", waves: null });
81
+ s.settle("ok");
82
+ expect(await runOpEvents({ args: args({ extraPositional: "r-human" }), plugins: [], serializers: [] })).toBe(0);
83
+ expect(out.join("")).toMatch(/run-start/);
84
+ expect(out.join("")).toMatch(/run-settle\s+ok/);
85
+ });
86
+
87
+ test("a run with no kept events is refused by name; no run is refused", async () => {
88
+ expect(await runOpEvents({ args: args({ extraPositional: "nope" }), plugins: [], serializers: [] })).toBe(1);
89
+ expect(await runOpEvents({ args: args({}), plugins: [], serializers: [] })).toBe(1);
90
+ expect(await runOpEvents({ args: args({ extraPositional: "x", on: "fountain" }), plugins: [], serializers: [] })).toBe(1);
91
+ });
92
+ });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `chant run --components --release <label|digest>` (#4043): the run builds
3
+ * from the checked-out commit, so the release must be cut from it and have a
4
+ * target for the environment. Checked before anything deploys.
5
+ */
6
+ import { describe, test, expect, vi } from "vitest";
7
+ import { makeRelease } from "../../lifecycle/release-store";
8
+
9
+ const COMMIT = "a1b2c3d4".repeat(5);
10
+ const release = makeRelease({
11
+ member: null,
12
+ commit: COMMIT,
13
+ artifactDigests: [],
14
+ targets: [{ env: "dev", templates: [], params: [] }],
15
+ });
16
+ let head = COMMIT;
17
+
18
+ vi.mock("../../lifecycle/git", () => ({
19
+ getHeadCommit: async () => head,
20
+ fetchLifecycle: async () => true,
21
+ pushLifecycle: async () => false,
22
+ }));
23
+ vi.mock("../../lifecycle/release-store", async () => {
24
+ const actual = await vi.importActual<typeof import("../../lifecycle/release-store")>("../../lifecycle/release-store");
25
+ return {
26
+ ...actual,
27
+ resolveRelease: async (ref: string) => (ref === "v1" || ref === release.releaseDigest ? { release, label: "v1" } : null),
28
+ };
29
+ });
30
+
31
+ const { resolveRunRelease } = await import("./run");
32
+
33
+ describe("resolveRunRelease", () => {
34
+ test("a release cut from HEAD with a target for the environment is the run's release, by label or digest", async () => {
35
+ head = COMMIT;
36
+ expect(await resolveRunRelease("v1", "dev", ".")).toMatchObject({ releaseDigest: release.releaseDigest, label: "v1" });
37
+ expect(await resolveRunRelease(release.releaseDigest, "dev", ".")).toMatchObject({ releaseDigest: release.releaseDigest });
38
+ });
39
+
40
+ test("a release it can't find is refused", async () => {
41
+ expect(await resolveRunRelease("v9", "dev", ".")).toMatch(/no release is labelled or digested "v9"/);
42
+ });
43
+
44
+ test("a release cut from another commit is refused, and the message says why", async () => {
45
+ head = "f".repeat(40);
46
+ const why = await resolveRunRelease("v1", "dev", ".");
47
+ expect(why).toMatch(/was cut from a1b2c3d4a1b2 and this checkout is at ffffffffffff/);
48
+ expect(why).toMatch(/promote --release/);
49
+ });
50
+
51
+ test("a release with no target for the environment is refused", async () => {
52
+ head = COMMIT;
53
+ expect(await resolveRunRelease("v1", "prod", ".")).toMatch(/has no target for "prod"/);
54
+ });
55
+ });
@@ -98,6 +98,7 @@ vi.mock("../../lifecycle/run-ledger", async (importOriginal) => {
98
98
 
99
99
  const { runOpList, runOpStatus, runOpLog, runOpSignalRenamed, runOpApprove, runOpCancel, runOp, runOpComponents } =
100
100
  await import("./run");
101
+ const { checkRunEventLines } = await import("../../workspace/__fixtures__/run-event-check");
101
102
 
102
103
  function makeArgs(overrides: Partial<ParsedArgs> = {}): ParsedArgs {
103
104
  return {
@@ -1554,6 +1555,35 @@ describe("runOpComponents", () => {
1554
1555
  vi.restoreAllMocks();
1555
1556
  });
1556
1557
 
1558
+ test("--events-json streams the run-event stream as runComponents emits it, and ends one that never reached a component (#4041)", async () => {
1559
+ runComponentsMock.mockImplementation(
1560
+ async (_path: string, _selector: string, options: { onRunEvent?: (b: unknown) => void }) => {
1561
+ options.onRunEvent?.({ type: "run-start", waves: [["svc"]] });
1562
+ options.onRunEvent?.({ type: "run-settle", status: "ok", durationMs: null });
1563
+ return {
1564
+ success: true,
1565
+ selected: ["svc"],
1566
+ run: { order: ["svc"], waves: [["svc"]], results: [{ component: "svc", ok: true, status: "ok", records: [] }], ok: true, status: "ok" },
1567
+ };
1568
+ },
1569
+ );
1570
+ const stdoutWrite = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
1571
+ vi.spyOn(process.stderr, "write").mockImplementation(() => true);
1572
+
1573
+ expect(await runOpComponents({ args: makeArgs({ path: "svc", eventsJson: true }), plugins: [], serializers: [] })).toBe(0);
1574
+ const events = checkRunEventLines(stdoutWrite.mock.calls.map((c) => String(c[0])));
1575
+ expect(events[0]).toMatchObject({ type: "run-start", kind: "component", name: "svc", waves: [["svc"]] });
1576
+ expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "ok" });
1577
+
1578
+ stdoutWrite.mockClear();
1579
+ runComponentsMock.mockReset().mockResolvedValue({ success: false, selected: [], error: "no component named svc" });
1580
+ expect(await runOpComponents({ args: makeArgs({ path: "svc", eventsJson: true }), plugins: [], serializers: [] })).toBe(1);
1581
+ const failed = checkRunEventLines(stdoutWrite.mock.calls.map((c) => String(c[0])));
1582
+ expect(failed.map((e) => e.type)).toEqual(["run-start", "run-settle"]);
1583
+ expect(failed.at(-1)).toMatchObject({ status: "failed", error: "no component named svc" });
1584
+ vi.restoreAllMocks();
1585
+ });
1586
+
1557
1587
  test("without --progress-json, runComponents receives onProgress: undefined and nothing streams", async () => {
1558
1588
  runComponentsMock.mockResolvedValue({
1559
1589
  success: true,
@@ -10,12 +10,12 @@ 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";
17
17
  import { formatError, formatWarning, formatSuccess, formatBold, formatInfo } from "../format";
18
- import { resolveCliBuildParams, parseParamFlags } from "../build-params-cli";
18
+ import { parseParamFlags } from "../build-params-cli";
19
19
  import type { CommandContext } from "../registry";
20
20
  import { stewardBesideFor, stewardFormFor, stewardTurnLeaseName, DEFAULT_STEWARD_ENV, type StewardDeclaration } from "../../op/steward";
21
21
  import { holdBesideLease } from "../../op/steward-beside";
@@ -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,91 @@ 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 = [
474
+ where,
475
+ typeof x.status === "string" ? x.status : "",
476
+ // A blocked component (#4007) names what held it back.
477
+ typeof x.blockedBy === "string" ? `by ${x.blockedBy}` : "",
478
+ typeof x.error === "string" ? x.error : "",
479
+ ].filter(Boolean).join(" ");
480
+ return `${String(x.at ?? "")} ${String(x.type).padEnd(16)}${detail}`.trimEnd();
481
+ }
482
+
483
+ /** The exit code a run's verdict maps to: `chant run`'s own (0 ok, 1 failed, 3 waiting on a person). */
484
+ function exitOfSettle(status: unknown): number {
485
+ return status === "ok" ? 0 : status === "gated" || status === "waiting" ? 3 : 1;
486
+ }
487
+
488
+ /**
489
+ * `chant run events <run> [--json] [--follow]` (#4042): the events a run on
490
+ * this host kept, replayed. `<run>` is a run id, or the name of an Op or a
491
+ * component selection, for which the newest stored run stands. A settled run
492
+ * and a run in progress both replay what is in the file; `--follow` goes on
493
+ * from there until the run settles. `--json` prints the stored lines as they
494
+ * were written; without it each event is one line for a person.
495
+ *
496
+ * The exit code is the replayed run's verdict (0, 1, or 3 for a run waiting
497
+ * on a person). A replay of a run still going, and a follow cut short by
498
+ * Ctrl-C, exit 0.
499
+ */
500
+ export async function runOpEvents(ctx: CommandContext): Promise<number> {
501
+ const ref = ctx.args.extraPositional;
502
+ if (!ref) {
503
+ console.error(formatError({ message: "A run id or an Op name is required: chant run events <run>" }));
504
+ return 1;
505
+ }
506
+ if (ctx.args.on !== undefined && ctx.args.on !== "local") {
507
+ console.error(formatError({
508
+ message: `run events reads the events kept on this host, so --on ${ctx.args.on} has none to read`,
509
+ hint: "A run hosted off this host is read once its in-flight state is off-host (#3990).",
510
+ }));
511
+ return 1;
512
+ }
513
+ const cwd = resolve(".");
514
+ let stored = await findStoredRun(cwd, ref);
515
+ if (!stored && ctx.args.follow) {
516
+ // The run may be starting: give it a moment to open its file.
517
+ const deadline = Date.now() + RUN_EVENTS_WAIT_MS;
518
+ while (!stored && Date.now() < deadline) {
519
+ await new Promise((r) => setTimeout(r, 100));
520
+ stored = await findStoredRun(cwd, ref);
521
+ }
522
+ }
523
+ if (!stored) {
524
+ console.error(formatError({
525
+ message: `No events are kept for "${ref}" on this host`,
526
+ 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.",
527
+ }));
528
+ return 1;
529
+ }
530
+
531
+ const controller = new AbortController();
532
+ const onSigint = () => controller.abort();
533
+ process.on("SIGINT", onSigint);
534
+ try {
535
+ const settle = await readRunEvents(
536
+ stored.file,
537
+ (event, line) => {
538
+ if (ctx.args.json) process.stdout.write(line);
539
+ else console.log(humanRunEventLine(event));
540
+ },
541
+ { follow: ctx.args.follow === true, signal: controller.signal },
542
+ );
543
+ return settle ? exitOfSettle((settle as unknown as { status?: unknown }).status) : 0;
544
+ } finally {
545
+ process.removeListener("SIGINT", onSigint);
546
+ }
547
+ }
548
+
549
+ /** How long `--follow` waits for a run's file to appear. */
550
+ const RUN_EVENTS_WAIT_MS = 5000;
551
+
463
552
  // ── chant run approve <op> <gate> ────────────────────────────────────────────
464
553
 
465
554
  /**
@@ -778,6 +867,7 @@ export async function runOp(ctx: CommandContext): Promise<number> {
778
867
  }));
779
868
  return 1;
780
869
  }
870
+ if (refusesBothStreams(ctx.args)) return 1;
781
871
  if (ctx.args.components) return runOpComponents(ctx);
782
872
  if (ctx.args.report) {
783
873
  console.error(formatError({
@@ -791,6 +881,16 @@ export async function runOp(ctx: CommandContext): Promise<number> {
791
881
 
792
882
  // ── Auto-release recording post-run (#597) ──────────────────────────────────
793
883
 
884
+ /**
885
+ * The run id and origin an auto-recorded release names (#4020): the CI run
886
+ * when the process is in CI, so the release and the gate it passed (which
887
+ * records the CI run id) name the same run, else `localId` with the `local`
888
+ * origin.
889
+ */
890
+ export function autoReleaseRun(localId: string): { runId: string; runOrigin: RunOrigin } {
891
+ return resolveCiRun() ?? { runId: localId, runOrigin: { forge: "local" } };
892
+ }
893
+
794
894
  /**
795
895
  * After a successful `chant run --components` (local executor), auto-emit
796
896
  * one release record per successfully deployed component that published a
@@ -831,6 +931,8 @@ export async function recordAutoReleasesForRun(
831
931
  env: string,
832
932
  runId: string,
833
933
  disabled: boolean,
934
+ runOrigin: RunOrigin = { forge: "local" },
935
+ releaseDigest?: string,
834
936
  ): Promise<ReleaseRecord[]> {
835
937
  const recorded: ReleaseRecord[] = [];
836
938
  for (const componentResult of results) {
@@ -842,8 +944,13 @@ export async function recordAutoReleasesForRun(
842
944
  success: true,
843
945
  records: componentResult.records,
844
946
  runId,
845
- // A local-executor id resolves nowhere — say so in a field (#2045).
846
- runOrigin: { forge: "local" },
947
+ // A local-executor id resolves nowhere, and says so in a field (#2045). A run in CI names the CI run.
948
+ runOrigin,
949
+ // #1035, #4020: who approved at the gate and which resolution admitted the deploy.
950
+ ...(gateApprover(componentResult) ? { approver: gateApprover(componentResult) } : {}),
951
+ ...(gateAdmission(componentResult) ? { admission: gateAdmission(componentResult) } : {}),
952
+ // #4043: the release the deploy was made under, when `--release` named one.
953
+ ...(releaseDigest !== undefined ? { releaseDigest } : {}),
847
954
  },
848
955
  { disabled },
849
956
  );
@@ -953,6 +1060,44 @@ function refusesNoRecordOn(runtime: Pick<OpRuntimeProvider, "name">): boolean {
953
1060
  return true;
954
1061
  }
955
1062
 
1063
+ /**
1064
+ * Resolve the release `chant run --components --release` names (#4043), or the
1065
+ * reason it can't be used. The run builds from the checked-out commit, so the
1066
+ * release's commit must be HEAD, and the release needs a target for `env`.
1067
+ */
1068
+ export async function resolveRunRelease(ref: string, env: string, cwd: string) {
1069
+ const { fetchLifecycle, getHeadCommit } = await import("../../lifecycle/git");
1070
+ const { resolveRelease } = await import("../../lifecycle/release-store");
1071
+ const { targetFor } = await import("../../components/release-promote");
1072
+ await fetchLifecycle({ cwd }).catch(() => false);
1073
+ const found = await resolveRelease(ref, { cwd });
1074
+ if (!found) return `no release is labelled or digested "${ref}" in this member`;
1075
+ const name = found.label ?? found.release.releaseDigest;
1076
+ const head = await getHeadCommit({ cwd });
1077
+ if (found.release.commit !== head) {
1078
+ return `release ${name} was cut from ${found.release.commit.slice(0, 12)} and this checkout is at ${head.slice(0, 12)}; run builds from the checkout, so check out the release's commit, or deploy it with \`chant components promote --release\``;
1079
+ }
1080
+ const target = targetFor(found.release, env);
1081
+ if ("error" in target) return target.error;
1082
+ return { releaseDigest: found.release.releaseDigest, label: found.label, release: found.release };
1083
+ }
1084
+
1085
+ /** A rebuilt image need not digest as the release's own did; say so, as promote does. Never fails the run. */
1086
+ async function warnOnArtifactMismatch(release: import("../../lifecycle/release-store").Release, recorded: readonly ReleaseRecord[]): Promise<void> {
1087
+ try {
1088
+ const { releaseArtifactMismatches } = await import("../../components/release-promote");
1089
+ const { readBuildManifest } = await import("../../lifecycle/build-ledger-store");
1090
+ const mismatches = await releaseArtifactMismatches(
1091
+ release,
1092
+ recorded.map((r) => ({ component: r.component, source: r })),
1093
+ (digest) => readBuildManifest(digest).catch(() => null),
1094
+ );
1095
+ for (const message of mismatches) console.error(formatWarning({ message }));
1096
+ } catch {
1097
+ // The record is written; a manifest that can't be read is not worth failing the run for.
1098
+ }
1099
+ }
1100
+
956
1101
  async function runComponentSelection(ctx: CommandContext): Promise<number> {
957
1102
  const selector = ctx.args.path;
958
1103
  if (!selector || selector === ".") {
@@ -968,11 +1113,13 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
968
1113
 
969
1114
  const projectPath = resolve(".");
970
1115
  const { config } = await loadChantConfig(projectPath).catch(() => ({ config: {} as ChantConfig }));
971
- const paramsResolution = resolveCliBuildParams(config.buildParams, {
1116
+ // #4024: with --env, a workspace member's flag records back their parameters.
1117
+ const { resolveFlaggedBuildParams } = await import("../../flag-params");
1118
+ const paramsResolution = await resolveFlaggedBuildParams(projectPath, config.buildParams, {
972
1119
  cli: parseParamFlags(ctx.args.param),
973
1120
  paramsFile: ctx.args.paramsFile,
974
1121
  verbose: ctx.args.verbose,
975
- });
1122
+ }, ctx.args.env);
976
1123
  if (!paramsResolution.success) {
977
1124
  for (const message of paramsResolution.errors) console.error(message);
978
1125
  return 1;
@@ -990,6 +1137,20 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
990
1137
  }
991
1138
 
992
1139
  const env = ctx.args.env ?? "local";
1140
+
1141
+ // #4043: `--release <label|digest>` says this run deploys that release. The
1142
+ // run builds from the checked-out commit, so the release must have been cut
1143
+ // from it, and must have a target for the environment. Both are checked
1144
+ // before anything deploys; nothing is inferred when the flag is absent.
1145
+ let underRelease: { releaseDigest: string; label: string | null; release: import("../../lifecycle/release-store").Release } | undefined;
1146
+ if (ctx.args.release) {
1147
+ const refused = await resolveRunRelease(ctx.args.release, env, projectPath);
1148
+ if (typeof refused === "string") {
1149
+ console.error(formatError({ message: refused, hint: "List the releases with: chant release list" }));
1150
+ return 1;
1151
+ }
1152
+ underRelease = refused;
1153
+ }
993
1154
  // Seed cross-component/cross-stack outputs from upstream jobs' dumped files
994
1155
  // (`--seed-outputs`), so a single-component run resolves references to a
995
1156
  // component that ran in an earlier CI job.
@@ -1011,6 +1172,10 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1011
1172
  // `undefined` and every `onProgress?.(...)` call in the driver is a no-op —
1012
1173
  // behavior is byte-for-byte unchanged from before this flag existed.
1013
1174
  const onProgress = ctx.args.progressJson ? ndjsonProgressSink() : undefined;
1175
+ // `--events-json` (#4041): the run-event stream instead. The run is named by
1176
+ // the id its release records carry, so the two join.
1177
+ const release = autoReleaseRun(`local-${Date.now()}`);
1178
+ const events = await openCliRunEvents(ctx.args, { kind: "component", name: selector, runId: release.runId, cwd: projectPath });
1014
1179
  // A list (#3950) pulls in what its components depend on; say which ones and
1015
1180
  // in what order before the first one runs. stderr, so --json and
1016
1181
  // --progress-json keep stdout to themselves.
@@ -1020,14 +1185,25 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1020
1185
  if (plan.added.length > 0) console.error(formatInfo(`added as dependencies: ${plan.added.join(", ")}`));
1021
1186
  }
1022
1187
  : 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
- });
1188
+ let result: RunComponentsResult;
1189
+ try {
1190
+ result = await runtime.runComponents(projectPath, selector, {
1191
+ env: ctx.args.env,
1192
+ componentOutputs: seededOutputs,
1193
+ onProgress,
1194
+ ...(events ? { onRunEvent: events.emit } : {}),
1195
+ sandbox: ctx.args.sandbox,
1196
+ buildParams: paramsResolution.provenance,
1197
+ ...(onPlan ? { onPlan } : {}),
1198
+ });
1199
+ } catch (err) {
1200
+ events?.settle("failed", err instanceof Error ? err.message : String(err));
1201
+ throw err;
1202
+ }
1203
+ // A run that never reached its components (a bad selector, a cycle) still ends its stream.
1204
+ if (result.success) events?.settle("ok");
1205
+ else if (result.gated) events?.settle("gated");
1206
+ else events?.settle("failed", result.error);
1031
1207
 
1032
1208
  // Dump the accumulated outputs for a downstream job to seed from. Written
1033
1209
  // even on failure (partial outputs) so a resumed run still has what completed.
@@ -1094,7 +1270,8 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1094
1270
  if (result.success && result.run) {
1095
1271
  // #3955: a run that records nothing has no release to report either.
1096
1272
  const disabled = resolveAutoReleaseDisabled(config, ctx.args.noReleaseRecord) || ledgerRecordingOff();
1097
- const recorded = await recordAutoReleasesForRun(result.run.results, env, `local-${Date.now()}`, disabled);
1273
+ const recorded = await recordAutoReleasesForRun(result.run.results, env, release.runId, disabled, release.runOrigin, underRelease?.releaseDigest);
1274
+ if (underRelease) await warnOnArtifactMismatch(underRelease.release, recorded);
1098
1275
  // `--digest-file` (#2602): the releases this run recorded, one
1099
1276
  // `<component>=<digest>` line each, the form `chant components promote
1100
1277
  // --digest` takes. A generated promote job reads it to promote exactly
@@ -1182,6 +1359,15 @@ async function runOpOnResolvedRuntime(ctx: CommandContext): Promise<number> {
1182
1359
  return 1;
1183
1360
  }
1184
1361
 
1362
+ // #4044: a runner key signs the run's record as it settles, so `ci tick`
1363
+ // can count the run. `--key` must be a runner key; the PEM in
1364
+ // CHANT_RUNNER_KEY that is not one leaves the run unsigned, with a warning.
1365
+ const signRecord = await runSignerFor(ctx);
1366
+ if (signRecord === null) {
1367
+ await stewardGate?.release?.();
1368
+ return 1;
1369
+ }
1370
+
1185
1371
  // `--progress-json` streams one NDJSON StepRecord per settled step, fed by
1186
1372
  // whatever the runtime reports through `progress`.
1187
1373
  const progress = ctx.args.progressJson ? ndjsonProgressSink<StepRecord>() : undefined;
@@ -1217,6 +1403,10 @@ async function runOpOnResolvedRuntime(ctx: CommandContext): Promise<number> {
1217
1403
  ...(params ? { params } : {}),
1218
1404
  ...(ctx.args.profile !== undefined ? { profile: ctx.args.profile } : {}),
1219
1405
  progress,
1406
+ // `--events-json` (#4041): the runtime writes the run's events itself,
1407
+ // since it is the one that knows the run's id and where it ran.
1408
+ ...(ctx.args.eventsJson ? { runEvents: stdoutLine } : {}),
1409
+ ...(signRecord ? { signRecord } : {}),
1220
1410
  signal: controller.signal,
1221
1411
  ...(work ? { work } : {}),
1222
1412
  });
@@ -1362,7 +1552,42 @@ async function resolveRunWork(
1362
1552
  export function runOpUnknown(ctx: CommandContext): Promise<number> {
1363
1553
  console.error(formatError({
1364
1554
  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",
1555
+ hint: "Available: chant run <name>, run list, run status, run approve, run cancel, run log, run events",
1366
1556
  }));
1367
1557
  return Promise.resolve(1);
1368
1558
  }
1559
+
1560
+ /** The variable a runner key is read from, as PEM, as `chant ci tick` reads it (`RUNNER_KEY_ENV` in ../../workspace/ci-green.ts). */
1561
+ const RUNNER_KEY_ENV = "CHANT_RUNNER_KEY";
1562
+
1563
+ /**
1564
+ * What signs the run's record (#4044): the runner key `--key` names, or the
1565
+ * PEM in `CHANT_RUNNER_KEY`, checked against `.chant/trust.json` at `HEAD`.
1566
+ * Undefined when neither is given. Null, after printing why, when `--key`
1567
+ * is not a runner key; an unusable `CHANT_RUNNER_KEY` only warns, since the
1568
+ * variable may be set for `ci tick` in a job that also runs Ops.
1569
+ */
1570
+ async function runSignerFor(ctx: CommandContext): Promise<((record: OpRunRecord) => NonNullable<OpRunRecord["statement"]>) | undefined | null> {
1571
+ const fromFlag = ctx.args.key !== undefined;
1572
+ let pem: string | undefined;
1573
+ try {
1574
+ pem = fromFlag ? readFileSync(resolve(ctx.args.key!), "utf-8") : process.env[RUNNER_KEY_ENV] || undefined;
1575
+ } catch (err) {
1576
+ console.error(formatError({ message: `--key ${ctx.args.key}: ${err instanceof Error ? err.message : String(err)}` }));
1577
+ return null;
1578
+ }
1579
+ if (pem === undefined) return undefined;
1580
+ try {
1581
+ // Loaded only when a key is given, so a run without one loads no workspace code (level 0, #2525).
1582
+ const { opRunSigner } = await import("../../workspace/ci-green");
1583
+ return opRunSigner(process.cwd(), pem);
1584
+ } catch (err) {
1585
+ const message = err instanceof Error ? err.message : String(err);
1586
+ if (fromFlag) {
1587
+ console.error(formatError({ message: `the run would not be signed: ${message}` }));
1588
+ return null;
1589
+ }
1590
+ console.error(formatWarning({ message: `${RUNNER_KEY_ENV} is set, and the run will not be signed: ${message}` }));
1591
+ return undefined;
1592
+ }
1593
+ }