@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
@@ -7,6 +7,22 @@ import { generateComponentsPipeline } from "../../components/cli-support";
7
7
  import { loadChantConfigUpward, type ChantConfig } from "../../config";
8
8
  import { resolveCliBuildParams, parseParamFlags } from "../build-params-cli";
9
9
  import { findWorkspaceRoot } from "../../project-root";
10
+ import { resolvePromotionSource } from "../../components/promote-order";
11
+ import type { PrLoopPipelineOptions } from "../../components/pr-pipeline";
12
+
13
+ /**
14
+ * The `prLoop` generator option from `--pr-loop`'s flags (#3183): the gate
15
+ * and the branch, and with `--events-json` and `--report-url` (#4007) the
16
+ * flags the generated jobs pass to pr-plan and pr-apply.
17
+ */
18
+ function prLoopOption(args: CommandContext["args"]): PrLoopPipelineOptions {
19
+ return {
20
+ ...(args.gate ? { gate: args.gate } : {}),
21
+ ...(args.branch ? { branch: args.branch } : {}),
22
+ ...(args.eventsJson ? { events: true } : {}),
23
+ ...(args.reportUrl ? { reportUrl: args.reportUrl } : {}),
24
+ };
25
+ }
10
26
 
11
27
  /**
12
28
  * `chant build --components --generate <lexicon>` — generate mode (#563,
@@ -45,6 +61,24 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
45
61
  return 1;
46
62
  }
47
63
 
64
+ // #4030: the generated promote job runs `promote --from <env> --to <promote-to>`,
65
+ // which the member's declared promotion order refuses when it skips a step.
66
+ // Refuse here, with the same code, rather than write a job that fails.
67
+ if (args.promoteTo) {
68
+ // Loaded here, not at the top: a build without --promote-to loads no workspace module (#2526).
69
+ const { promotionOrder } = await import("../../workspace/promotion-order");
70
+ const resolved = resolvePromotionSource({
71
+ order: promotionOrder(config.environments),
72
+ to: args.promoteTo,
73
+ from: args.env ?? "production",
74
+ ...(args.allowSkip ? { allowSkip: true } : {}),
75
+ });
76
+ if ("error" in resolved) {
77
+ console.error(formatError({ message: `${resolved.error.message} [${resolved.error.code}]`, hint: resolved.error.fix }));
78
+ return 1;
79
+ }
80
+ }
81
+
48
82
  // A project inside a workspace member gets that member's pipeline (#2542):
49
83
  // path-filtered, run in the member's directory, written to a file named
50
84
  // after the member and recorded as the member's generated file. The
@@ -62,11 +96,14 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
62
96
  lexicon,
63
97
  env: args.env,
64
98
  promoteTo: args.promoteTo,
99
+ ...(args.allowSkip ? { allowSkip: true } : {}),
65
100
  output: args.output,
66
101
  params: args.param,
67
102
  paramsFile: args.paramsFile,
68
103
  ...(args.waveGate ? { waveGate: { gate: args.waveGate, ...(args.canary?.length ? { canary: args.canary } : {}), ...(args.base ? { base: args.base } : {}) } } : {}),
69
- ...(args.prLoop ? { prLoop: { ...(args.gate ? { gate: args.gate } : {}), ...(args.branch ? { branch: args.branch } : {}) } } : {}),
104
+ ...(args.prLoop ? { prLoop: prLoopOption(args) } : {}),
105
+ ...(args.image ? { image: args.image } : {}),
106
+ ...(args.beforeScript?.length ? { beforeScript: args.beforeScript } : {}),
70
107
  });
71
108
  } catch (err) {
72
109
  console.error(formatError({ message: memberPipeline.describeError(err) }));
@@ -84,15 +121,20 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
84
121
  {
85
122
  env: args.env,
86
123
  ...(args.promoteTo ? { promoteTo: args.promoteTo } : {}),
124
+ // #4030: the job passes --allow-skip through, so it is not refused again at deploy time.
125
+ ...(args.promoteTo && args.allowSkip
126
+ ? { promoteCommand: ["chant", "components", "promote", "--from", args.env ?? "production", "--to", args.promoteTo, "--allow-skip"] }
127
+ : {}),
87
128
  ...(plan ? { member: plan.member } : {}),
88
129
  // #3049: one job per wave of a gated-wave fan-out instead of one per component.
89
130
  ...(args.waveGate
90
131
  ? { gatedWaves: { gate: args.waveGate, ...(args.canary?.length ? { canary: args.canary } : {}), ...(args.base ? { base: args.base } : {}) } }
91
132
  : {}),
92
133
  // #3183: a plan job per pull request and an apply job per merge.
93
- ...(args.prLoop
94
- ? { prLoop: { ...(args.gate ? { gate: args.gate } : {}), ...(args.branch ? { branch: args.branch } : {}) } }
95
- : {}),
134
+ ...(args.prLoop ? { prLoop: prLoopOption(args) } : {}),
135
+ // #4002: the job image and the lines that install what it lacks, chant itself included.
136
+ ...(args.image ? { image: args.image } : {}),
137
+ ...(args.beforeScript?.length ? { beforeScript: args.beforeScript } : {}),
96
138
  },
97
139
  args.sandbox,
98
140
  paramsResolution.provenance,
@@ -1,10 +1,12 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
2
3
  import { CHANGE_SET_CONTRACT, CHANGE_SET_SCHEMA_ID, type ChangeSetDocument } from "../../change-set";
3
4
  import { GITHUB_COMMENT_LIMIT, groupChangeSet, renderPlanSummaryMarkdown, renderPlanSummaryText } from "../../plan-summary";
4
- import { formatError } from "../format";
5
+ import { formatError, formatWarning } from "../format";
5
6
  import type { CommandContext } from "../registry";
6
7
 
7
8
  const USAGE = "chant change-set summary <change-set.json> [--format text|json|markdown] [--limit <chars>]";
9
+ const SHOW_USAGE = "chant change-set show <digest> [--member <name>] [--json]";
8
10
 
9
11
  /**
10
12
  * `chant change-set summary <file>` (#3188): the grouped plan summary of a
@@ -42,3 +44,49 @@ export async function runChangeSetSummary(ctx: CommandContext): Promise<number>
42
44
  else process.stdout.write(renderPlanSummaryText(summary));
43
45
  return 0;
44
46
  }
47
+
48
+ /**
49
+ * `chant change-set show <digest>` (#4022): the change-set document a gate
50
+ * bound, stored on `chant/lifecycle` under its digest, with each entry's
51
+ * graph node. `--json` prints the read contract's `change-set-show`
52
+ * document (`../../workspace/change-set-show.ts`); without it, the grouped
53
+ * summary and each entry with its node. `--member` reads a workspace
54
+ * member's ledger from anywhere in the checkout. Exit 1 when nothing is
55
+ * stored under the digest.
56
+ */
57
+ export async function runChangeSetShow(ctx: CommandContext): Promise<number> {
58
+ const { args } = ctx;
59
+ const digest = args.extraPositional;
60
+ const { changeSetShowDocument, CHANGE_SET_DIGEST_PATTERN } = await import("../../workspace/change-set-show");
61
+ if (!digest || !CHANGE_SET_DIGEST_PATTERN.test(digest)) {
62
+ console.error(formatError({ message: digest ? `${digest} is not a change-set digest` : "change-set show needs the digest a gate binds", hint: SHOW_USAGE }));
63
+ return 1;
64
+ }
65
+ if ((args.members?.length ?? 0) > 1) {
66
+ console.error(formatError({ message: `change-set show reads one member, not ${args.members!.join(", ")}`, hint: SHOW_USAGE }));
67
+ return 1;
68
+ }
69
+ const member = args.members?.[0]?.split("/").filter(Boolean);
70
+ const doc = await changeSetShowDocument({ cwd: resolve("."), digest, ...(member ? { member } : {}) });
71
+ if (args.json) {
72
+ console.log(JSON.stringify(doc, null, 2));
73
+ return "error" in doc || doc.changeSet === null ? 1 : 0;
74
+ }
75
+ if ("error" in doc) {
76
+ console.error(formatError({ message: doc.error.message, hint: SHOW_USAGE }));
77
+ return 1;
78
+ }
79
+ if (doc.reason) console.error(formatWarning({ message: doc.reason.message }));
80
+ if (doc.changeSet === null) {
81
+ if (!doc.reason) console.error(formatError({ message: `no change set is stored under ${digest}${doc.member ? ` for member ${doc.member}` : ""}`, hint: "A pull-request or release gate stores the change set it binds; a component gate binds a lifecycle plan and stores none." }));
82
+ return 1;
83
+ }
84
+ process.stdout.write(renderPlanSummaryText(groupChangeSet(doc.changeSet)));
85
+ const graph = doc.changeSet.graph;
86
+ console.log(graph ? `\nNodes from the graph of ${graph.member ?? "this project"}${graph.at ? ` at ${graph.at.slice(0, 12)}` : ""}:` : "\nNo graph was read, so no entry has a node.");
87
+ for (const e of doc.changeSet.entries) {
88
+ if (e.action === "no-op" || e.action === "read") continue;
89
+ console.log(` ${e.action.padEnd(8)} ${e.member} ${e.address}${e.deposed ? ` (deposed ${e.deposed})` : ""} ${e.node ?? "(no node)"}`);
90
+ }
91
+ return 0;
92
+ }
@@ -52,6 +52,7 @@ const sealedGates = new Set<string>();
52
52
  const { CapabilityRegistry } = await import("../../components/capability");
53
53
  const { memoryGateLedgerPort } = await import("../../op/gate");
54
54
  const { runComponentsFanOut } = await import("./fan-out");
55
+ const { checkRunEventLines } = await import("../../workspace/__fixtures__/run-event-check");
55
56
 
56
57
  let gateLedger = memoryGateLedgerPort();
57
58
 
@@ -232,7 +233,8 @@ describe("proof: a plan-only invocation prints the derivation and dispatches not
232
233
 
233
234
  ran = [];
234
235
  stderrLines = [];
235
- expect(await runComponentsFanOut(ctx({ base: "main", resume, dryRun: true }))).toBe(0);
236
+ // The same environment: progress is bound to it (#4081).
237
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume, dryRun: true }))).toBe(0);
236
238
 
237
239
  expect(ran).toEqual([]);
238
240
  const out = stderr();
@@ -622,3 +624,228 @@ describe("gated waves (#3049): one gate per wave, bound to the wave's set digest
622
624
  expect(stderr()).toContain("--wave runs one wave");
623
625
  });
624
626
  });
627
+
628
+ describe("--events-json (#4041)", () => {
629
+ const lines = () => stdoutLines.filter((l) => l.startsWith('{"$schema"'));
630
+
631
+ test("a fan-out over the set: the gate is a gate phase settled gated, and nothing after it starts", async () => {
632
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", gate: "release", eventsJson: true }))).toBe(3);
633
+ const events = checkRunEventLines(lines());
634
+ expect(events[0]).toMatchObject({ type: "run-start", kind: "component", waves: [["net"], ["cluster-a", "cluster-b"], ["app-one", "app-three", "app-two"]] });
635
+ expect(events.map((e) => e.type)).toEqual(["run-start", "phase-start", "step-start", "gate-waiting", "step-settle", "phase-settle", "run-settle"]);
636
+ expect(events.find((e) => e.type === "gate-waiting")).toMatchObject({ component: null, phase: "gate", step: "gate:release", op: "fan-out", gate: "release" });
637
+ expect(events.at(-1)).toMatchObject({ status: "gated" });
638
+ });
639
+
640
+ test("approved, the gate resolves and every wave and component follows, each step started before it settles", async () => {
641
+ await runComponentsFanOut(ctx({ base: "main", env: "test", gate: "release" }));
642
+ const pending = gateLedger.appended[0];
643
+ gateLedger = memoryGateLedgerPort({
644
+ pending: [pending],
645
+ resolutions: [{
646
+ version: 1, kind: "resolution", op: "fan-out", gate: "release", resolvedBy: "operator",
647
+ timestamp: new Date(Date.parse(pending.timestamp) + 1000).toISOString(), planDigest: pending.planDigest,
648
+ }],
649
+ });
650
+ stdoutLines = [];
651
+ failing = ["cluster-b"];
652
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", gate: "release", eventsJson: true }))).toBe(1);
653
+ const events = checkRunEventLines(lines());
654
+ expect(events.find((e) => e.type === "gate-resolved")).toMatchObject({ gate: "release", resolvedBy: "operator" });
655
+ expect(events.filter((e) => e.type === "wave-start").map((e) => (e as { wave: number }).wave)).toEqual([1, 2, 3]);
656
+ expect(events.find((e) => e.type === "component-settle" && e.component === "cluster-b")).toMatchObject({ status: "failed" });
657
+ expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "failed" });
658
+ });
659
+
660
+ test("a component behind a failure settles blocked, naming it, after the failing wave and before the next starts (#4007)", async () => {
661
+ failing = ["cluster-a"];
662
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", eventsJson: true }))).toBe(1);
663
+ const events = checkRunEventLines(lines());
664
+ const blocked = events.filter((e) => e.type === "component-settle" && e.status === "blocked");
665
+ expect(blocked.map((e) => ({ wave: (e as { wave: number }).wave, component: (e as { component: string }).component, blockedBy: (e as { blockedBy?: string }).blockedBy }))).toEqual([
666
+ { wave: 3, component: "app-one", blockedBy: "cluster-a" },
667
+ { wave: 3, component: "app-two", blockedBy: "cluster-a" },
668
+ ]);
669
+ const wave2Settle = events.findIndex((e) => e.type === "wave-settle" && e.wave === 2);
670
+ const wave3Start = events.findIndex((e) => e.type === "wave-start" && e.wave === 3);
671
+ expect(events.indexOf(blocked[0]!)).toBeGreaterThan(wave2Settle);
672
+ expect(events.indexOf(blocked[1]!)).toBeLessThan(wave3Start);
673
+ // A blocked component never starts.
674
+ expect(events.some((e) => e.type === "component-start" && e.component === "app-one")).toBe(false);
675
+ });
676
+
677
+ test("a resumed fan-out settles what the earlier attempt applied as already-applied, in the derivation's waves (#4007)", async () => {
678
+ const dir = await mkdtemp(join(tmpdir(), "fan-out-"));
679
+ try {
680
+ const resume = join(dir, "attempt.json");
681
+ failing = ["cluster-a"];
682
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(1);
683
+ failing = [];
684
+ stdoutLines = [];
685
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume, eventsJson: true }))).toBe(0);
686
+ const events = checkRunEventLines(lines());
687
+ const firstWave = events.findIndex((e) => e.type === "wave-start");
688
+ const applied = events.filter((e) => e.type === "component-settle" && e.status === "already-applied");
689
+ expect(applied.map((e) => [(e as { component: string }).component, (e as { wave: number }).wave])).toEqual([["app-three", 3], ["cluster-b", 2], ["net", 1]]);
690
+ for (const e of applied) expect(events.indexOf(e)).toBeLessThan(firstWave);
691
+ // --progress-json never printed them, and still does not.
692
+ stdoutLines = [];
693
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume, progressJson: true }))).toBe(0);
694
+ expect(stdoutLines.filter((l) => l.startsWith('{"type"')).map((l) => JSON.parse(l).type)).toEqual(["run-start", "run-done"]);
695
+ } finally {
696
+ await rm(dir, { recursive: true, force: true });
697
+ }
698
+ });
699
+
700
+ test("--progress-json on a set the gate stops now prints run-start and run-done gated (a level-0 change)", async () => {
701
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", gate: "release", progressJson: true }))).toBe(3);
702
+ const progress = stdoutLines.filter((l) => l.startsWith('{"type"')).map((l) => JSON.parse(l));
703
+ expect(progress).toEqual([
704
+ { type: "run-start", waves: [["net"], ["cluster-a", "cluster-b"], ["app-one", "app-three", "app-two"]] },
705
+ { type: "run-done", status: "gated" },
706
+ ]);
707
+ });
708
+
709
+ test("beside --progress-json it is refused", async () => {
710
+ expect(await runComponentsFanOut(ctx({ base: "main", eventsJson: true, progressJson: true }))).toBe(1);
711
+ expect(stderr()).toContain("--events-json and --progress-json both stream to stdout");
712
+ });
713
+ });
714
+
715
+ describe("progress carries only for an unchanged input (#4081)", () => {
716
+ /** The estate with each component deploying a built template from `templates/`, read relative to the project. */
717
+ const templated = ESTATE.map((c) => ({
718
+ ...c,
719
+ deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", stack: `${c.name}-stack`, template: `templates/${c.name}.json` }] }],
720
+ }));
721
+ let dir: string;
722
+ const writeTemplate = (name: string, body: unknown) =>
723
+ writeFile(join(dir, "templates", `${name}.json`), JSON.stringify(body));
724
+
725
+ beforeEach(async () => {
726
+ dir = await mkdtemp(join(tmpdir(), "fan-out-inputs-"));
727
+ const { mkdir } = await import("node:fs/promises");
728
+ await mkdir(join(dir, "templates"));
729
+ for (const c of templated) await writeTemplate(c.name, { Resources: { [c.name]: { Type: "AWS::SNS::Topic" } } });
730
+ // The project is the temp directory, so the templates are read from there and nothing touches the checkout.
731
+ vi.spyOn(process, "cwd").mockReturnValue(dir);
732
+ discoverComponentsMock.mockResolvedValue({
733
+ components: new Map(templated.map((c) => [c.name, { component: c, exportName: c.name, filePath: `${c.name}.component.ts` }])),
734
+ sourceFiles: [],
735
+ errors: [],
736
+ });
737
+ });
738
+ afterEach(async () => {
739
+ await rm(dir, { recursive: true, force: true });
740
+ });
741
+
742
+ test("same shape, changed content: a stale record does not skip the components whose input moved", async () => {
743
+ const resume = join(dir, "attempt.json");
744
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(0);
745
+ const first = JSON.parse(await readFile(resume, "utf8"));
746
+ expect(first.completed).toEqual(["app-one", "app-three", "app-two", "cluster-a", "cluster-b", "net"]);
747
+ expect(Object.keys(first.inputs).sort()).toEqual(first.completed);
748
+ const digest = /plan: (jcs1-sha256:[0-9a-f]{64})/.exec(stderr())?.[1];
749
+
750
+ // The next change: net's topic gets a display name and cluster-a a queue.
751
+ // Same components, same edges, so the same plan digest.
752
+ await writeTemplate("net", { Resources: { net: { Type: "AWS::SNS::Topic", Properties: { DisplayName: "estate" } } } });
753
+ await writeTemplate("cluster-a", { Resources: { "cluster-a": { Type: "AWS::SNS::Topic" }, events: { Type: "AWS::SQS::Queue" } } });
754
+ ran = [];
755
+ stderrLines = [];
756
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(0);
757
+
758
+ expect([...ran].sort()).toEqual(["app-one", "app-three", "app-two", "cluster-a", "cluster-b", "net"]);
759
+ const out = stderr();
760
+ expect(out).toContain(`plan: ${digest}`);
761
+ expect(out).toContain("net: changed since the earlier attempt, so it runs again");
762
+ expect(out).toContain("cluster-a: changed since the earlier attempt, so it runs again");
763
+ // Downstream of net, so its parameters read a changed upstream.
764
+ expect(out).toContain("app-three: changed since the earlier attempt, so it runs again");
765
+ expect(out).not.toMatch(/already-applied/);
766
+ expect(out).toContain("fan-out completed: 6 applied, 0 failed, 0 blocked");
767
+ const second = JSON.parse(await readFile(resume, "utf8"));
768
+ expect(second.inputs.net).not.toBe(first.inputs.net);
769
+ expect(second.inputs["cluster-b"]).not.toBe(first.inputs["cluster-b"]);
770
+ });
771
+
772
+ test("a change below the root reruns only its own subtree", async () => {
773
+ const resume = join(dir, "attempt.json");
774
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(0);
775
+ await writeTemplate("cluster-a", { Resources: { "cluster-a": { Type: "AWS::SNS::Topic" }, events: { Type: "AWS::SQS::Queue" } } });
776
+ ran = [];
777
+ stderrLines = [];
778
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(0);
779
+ expect(ran).toEqual(["cluster-a", "app-one", "app-two"]);
780
+ expect(stderr()).toMatch(/cluster-b +already-applied/);
781
+ expect(stderr()).not.toContain("net: changed");
782
+ });
783
+
784
+ test("the README's fail-then-fix: the fix reruns the failed subtree and the rest stay already-applied", async () => {
785
+ const resume = join(dir, "attempt.json");
786
+ failing = ["cluster-a"];
787
+ await writeTemplate("cluster-a", { Resources: { events: { Type: "AWS::SQS::Queue", Properties: { FifoQueue: true, QueueName: "events" } } } });
788
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(1);
789
+ const digest = /plan: (jcs1-sha256:[0-9a-f]{64})/.exec(stderr())?.[1];
790
+
791
+ // The fix changes cluster-a's template, which never applied.
792
+ failing = [];
793
+ await writeTemplate("cluster-a", { Resources: { events: { Type: "AWS::SQS::Queue", Properties: { FifoQueue: true, QueueName: "events.fifo" } } } });
794
+ ran = [];
795
+ stderrLines = [];
796
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(0);
797
+ expect(ran).toEqual(["cluster-a", "app-one", "app-two"]);
798
+ const out = stderr();
799
+ expect(out).toContain(`plan: ${digest}`);
800
+ expect(out).not.toContain("changed since the earlier attempt");
801
+ for (const name of ["net", "cluster-b", "app-three"]) expect(out).toMatch(new RegExp(`${name} +already-applied`));
802
+ expect(out).toContain("fan-out completed: 3 applied, 0 failed, 0 blocked");
803
+ });
804
+
805
+ test("the fix keeps the approval: approval binds the derivation, progress binds the inputs", async () => {
806
+ const resume = join(dir, "attempt.json");
807
+ failing = ["cluster-a"];
808
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", gate: "release", resume }))).toBe(3);
809
+ const pending = gateLedger.appended[0]!;
810
+ gateLedger = memoryGateLedgerPort({
811
+ pending: [pending],
812
+ resolutions: [
813
+ {
814
+ version: 1,
815
+ kind: "resolution",
816
+ op: "fan-out",
817
+ gate: "release",
818
+ resolvedBy: "operator",
819
+ timestamp: new Date(Date.parse(pending.timestamp) + 1000).toISOString(),
820
+ planDigest: pending.planDigest,
821
+ },
822
+ ],
823
+ });
824
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", gate: "release", resume }))).toBe(1);
825
+
826
+ failing = [];
827
+ await writeTemplate("cluster-a", { Resources: { fixed: { Type: "AWS::SQS::Queue" } } });
828
+ ran = [];
829
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", gate: "release", resume }))).toBe(0);
830
+ expect(ran).toEqual(["cluster-a", "app-one", "app-two"]);
831
+ });
832
+
833
+ test("the input digest is per environment: progress against one does not carry into another", async () => {
834
+ const resume = join(dir, "attempt.json");
835
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(0);
836
+ ran = [];
837
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "prod", resume }))).toBe(0);
838
+ expect(ran).toHaveLength(6);
839
+ });
840
+
841
+ test("a record without input digests carries nothing, and says so", async () => {
842
+ const resume = join(dir, "attempt.json");
843
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", dryRun: true }))).toBe(0);
844
+ const digest = /plan: (jcs1-sha256:[0-9a-f]{64})/.exec(stderr())![1]!;
845
+ await writeFile(resume, JSON.stringify({ digest, completed: ["net", "cluster-b"], failed: [], outputs: {} }));
846
+ stderrLines = [];
847
+ expect(await runComponentsFanOut(ctx({ base: "main", env: "test", resume }))).toBe(0);
848
+ expect(ran).toHaveLength(6);
849
+ expect(stderr()).toContain("does not record what each component applied, so none of its progress carries");
850
+ });
851
+ });
@@ -34,6 +34,15 @@
34
34
  * the plan before the gate is decided, and the digest is carried rather than
35
35
  * recomputed, so the approval still stands.
36
36
  *
37
+ * The approval and the progress are bound to different things (#4081). The
38
+ * approval is bound to the plan digest, the derivation: who runs, in what
39
+ * order, off which edges. The progress is bound to each component's input
40
+ * digest (../../components/fan-out-inputs.ts): a component the earlier
41
+ * attempt applied stays `already-applied` only while what it would apply is
42
+ * the same, and one whose template, step inputs or upstream moved runs again
43
+ * with a line saying so. The fix after a failure keeps the approval, and a
44
+ * different change of the same shape does not inherit the progress.
45
+ *
37
46
  * `--wave-gate <name>` (#3049) swaps the one gate for one per wave: the same
38
47
  * repeated command stops at each wave's gate in turn, each planned after the
39
48
  * wave before it applied, and `chant approve fan-out <name>-wave-<n>` answers
@@ -49,9 +58,11 @@ import { changedFilesBetween, lexiconChangedUnits } from "../../components/chang
49
58
  import { deriveFanOut, fanOutRegistry } from "../../components/fan-out-support";
50
59
  import { EarlierWaveNotAppliedError, runFanOut } from "../../components/fan-out-run";
51
60
  import { readFanOutAttempt, writeFanOutAttempt, type FanOutAttempt } from "../../components/fan-out-record";
61
+ import { componentInputDigests, progressThatCarries } from "../../components/fan-out-inputs";
52
62
  import { describeChangedWave, withWaveRecord, type WaveRecord } from "../../gated-waves";
53
63
  import { renderFanOutHuman, renderFanOutJson, renderFanOutPlan } from "../../components/fan-out-output";
54
64
  import { ndjsonProgressSink } from "../../components/run-progress";
65
+ import { openCliRunEvents, refusesBothStreams } from "../run-events";
55
66
  import { summaryLedgerPrefix, writeGatedRunSummary } from "../../op/gate-summary";
56
67
  import { approveCommand, gateIsSealed } from "../../op/gate";
57
68
  import { getHeadCommit } from "../../lifecycle/git";
@@ -60,7 +71,7 @@ import { resolveCliBuildParams, parseParamFlags } from "../build-params-cli";
60
71
  import { formatError, formatInfo, formatWarning } from "../format";
61
72
  import { FAN_OUT_GATE_OP } from "../../op/gate-name";
62
73
  import { resolveBuildRoot } from "./lifecycle";
63
- import { GATED_EXIT_CODE, recordAutoReleasesForRun } from "./run";
74
+ import { GATED_EXIT_CODE, autoReleaseRun, recordAutoReleasesForRun } from "./run";
64
75
  import type { CommandContext } from "../registry";
65
76
 
66
77
  /**
@@ -172,10 +183,11 @@ export async function resolveChangedUnits(
172
183
  * chant components fan-out --base <ref> | --from-affected <file>
173
184
  * [--head <ref>] [--include-dependents] [--env <env>] [--gate <name>]
174
185
  * [--dry-run] [--json] [--resume <file>] [--seed-outputs <file>]
175
- * [--dump-outputs <file>] [--progress-json]
186
+ * [--dump-outputs <file>] [--progress-json | --events-json]
176
187
  */
177
188
  export async function runComponentsFanOut(ctx: CommandContext): Promise<number> {
178
189
  const { args } = ctx;
190
+ if (refusesBothStreams(args)) return 1;
179
191
  const projectPath = resolve(".");
180
192
  const { config } = await loadChantConfig(projectPath).catch(() => ({ config: {} as ChantConfig }));
181
193
 
@@ -251,7 +263,12 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
251
263
  // real run happens inside `runFanOut`, before the gate is decided, so an
252
264
  // approval survives the resume rather than being re-asked for.
253
265
  const resumePath = args.resume ? resolve(args.resume) : undefined;
266
+ // What each component would apply now (#4081). The record keeps it for
267
+ // every component that applies, and a later attempt carries progress only
268
+ // where it is unchanged.
269
+ const inputsNow = resumePath ? componentInputDigests(derived.components, { projectRoot: projectPath, env: args.env ?? "local" }) : {};
254
270
  let priorCompleted: string[] = [];
271
+ let priorInputs: Record<string, string> = {};
255
272
  let priorOutputs: Record<string, Record<string, unknown>> = {};
256
273
  let priorWaves: WaveRecord[] = [];
257
274
  let priorCarried: Record<string, unknown> = {};
@@ -275,11 +292,28 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
275
292
  // as progress would block their subtrees again on the very run that
276
293
  // exists to retry them — the operator repeated the command precisely
277
294
  // because the thing that failed is now expected to work.
278
- priorCompleted = attempt.completed;
295
+ //
296
+ // And only the part of `completed` whose input is the same now (#4081).
297
+ // The plan digest identifies the derivation, which a different change
298
+ // of the same shape shares; the input digest says what was applied.
299
+ const split = progressThatCarries(attempt, inputsNow);
300
+ if (split.unrecorded && split.changed.length > 0) {
301
+ console.error(formatWarning({
302
+ message: `--resume: "${args.resume}" does not record what each component applied, so none of its progress carries`,
303
+ hint: "It was written before chant recorded component inputs. Everything selected will run.",
304
+ }));
305
+ } else {
306
+ for (const name of split.changed) {
307
+ console.error(formatInfo(`${name}: changed since the earlier attempt, so it runs again`));
308
+ }
309
+ }
310
+ const carried = new Set(split.carried);
311
+ priorCompleted = split.carried;
312
+ priorInputs = Object.fromEntries(split.carried.map((name) => [name, inputsNow[name]!]));
279
313
  priorOutputs = Object.fromEntries(
280
- Object.entries(attempt.outputs).filter(([name]) => attempt.completed.includes(name)),
314
+ Object.entries(attempt.outputs).filter(([name]) => carried.has(name)),
281
315
  );
282
- progress = { completed: attempt.completed };
316
+ progress = { completed: split.carried };
283
317
  priorWaves = attempt.waves ?? [];
284
318
  // What steps kept per member (#3459), such as a choudoufu root's wave
285
319
  // resume file. Each step decides whether what it kept still applies.
@@ -317,6 +351,7 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
317
351
  // runner, ctrl-c) then still knows that the first two applied, and the
318
352
  // repeated command picks up from there instead of redoing them.
319
353
  const completedSoFar = new Set(priorCompleted);
354
+ const inputsSoFar: Record<string, string> = { ...priorInputs };
320
355
  const failedSoFar: string[] = [];
321
356
  const outputsSoFar = { ...priorOutputs };
322
357
  let wavesSoFar = priorWaves;
@@ -328,12 +363,18 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
328
363
  completed: [...completedSoFar].sort(),
329
364
  failed: [...failedSoFar].sort(),
330
365
  outputs: outputsSoFar,
366
+ inputs: Object.fromEntries(Object.entries(inputsSoFar).sort(([a], [b]) => a.localeCompare(b))),
331
367
  ...(wavesSoFar.length > 0 ? { waves: wavesSoFar } : {}),
332
368
  ...(Object.keys(carriedSoFar).length > 0 ? { carried: carriedSoFar } : {}),
333
369
  };
334
370
  writeFanOutAttempt(resumePath, attempt);
335
371
  };
336
372
 
373
+ // The run id the release records carry, chosen before the run so that
374
+ // `--events-json` (#4041) names the run by it too.
375
+ const release = autoReleaseRun(`fan-out-${Date.now()}`);
376
+ const events = await openCliRunEvents(args, { kind: "component", name: `fan-out ${derived.plan.order.join(",")}`, runId: release.runId, cwd: projectPath });
377
+
337
378
  let result;
338
379
  try {
339
380
  result = await runFanOut(derived.plan, derived.components, registry, {
@@ -364,11 +405,13 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
364
405
  }
365
406
  : {}),
366
407
  ...(args.progressJson ? { onProgress: ndjsonProgressSink() } : {}),
408
+ ...(events ? { onRunEvent: events.emit } : {}),
367
409
  ...(resumePath
368
410
  ? {
369
411
  onComponentSettled: (settled, outputs) => {
370
412
  if (settled.status === "ok") {
371
413
  completedSoFar.add(settled.component);
414
+ if (inputsNow[settled.component] !== undefined) inputsSoFar[settled.component] = inputsNow[settled.component]!;
372
415
  if (outputs) outputsSoFar[settled.component] = outputs;
373
416
  } else if (settled.status === "fail") {
374
417
  failedSoFar.push(settled.component);
@@ -379,6 +422,7 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
379
422
  : {}),
380
423
  });
381
424
  } catch (err) {
425
+ events?.settle("failed", err instanceof Error ? err.message : String(err));
382
426
  // `--wave <n>` reached before an earlier wave applied: nothing ran.
383
427
  if (err instanceof EarlierWaveNotAppliedError) {
384
428
  console.error(formatError({ message: err.message }));
@@ -386,6 +430,8 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
386
430
  }
387
431
  throw err;
388
432
  }
433
+ // The fan-out settles the stream; this only ends one it left open.
434
+ events?.settle(result.status === "fail" ? "failed" : result.status);
389
435
 
390
436
  if (args.dumpOutputs) {
391
437
  const dumpPath = resolve(args.dumpOutputs);
@@ -420,8 +466,9 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
420
466
  await recordAutoReleasesForRun(
421
467
  result.results,
422
468
  args.env ?? "local",
423
- `fan-out-${Date.now()}`,
469
+ release.runId,
424
470
  resolveAutoReleaseDisabled(config, args.noReleaseRecord),
471
+ release.runOrigin,
425
472
  );
426
473
  }
427
474