@intentius/chant 0.124.28 → 0.124.30

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 (449) hide show
  1. package/dist/build-params.d.ts +34 -1
  2. package/dist/build-params.d.ts.map +1 -1
  3. package/dist/build-params.js +31 -1
  4. package/dist/change-set-graph.d.ts +8 -1
  5. package/dist/change-set-graph.d.ts.map +1 -1
  6. package/dist/change-set-graph.js +4 -2
  7. package/dist/change-set.d.ts +10 -0
  8. package/dist/change-set.d.ts.map +1 -1
  9. package/dist/change-set.js +4 -3
  10. package/dist/cli/build-params-cli.d.ts +7 -1
  11. package/dist/cli/build-params-cli.d.ts.map +1 -1
  12. package/dist/cli/build-params-cli.js +17 -8
  13. package/dist/cli/commands/build.d.ts.map +1 -1
  14. package/dist/cli/commands/build.js +6 -3
  15. package/dist/cli/handlers/approve.d.ts.map +1 -1
  16. package/dist/cli/handlers/approve.js +45 -0
  17. package/dist/cli/handlers/build.d.ts.map +1 -1
  18. package/dist/cli/handlers/build.js +18 -4
  19. package/dist/cli/handlers/fan-out.d.ts +9 -0
  20. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  21. package/dist/cli/handlers/fan-out.js +67 -8
  22. package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
  23. package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
  24. package/dist/cli/handlers/lifecycle-gc.js +143 -0
  25. package/dist/cli/handlers/pr.d.ts +11 -3
  26. package/dist/cli/handlers/pr.d.ts.map +1 -1
  27. package/dist/cli/handlers/pr.js +134 -26
  28. package/dist/cli/handlers/promote.d.ts +7 -0
  29. package/dist/cli/handlers/promote.d.ts.map +1 -1
  30. package/dist/cli/handlers/promote.js +210 -33
  31. package/dist/cli/handlers/release.d.ts +62 -5
  32. package/dist/cli/handlers/release.d.ts.map +1 -1
  33. package/dist/cli/handlers/release.js +414 -28
  34. package/dist/cli/handlers/run.d.ts +11 -1
  35. package/dist/cli/handlers/run.d.ts.map +1 -1
  36. package/dist/cli/handlers/run.js +67 -6
  37. package/dist/cli/main.d.ts.map +1 -1
  38. package/dist/cli/main.js +91 -10
  39. package/dist/cli/parse-args.d.ts.map +1 -1
  40. package/dist/cli/parse-args.js +73 -2
  41. package/dist/cli/registry.d.ts +32 -4
  42. package/dist/cli/registry.d.ts.map +1 -1
  43. package/dist/cli/run-events.d.ts +3 -2
  44. package/dist/cli/run-events.d.ts.map +1 -1
  45. package/dist/cli/run-events.js +5 -3
  46. package/dist/components/auto-release.d.ts +2 -0
  47. package/dist/components/auto-release.d.ts.map +1 -1
  48. package/dist/components/auto-release.js +1 -0
  49. package/dist/components/capability.d.ts +9 -0
  50. package/dist/components/capability.d.ts.map +1 -1
  51. package/dist/components/driver.d.ts +8 -0
  52. package/dist/components/driver.d.ts.map +1 -1
  53. package/dist/components/driver.js +35 -0
  54. package/dist/components/fan-out-inputs.d.ts +69 -0
  55. package/dist/components/fan-out-inputs.d.ts.map +1 -0
  56. package/dist/components/fan-out-inputs.js +169 -0
  57. package/dist/components/fan-out-record.d.ts +13 -0
  58. package/dist/components/fan-out-record.d.ts.map +1 -1
  59. package/dist/components/fan-out-record.js +16 -0
  60. package/dist/components/fan-out-run.d.ts.map +1 -1
  61. package/dist/components/fan-out-run.js +50 -0
  62. package/dist/components/fan-out.d.ts +4 -0
  63. package/dist/components/fan-out.d.ts.map +1 -1
  64. package/dist/components/pr-pipeline.d.ts +49 -0
  65. package/dist/components/pr-pipeline.d.ts.map +1 -1
  66. package/dist/components/pr-pipeline.js +38 -4
  67. package/dist/components/pr-run.d.ts +20 -2
  68. package/dist/components/pr-run.d.ts.map +1 -1
  69. package/dist/components/pr-run.js +26 -13
  70. package/dist/components/promote.d.ts +23 -0
  71. package/dist/components/promote.d.ts.map +1 -1
  72. package/dist/components/promote.js +60 -2
  73. package/dist/components/release-compile.d.ts +9 -0
  74. package/dist/components/release-compile.d.ts.map +1 -1
  75. package/dist/components/release-compile.js +9 -3
  76. package/dist/components/release-gate.d.ts +11 -1
  77. package/dist/components/release-gate.d.ts.map +1 -1
  78. package/dist/components/release-gate.js +10 -4
  79. package/dist/components/wave-plan.d.ts +16 -0
  80. package/dist/components/wave-plan.d.ts.map +1 -1
  81. package/dist/components/wave-plan.js +36 -2
  82. package/dist/config.d.ts +38 -0
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js +2 -0
  85. package/dist/flag-params.d.ts +54 -0
  86. package/dist/flag-params.d.ts.map +1 -0
  87. package/dist/flag-params.js +130 -0
  88. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  89. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  90. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  91. package/dist/lifecycle/change-set-store.d.ts +2 -1
  92. package/dist/lifecycle/change-set-store.d.ts.map +1 -1
  93. package/dist/lifecycle/change-set-store.js +2 -1
  94. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  95. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  96. package/dist/lifecycle/delivery-metrics.js +145 -0
  97. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  98. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  99. package/dist/lifecycle/deploy-outcome.js +101 -0
  100. package/dist/lifecycle/gate-change-set.d.ts +10 -1
  101. package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
  102. package/dist/lifecycle/gate-change-set.js +35 -3
  103. package/dist/lifecycle/gate-ledger.d.ts +2 -3
  104. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  105. package/dist/lifecycle/git.d.ts +30 -1
  106. package/dist/lifecycle/git.d.ts.map +1 -1
  107. package/dist/lifecycle/git.js +31 -5
  108. package/dist/lifecycle/index.d.ts +4 -0
  109. package/dist/lifecycle/index.d.ts.map +1 -1
  110. package/dist/lifecycle/index.js +4 -0
  111. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  112. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  113. package/dist/lifecycle/mixed-environment.js +154 -0
  114. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  115. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  116. package/dist/lifecycle/publish-attachments.js +179 -0
  117. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  118. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  119. package/dist/lifecycle/publish-credentials.js +69 -0
  120. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  121. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  122. package/dist/lifecycle/publish-declaration.js +83 -0
  123. package/dist/lifecycle/publish-forge.d.ts +53 -0
  124. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  125. package/dist/lifecycle/publish-forge.js +298 -0
  126. package/dist/lifecycle/publish-npm.d.ts +61 -0
  127. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  128. package/dist/lifecycle/publish-npm.js +242 -0
  129. package/dist/lifecycle/publish-oci.d.ts +64 -0
  130. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  131. package/dist/lifecycle/publish-oci.js +383 -0
  132. package/dist/lifecycle/publish-record.d.ts +66 -0
  133. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  134. package/dist/lifecycle/publish-record.js +84 -0
  135. package/dist/lifecycle/publish-targets.d.ts +45 -0
  136. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  137. package/dist/lifecycle/publish-targets.js +85 -0
  138. package/dist/lifecycle/release-cut.d.ts +48 -1
  139. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  140. package/dist/lifecycle/release-cut.js +116 -23
  141. package/dist/lifecycle/release-join.d.ts +95 -0
  142. package/dist/lifecycle/release-join.d.ts.map +1 -0
  143. package/dist/lifecycle/release-join.js +139 -0
  144. package/dist/lifecycle/release-ledger.d.ts +8 -3
  145. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  146. package/dist/lifecycle/release-ledger.js +2 -0
  147. package/dist/lifecycle/release-publish.d.ts +312 -0
  148. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  149. package/dist/lifecycle/release-publish.js +335 -0
  150. package/dist/lifecycle/release-revocation.d.ts +80 -0
  151. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  152. package/dist/lifecycle/release-revocation.js +123 -0
  153. package/dist/lifecycle/release-store.d.ts +12 -2
  154. package/dist/lifecycle/release-store.d.ts.map +1 -1
  155. package/dist/lifecycle/release-store.js +4 -2
  156. package/dist/lifecycle/release-verify.d.ts +61 -0
  157. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  158. package/dist/lifecycle/release-verify.js +92 -0
  159. package/dist/lifecycle/release-workstation.d.ts +91 -0
  160. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  161. package/dist/lifecycle/release-workstation.js +180 -0
  162. package/dist/lifecycle/retention-policy.d.ts +45 -0
  163. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  164. package/dist/lifecycle/retention-policy.js +43 -0
  165. package/dist/lifecycle/retention.d.ts +185 -0
  166. package/dist/lifecycle/retention.d.ts.map +1 -0
  167. package/dist/lifecycle/retention.js +400 -0
  168. package/dist/lifecycle/run-ledger.d.ts +1 -1
  169. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  170. package/dist/lifecycle/run-ledger.js +9 -0
  171. package/dist/op/activities/activity-contracts.d.ts +1 -0
  172. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  173. package/dist/op/activities/activity-contracts.js +1 -0
  174. package/dist/op/activities/shell.d.ts +6 -0
  175. package/dist/op/activities/shell.d.ts.map +1 -1
  176. package/dist/op/activities/shell.js +1 -1
  177. package/dist/op/local-executor.d.ts +23 -1
  178. package/dist/op/local-executor.d.ts.map +1 -1
  179. package/dist/op/local-executor.js +9 -3
  180. package/dist/op/runtime.d.ts +27 -0
  181. package/dist/op/runtime.d.ts.map +1 -1
  182. package/dist/op/runtime.js +20 -0
  183. package/dist/plan-summary.d.ts.map +1 -1
  184. package/dist/plan-summary.js +8 -2
  185. package/dist/pr-forge.d.ts +23 -0
  186. package/dist/pr-forge.d.ts.map +1 -1
  187. package/dist/pr-forge.js +44 -0
  188. package/dist/pr-loop.d.ts +17 -0
  189. package/dist/pr-loop.d.ts.map +1 -1
  190. package/dist/pr-loop.js +30 -4
  191. package/dist/pr-report-url.d.ts +82 -0
  192. package/dist/pr-report-url.d.ts.map +1 -0
  193. package/dist/pr-report-url.js +174 -0
  194. package/dist/provenance.d.ts +2 -2
  195. package/dist/provenance.d.ts.map +1 -1
  196. package/dist/run-event-store.d.ts +29 -2
  197. package/dist/run-event-store.d.ts.map +1 -1
  198. package/dist/run-event-store.js +50 -3
  199. package/dist/run-event-stream.d.ts +4 -3
  200. package/dist/run-event-stream.d.ts.map +1 -1
  201. package/dist/run-event-stream.js +7 -3
  202. package/dist/run-event.d.ts +34 -1
  203. package/dist/run-event.d.ts.map +1 -1
  204. package/dist/run-event.js +4 -0
  205. package/dist/wave-runtime.mjs +260 -229
  206. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  207. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  208. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  209. package/dist/workspace/change-set.schema.json +5 -1
  210. package/dist/workspace/check.schema.json +4 -2
  211. package/dist/workspace/checks/records.d.ts +9 -1
  212. package/dist/workspace/checks/records.d.ts.map +1 -1
  213. package/dist/workspace/checks/records.js +35 -1
  214. package/dist/workspace/checks.d.ts +1 -0
  215. package/dist/workspace/checks.d.ts.map +1 -1
  216. package/dist/workspace/checks.js +2 -1
  217. package/dist/workspace/ci-green-forge.d.ts +2 -0
  218. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  219. package/dist/workspace/ci-green.d.ts +10 -0
  220. package/dist/workspace/ci-green.d.ts.map +1 -1
  221. package/dist/workspace/ci-green.js +14 -5
  222. package/dist/workspace/ci-run.d.ts +222 -0
  223. package/dist/workspace/ci-run.d.ts.map +1 -0
  224. package/dist/workspace/ci-run.js +464 -0
  225. package/dist/workspace/ci-serve.d.ts +214 -0
  226. package/dist/workspace/ci-serve.d.ts.map +1 -0
  227. package/dist/workspace/ci-serve.js +412 -0
  228. package/dist/workspace/declaration.d.ts +16 -0
  229. package/dist/workspace/declaration.d.ts.map +1 -1
  230. package/dist/workspace/declaration.js +26 -0
  231. package/dist/workspace/declaration.schema.json +64 -0
  232. package/dist/workspace/declared-kinds.d.ts +16 -0
  233. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  234. package/dist/workspace/declared-kinds.js +25 -0
  235. package/dist/workspace/delivery-metrics.d.ts +91 -0
  236. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  237. package/dist/workspace/delivery-metrics.js +217 -0
  238. package/dist/workspace/delivery-metrics.schema.json +519 -0
  239. package/dist/workspace/flags-read.d.ts +37 -0
  240. package/dist/workspace/flags-read.d.ts.map +1 -0
  241. package/dist/workspace/flags-read.js +103 -0
  242. package/dist/workspace/flags.d.ts +100 -0
  243. package/dist/workspace/flags.d.ts.map +1 -0
  244. package/dist/workspace/flags.js +98 -0
  245. package/dist/workspace/graph-cli.js +1 -1
  246. package/dist/workspace/intent-cli.d.ts +11 -2
  247. package/dist/workspace/intent-cli.d.ts.map +1 -1
  248. package/dist/workspace/intent-cli.js +54 -5
  249. package/dist/workspace/intent-trace.d.ts +219 -0
  250. package/dist/workspace/intent-trace.d.ts.map +1 -0
  251. package/dist/workspace/intent-trace.js +314 -0
  252. package/dist/workspace/intent.d.ts +30 -6
  253. package/dist/workspace/intent.d.ts.map +1 -1
  254. package/dist/workspace/intent.js +141 -21
  255. package/dist/workspace/intent.schema.json +702 -6
  256. package/dist/workspace/overlap.d.ts +146 -0
  257. package/dist/workspace/overlap.d.ts.map +1 -0
  258. package/dist/workspace/overlap.js +276 -0
  259. package/dist/workspace/pr-report.schema.json +5 -0
  260. package/dist/workspace/reason-codes.d.ts +2 -0
  261. package/dist/workspace/reason-codes.d.ts.map +1 -1
  262. package/dist/workspace/reason-codes.js +3 -0
  263. package/dist/workspace/records.d.ts +15 -2
  264. package/dist/workspace/records.d.ts.map +1 -1
  265. package/dist/workspace/records.js +32 -0
  266. package/dist/workspace/records.schema.json +21 -2
  267. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  268. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  269. package/dist/workspace/releases.d.ts +9 -0
  270. package/dist/workspace/releases.d.ts.map +1 -1
  271. package/dist/workspace/releases.js +9 -0
  272. package/dist/workspace/releases.schema.json +123 -1
  273. package/dist/workspace/run-event.schema.json +24 -6
  274. package/dist/workspace/status-gates.d.ts +8 -0
  275. package/dist/workspace/status-gates.d.ts.map +1 -1
  276. package/dist/workspace/status-stewards.d.ts +2 -0
  277. package/dist/workspace/status-stewards.d.ts.map +1 -1
  278. package/dist/workspace/status-stewards.js +1 -0
  279. package/dist/workspace/status.d.ts +78 -0
  280. package/dist/workspace/status.d.ts.map +1 -1
  281. package/dist/workspace/status.js +170 -3
  282. package/dist/workspace/status.schema.json +210 -2
  283. package/dist/workspace/wip.d.ts.map +1 -1
  284. package/dist/workspace/wip.js +11 -3
  285. package/package.json +1 -1
  286. package/src/build-params.test.ts +46 -0
  287. package/src/build-params.ts +55 -1
  288. package/src/change-set-graph.ts +10 -2
  289. package/src/change-set.ts +14 -3
  290. package/src/cli/build-params-cli.ts +22 -9
  291. package/src/cli/commands/build.ts +6 -3
  292. package/src/cli/handlers/approve-release.test.ts +5 -1
  293. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  294. package/src/cli/handlers/approve.ts +47 -0
  295. package/src/cli/handlers/build.test.ts +30 -0
  296. package/src/cli/handlers/build.ts +20 -4
  297. package/src/cli/handlers/fan-out.test.ts +262 -1
  298. package/src/cli/handlers/fan-out.ts +72 -8
  299. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  300. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  301. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  302. package/src/cli/handlers/pr.test.ts +148 -1
  303. package/src/cli/handlers/pr.ts +144 -28
  304. package/src/cli/handlers/promote.test.ts +280 -2
  305. package/src/cli/handlers/promote.ts +210 -34
  306. package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
  307. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  308. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  309. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  310. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  311. package/src/cli/handlers/release.e2e.test.ts +146 -32
  312. package/src/cli/handlers/release.ts +415 -28
  313. package/src/cli/handlers/run-auto-release.test.ts +9 -0
  314. package/src/cli/handlers/run-release.test.ts +55 -0
  315. package/src/cli/handlers/run.ts +69 -5
  316. package/src/cli/main.test.ts +1 -0
  317. package/src/cli/main.ts +91 -10
  318. package/src/cli/parse-args.ts +54 -2
  319. package/src/cli/registry.ts +32 -4
  320. package/src/cli/run-events.ts +5 -2
  321. package/src/components/auto-release.test.ts +9 -0
  322. package/src/components/auto-release.ts +3 -0
  323. package/src/components/capability.ts +9 -0
  324. package/src/components/driver.test.ts +24 -0
  325. package/src/components/driver.ts +36 -0
  326. package/src/components/fan-out-inputs.test.ts +85 -0
  327. package/src/components/fan-out-inputs.ts +192 -0
  328. package/src/components/fan-out-record.test.ts +10 -0
  329. package/src/components/fan-out-record.ts +19 -0
  330. package/src/components/fan-out-run.ts +48 -0
  331. package/src/components/fan-out.ts +4 -0
  332. package/src/components/pr-pipeline.ts +67 -4
  333. package/src/components/pr-run.test.ts +127 -0
  334. package/src/components/pr-run.ts +32 -12
  335. package/src/components/promote.ts +68 -2
  336. package/src/components/release-compile.ts +17 -3
  337. package/src/components/release-gate.ts +18 -4
  338. package/src/components/wave-plan.ts +45 -2
  339. package/src/config.ts +8 -0
  340. package/src/flag-params.test.ts +144 -0
  341. package/src/flag-params.ts +134 -0
  342. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  343. package/src/lifecycle/change-set-store.ts +2 -1
  344. package/src/lifecycle/delivery-metrics.ts +263 -0
  345. package/src/lifecycle/deploy-outcome.ts +147 -0
  346. package/src/lifecycle/gate-change-set.ts +37 -6
  347. package/src/lifecycle/gate-ledger.ts +2 -2
  348. package/src/lifecycle/git.ts +61 -8
  349. package/src/lifecycle/index.ts +4 -0
  350. package/src/lifecycle/mixed-environment.test.ts +97 -0
  351. package/src/lifecycle/mixed-environment.ts +204 -0
  352. package/src/lifecycle/publish-attachments.ts +196 -0
  353. package/src/lifecycle/publish-credentials.ts +84 -0
  354. package/src/lifecycle/publish-declaration.ts +95 -0
  355. package/src/lifecycle/publish-forge.ts +325 -0
  356. package/src/lifecycle/publish-npm.ts +259 -0
  357. package/src/lifecycle/publish-oci.ts +396 -0
  358. package/src/lifecycle/publish-record.ts +113 -0
  359. package/src/lifecycle/publish-resume.test.ts +168 -0
  360. package/src/lifecycle/publish-targets.test.ts +494 -0
  361. package/src/lifecycle/publish-targets.ts +100 -0
  362. package/src/lifecycle/release-cut.test.ts +74 -2
  363. package/src/lifecycle/release-cut.ts +158 -22
  364. package/src/lifecycle/release-flag.test.ts +112 -0
  365. package/src/lifecycle/release-join.test.ts +68 -0
  366. package/src/lifecycle/release-join.ts +182 -0
  367. package/src/lifecycle/release-ledger.ts +9 -3
  368. package/src/lifecycle/release-publish.test.ts +225 -0
  369. package/src/lifecycle/release-publish.ts +568 -0
  370. package/src/lifecycle/release-revocation.ts +147 -0
  371. package/src/lifecycle/release-store.ts +15 -4
  372. package/src/lifecycle/release-verify.test.ts +110 -0
  373. package/src/lifecycle/release-verify.ts +120 -0
  374. package/src/lifecycle/release-workstation.test.ts +204 -0
  375. package/src/lifecycle/release-workstation.ts +216 -0
  376. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  377. package/src/lifecycle/retention-policy.ts +63 -0
  378. package/src/lifecycle/retention.test.ts +363 -0
  379. package/src/lifecycle/retention.ts +508 -0
  380. package/src/lifecycle/run-ledger.ts +7 -1
  381. package/src/op/activities/activity-contracts.ts +1 -0
  382. package/src/op/activities/shell.ts +7 -1
  383. package/src/op/local-executor.ts +27 -4
  384. package/src/op/runtime.ts +42 -0
  385. package/src/plan-summary.test.ts +20 -0
  386. package/src/plan-summary.ts +8 -1
  387. package/src/pr-forge.test.ts +25 -0
  388. package/src/pr-forge.ts +62 -0
  389. package/src/pr-loop.test.ts +13 -1
  390. package/src/pr-loop.ts +41 -4
  391. package/src/pr-report-url.test.ts +131 -0
  392. package/src/pr-report-url.ts +209 -0
  393. package/src/provenance.ts +2 -2
  394. package/src/run-event-store.ts +63 -3
  395. package/src/run-event-stream.ts +8 -4
  396. package/src/run-event.ts +38 -1
  397. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  398. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  399. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  400. package/src/workspace/change-set.schema.json +5 -1
  401. package/src/workspace/check.schema.json +4 -2
  402. package/src/workspace/checks/records.ts +35 -1
  403. package/src/workspace/checks.ts +2 -1
  404. package/src/workspace/ci-green-forge.ts +2 -0
  405. package/src/workspace/ci-green.ts +20 -5
  406. package/src/workspace/ci-run.test.ts +416 -0
  407. package/src/workspace/ci-run.ts +571 -0
  408. package/src/workspace/ci-serve.test.ts +268 -0
  409. package/src/workspace/ci-serve.ts +521 -0
  410. package/src/workspace/declaration.schema.json +64 -0
  411. package/src/workspace/declaration.test.ts +28 -0
  412. package/src/workspace/declaration.ts +38 -0
  413. package/src/workspace/declared-kinds.ts +40 -1
  414. package/src/workspace/delivery-metrics.schema.json +519 -0
  415. package/src/workspace/delivery-metrics.test.ts +236 -0
  416. package/src/workspace/delivery-metrics.ts +286 -0
  417. package/src/workspace/flags-read.ts +121 -0
  418. package/src/workspace/flags.test.ts +141 -0
  419. package/src/workspace/flags.ts +157 -0
  420. package/src/workspace/graph-cli.ts +1 -1
  421. package/src/workspace/intent-cli.ts +46 -5
  422. package/src/workspace/intent-trace.test.ts +173 -0
  423. package/src/workspace/intent-trace.ts +484 -0
  424. package/src/workspace/intent.schema.json +702 -6
  425. package/src/workspace/intent.ts +148 -25
  426. package/src/workspace/older-records.test.ts +412 -0
  427. package/src/workspace/overlap.test.ts +165 -0
  428. package/src/workspace/overlap.ts +340 -0
  429. package/src/workspace/pr-report.schema.json +5 -0
  430. package/src/workspace/read-contract.e2e.test.ts +2 -1
  431. package/src/workspace/reason-codes.test.ts +5 -0
  432. package/src/workspace/reason-codes.ts +3 -0
  433. package/src/workspace/record-assets.test.ts +2 -1
  434. package/src/workspace/records-contract.e2e.test.ts +2 -1
  435. package/src/workspace/records.schema.json +21 -2
  436. package/src/workspace/records.ts +36 -2
  437. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  438. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  439. package/src/workspace/releases.schema.json +123 -1
  440. package/src/workspace/releases.ts +11 -0
  441. package/src/workspace/run-event.schema.json +24 -6
  442. package/src/workspace/status-contract.test.ts +29 -0
  443. package/src/workspace/status-gates.ts +8 -0
  444. package/src/workspace/status-mixed.test.ts +152 -0
  445. package/src/workspace/status-revoked.test.ts +153 -0
  446. package/src/workspace/status-stewards.ts +3 -0
  447. package/src/workspace/status.schema.json +210 -2
  448. package/src/workspace/status.ts +254 -3
  449. package/src/workspace/wip.ts +11 -2
@@ -15,7 +15,7 @@ import { pointAnswerCommand, renderHuman, renderJson } from "../../op/local-outp
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";
@@ -470,7 +470,13 @@ async function runOpLogOnRuntime(ctx: CommandContext, name: string): Promise<num
470
470
  export function humanRunEventLine(e: RunEvent): string {
471
471
  const x = e as unknown as Record<string, unknown>;
472
472
  const where = [x.component, x.phase, x.step].filter((v): v is string => typeof v === "string").join("/");
473
- const detail = [where, typeof x.status === "string" ? x.status : "", typeof x.error === "string" ? x.error : ""].filter(Boolean).join(" ");
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(" ");
474
480
  return `${String(x.at ?? "")} ${String(x.type).padEnd(16)}${detail}`.trimEnd();
475
481
  }
476
482
 
@@ -926,6 +932,7 @@ export async function recordAutoReleasesForRun(
926
932
  runId: string,
927
933
  disabled: boolean,
928
934
  runOrigin: RunOrigin = { forge: "local" },
935
+ releaseDigest?: string,
929
936
  ): Promise<ReleaseRecord[]> {
930
937
  const recorded: ReleaseRecord[] = [];
931
938
  for (const componentResult of results) {
@@ -942,6 +949,8 @@ export async function recordAutoReleasesForRun(
942
949
  // #1035, #4020: who approved at the gate and which resolution admitted the deploy.
943
950
  ...(gateApprover(componentResult) ? { approver: gateApprover(componentResult) } : {}),
944
951
  ...(gateAdmission(componentResult) ? { admission: gateAdmission(componentResult) } : {}),
952
+ // #4043: the release the deploy was made under, when `--release` named one.
953
+ ...(releaseDigest !== undefined ? { releaseDigest } : {}),
945
954
  },
946
955
  { disabled },
947
956
  );
@@ -1051,6 +1060,44 @@ function refusesNoRecordOn(runtime: Pick<OpRuntimeProvider, "name">): boolean {
1051
1060
  return true;
1052
1061
  }
1053
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
+
1054
1101
  async function runComponentSelection(ctx: CommandContext): Promise<number> {
1055
1102
  const selector = ctx.args.path;
1056
1103
  if (!selector || selector === ".") {
@@ -1066,11 +1113,13 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1066
1113
 
1067
1114
  const projectPath = resolve(".");
1068
1115
  const { config } = await loadChantConfig(projectPath).catch(() => ({ config: {} as ChantConfig }));
1069
- 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, {
1070
1119
  cli: parseParamFlags(ctx.args.param),
1071
1120
  paramsFile: ctx.args.paramsFile,
1072
1121
  verbose: ctx.args.verbose,
1073
- });
1122
+ }, ctx.args.env);
1074
1123
  if (!paramsResolution.success) {
1075
1124
  for (const message of paramsResolution.errors) console.error(message);
1076
1125
  return 1;
@@ -1088,6 +1137,20 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1088
1137
  }
1089
1138
 
1090
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
+ }
1091
1154
  // Seed cross-component/cross-stack outputs from upstream jobs' dumped files
1092
1155
  // (`--seed-outputs`), so a single-component run resolves references to a
1093
1156
  // component that ran in an earlier CI job.
@@ -1207,7 +1270,8 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1207
1270
  if (result.success && result.run) {
1208
1271
  // #3955: a run that records nothing has no release to report either.
1209
1272
  const disabled = resolveAutoReleaseDisabled(config, ctx.args.noReleaseRecord) || ledgerRecordingOff();
1210
- const recorded = await recordAutoReleasesForRun(result.run.results, env, release.runId, disabled, release.runOrigin);
1273
+ const recorded = await recordAutoReleasesForRun(result.run.results, env, release.runId, disabled, release.runOrigin, underRelease?.releaseDigest);
1274
+ if (underRelease) await warnOnArtifactMismatch(underRelease.release, recorded);
1211
1275
  // `--digest-file` (#2602): the releases this run recorded, one
1212
1276
  // `<component>=<digest>` line each, the form `chant components promote
1213
1277
  // --digest` takes. A generated promote job reads it to promote exactly
@@ -828,6 +828,7 @@ describe("workspace init and ls (#2534)", () => {
828
828
  "workspace ls",
829
829
  "workspace member",
830
830
  "workspace member-run",
831
+ "workspace metrics",
831
832
  "workspace patch",
832
833
  "workspace pin",
833
834
  "workspace points",
package/src/cli/main.ts CHANGED
@@ -249,6 +249,33 @@ CI:
249
249
  chant/lifecycle that a runner key signed. Pushes the
250
250
  tags. --key, or the PEM in CHANT_RUNNER_KEY, signs
251
251
  each tag with a runner key.
252
+ ci run plan|apply --env <env> --base <sha> [--pr <n>] [--commit <sha>]
253
+ [--forge github|gitlab|forgejo] [--gate <name>] [--branch <name>]
254
+ [--events-json] [--report-url <url>] [--image <image>]
255
+ [--before-script <line>]... [--pr-build]
256
+ [--pr-build-command <line>] [--chant <command>]
257
+ [--pass-env <NAME>]... [--key <runner key.pem>] [-o <dir>]
258
+ Run the pull-request pipeline's plan or apply job as
259
+ a pipeline Op in a fresh worktree of --commit (default
260
+ HEAD) with a clean environment. --base and --pr stand
261
+ in for the forge's event values. --pr-build runs
262
+ the job's build and lint lines first. Records the run as
263
+ pr-plan or pr-apply on chant/lifecycle, signed with
264
+ --key or CHANT_RUNNER_KEY, so ci tick from runs counts
265
+ it, and copies the report to -o (default .chant/pr).
266
+ ci serve --forge forgejo|github --repo <owner/repo> --env <env>
267
+ [--interval <s>] [--max-jobs <n>] [--state <dir>]
268
+ [--author <login>]... [--once] [--rerun-failed]
269
+ [ci run's job flags]
270
+ Poll the forge for open pull requests and the target
271
+ branch's head, and run ci run plan for each new pull
272
+ request head and ci run apply for each new commit on
273
+ the branch. Dedupes on job and commit in a state file
274
+ under --state, takes a lease per apply group, keeps
275
+ each apply's resume record there, and runs at most
276
+ --max-jobs (default 1) jobs at once. --once runs one
277
+ poll and waits for its jobs. --rerun-failed runs a
278
+ failed apply or plan again.
252
279
  ci workflow [--workflow <name>]... [--output <file>] Write
253
280
  [--chant <command>] [--install <command>]
254
281
  [--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>]
@@ -263,12 +290,18 @@ CI:
263
290
  signs its tags with.
264
291
 
265
292
  Releases:
266
- release cut [<label>] [--build <command>] [--param <k=v>] [--params-file <file>]
267
- [--src <dir>] [-o <file>] [--lexicon <name>] [--sign [<key file>]]
268
- Cut a release of this member from HEAD, which must
269
- carry a ci/green tag: compile every environment in
270
- the config's environments, build the artifacts once,
271
- and store it on chant/lifecycle under its digest.
293
+ release cut [<label>] [--yes] [--build <command>] [--param <k=v>]
294
+ [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>]
295
+ [--actor <name>] [--sign [<key file>]] [--json]
296
+ A dry run of a cut of this member from HEAD, which
297
+ must be clean, on trunk and pushed, and carry a
298
+ ci/green tag: compile every environment in the
299
+ config's environments and print what the cut would
300
+ build, record and tag, and what publishes where.
301
+ --yes cuts: builds the artifacts once, stores the
302
+ release on chant/lifecycle under its digest, records
303
+ the actor and run id, tags the commit release/<label>
304
+ here, and prints the next command.
272
305
  --build runs before each environment's templates are
273
306
  read, with CHANT_ENV set; {env} in --params-file is
274
307
  replaced by the environment. --sign signs the cut with
@@ -277,6 +310,21 @@ Releases:
277
310
  what chant build --env <env> writes: --src <dir> is
278
311
  the build root, -o <file> the output (dist/build.yaml),
279
312
  --lexicon narrows it.
313
+ release publish <label|digest> [--yes] [--allow-unsigned]
314
+ [--require-provenance] [--json]
315
+ Publish a cut release to the targets release.publish
316
+ in chant.config.ts declares (npm, OCI registries,
317
+ forge release assets), each with an SBOM and the
318
+ release's provenance. A dry run without --yes.
319
+ Refuses a revoked release, one whose signature does
320
+ not verify against .chant/allowed_signers unless
321
+ --allow-unsigned, and with a target a checkout that
322
+ is not the release's commit. Warns first when the run
323
+ has no OIDC token, so what it publishes carries no
324
+ OIDC provenance; --require-provenance refuses instead.
325
+ release verify <label|digest> [--json]
326
+ Check, offline, that the release was signed by a key
327
+ .chant/allowed_signers lists at the release's commit.
280
328
  release list [--json] Every release of this member with its targets, its
281
329
  cuts and the environments that run it.
282
330
  release export <label|digest> --env <env> [-o <dir>] [--json]
@@ -344,7 +392,9 @@ Ops:
344
392
  run, and prints that order first (#3950).
345
393
  On success, auto-emits a release-ledger record per
346
394
  component that published a digest (default: on;
347
- --no-release-record to opt out; #597)
395
+ --no-release-record to opt out; #597).
396
+ --release <label|digest>: the run is of that release,
397
+ cut from this checkout's commit; the records name it (#4043)
348
398
  --progress-json: stream one NDJSON RunProgressEvent
349
399
  per line to stdout while the run executes, for a
350
400
  consumer to render live wave/component/phase/step
@@ -443,6 +493,13 @@ Workspace (level 1, #2524):
443
493
  beside it and marks the members whose digests differ.
444
494
  Read only; never fetches. --json prints the
445
495
  read-contract document
496
+ workspace metrics [dir] [--env <env>] [--member <name>] [--since <date>] [--json]
497
+ The four DORA metrics per member and environment
498
+ (#4039): deployment frequency, lead time from commit
499
+ to running release, change failure rate and time to
500
+ restore, from the deploy records, releases and ci
501
+ revoked tags. Eight weeks back by default. Read only;
502
+ never fetches. --json prints the read-contract document
446
503
  workspace records [--kind <kind file>] [--current] [--uncommitted | --at <rev>] [--base <rev>] [--require attested] [--json]
447
504
  Without --kind, every record kind the declaration
448
505
  names. Read the records a record kind locates, validated
@@ -757,7 +814,7 @@ Workspace (level 1, #2524):
757
814
  Each composite instance the members declare, with the
758
815
  components whose contract can deploy it; an instance
759
816
  with none lists an empty set
760
- workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]
817
+ workspace graph --intent <path[:start-end]|path#symbol|member/node|sha> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]
761
818
  The intent graph over one region: the commits that
762
819
  touched it, the decisions whose constrains cover it,
763
820
  the artifacts they pin, and findings with closed codes.
@@ -796,6 +853,13 @@ Lifecycle (alias: lc):
796
853
  executes the plan (production-like names also need
797
854
  --confirm-prod, or an interactive confirmation)
798
855
  lifecycle log [env] History of lifecycle snapshots
856
+ lifecycle gc [--yes] [--push] [--json]
857
+ Prune stored change sets, releases and run event
858
+ files by the retention policy (#4036). A dry run
859
+ without --yes; --yes writes one commit removing
860
+ them on the local chant/lifecycle and deletes the
861
+ run event files; --push pushes it. History is
862
+ never rewritten, and the ledgers are never pruned
799
863
 
800
864
  Plan scenarios (#1292):
801
865
  scenario check Evaluate every declared Scenario's expect clause
@@ -834,7 +898,11 @@ Component release ledger + status:
834
898
  --member <name>, both commands: a workspace
835
899
  member's loop, gate op pr-<n>-<member>;
836
900
  --resume <file> finishes an apply that failed
837
- partway under the same approval)
901
+ partway under the same approval;
902
+ --events-json, both commands: the run-event
903
+ stream on stdout; --report-url <s3 or file URL>,
904
+ both commands: copy the report, the change set
905
+ and the events to <url>/<pr>/<commit>/)
838
906
  components release <env> Append one immutable release record
839
907
  (--component <name> --digest <sha256:...>
840
908
  [--git-sha <sha>] [--run-id <id>] [--actor <name>]);
@@ -858,7 +926,9 @@ Component release ledger + status:
858
926
  components rollback <env> Redeploy an earlier release of an environment
859
927
  from its recorded digest (--component <name>
860
928
  [--digest <sha256:...>]; defaults to the release
861
- before the current one; --dry-run prints the plan)
929
+ before the current one; --dry-run prints the plan).
930
+ --release <label|digest> restores every component
931
+ of that release through the release gate (#4035)
862
932
  components redeploy <env> Redeploy the release an environment's ledger
863
933
  records as current, after a deploy that failed
864
934
  partway (--component <name> [--digest <sha256:...>];
@@ -1263,8 +1333,15 @@ export const commandRegistry: CommandDef[] = [
1263
1333
  { name: "ci list", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiList(ctx) },
1264
1334
  { name: "ci tick", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiTick(ctx) },
1265
1335
  { name: "ci workflow", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-green-cli")).runCiWorkflow(ctx) },
1336
+ // #4094: one pull-request job as a pipeline Op in a fresh checkout, the chant runner's first slice.
1337
+ { name: "ci run", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-run")).runCiRun(ctx) },
1338
+ { name: "ci serve", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-serve")).runCiServe(ctx) },
1266
1339
  // #4026: the release cut from a green commit (ws-104). Imported on first use.
1267
1340
  { name: "release cut", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCut(ctx) },
1341
+ // #4027: publish a cut release, to the targets #4047 declares.
1342
+ { name: "release publish", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleasePublish(ctx) },
1343
+ // #4049: check a release's signature offline.
1344
+ { name: "release verify", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseVerify(ctx) },
1268
1345
  { name: "release list", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseList(ctx) },
1269
1346
  { name: "release export", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseExport(ctx) },
1270
1347
  { name: "release compile", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCompile(ctx) },
@@ -1323,6 +1400,8 @@ export const commandRegistry: CommandDef[] = [
1323
1400
  { name: "workspace work", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/work-cli")).runWorkspaceWork(ctx) },
1324
1401
  // #3033 — the agent run record: what each run cost and which commits it made. chant records runs and never starts one.
1325
1402
  { name: "workspace runs", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/runs-cli")).runWorkspaceRuns(ctx) },
1403
+ // #4039 — DORA metrics from the deploy, release and revocation records.
1404
+ { name: "workspace metrics", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/delivery-metrics")).runWorkspaceMetrics(ctx) },
1326
1405
  { name: "workspace status", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/status")).runWorkspaceStatus(ctx) },
1327
1406
  { name: "workspace lineage", handler: async (ctx) => (await import("../workspace/lineage-cli")).runWorkspaceLineage(ctx) },
1328
1407
  { name: "workspace upgrade", handler: async (ctx) => (await import("../workspace/lineage-upgrade-cli")).runWorkspaceUpgrade(ctx) },
@@ -1373,6 +1452,8 @@ export const commandRegistry: CommandDef[] = [
1373
1452
  { name: "lifecycle whoami", requiresPlugins: true, handler: runLifecycleWhoami },
1374
1453
  { name: "lifecycle teardown", requiresPlugins: true, handler: runLifecycleTeardown },
1375
1454
  { name: "lifecycle log", handler: runLifecycleLog },
1455
+ // #4036 — retention's garbage collection. Imported on first use.
1456
+ { name: "lifecycle gc", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/lifecycle-gc")).runLifecycleGc(ctx) },
1376
1457
 
1377
1458
  // Plan scenarios (#1292) — fully offline, no plugin network calls, but
1378
1459
  // requiresPlugins:true so the build step has serializers to partition
@@ -42,6 +42,7 @@ const BOOLEAN_FLAGS = new Set([
42
42
  "--composites",
43
43
  "--stacks",
44
44
  "--components",
45
+ "--pr-build",
45
46
  "--up",
46
47
  "--down",
47
48
  "--include-dependents",
@@ -64,6 +65,7 @@ const BOOLEAN_FLAGS = new Set([
64
65
  "--yes",
65
66
  "--confirm-prod",
66
67
  "--once",
68
+ "--rerun-failed",
67
69
  "--expire",
68
70
  "--check-live",
69
71
  "--check-snapshot",
@@ -74,12 +76,15 @@ const BOOLEAN_FLAGS = new Set([
74
76
  "--uncommitted",
75
77
  "--allow-code",
76
78
  "--allow-skip",
79
+ "--allow-unsigned",
80
+ "--require-provenance",
77
81
  "--root-only",
78
82
  "--generated",
79
83
  "--decide",
80
84
  "--record-plans",
81
85
  "--loopback",
82
86
  "--artifacts",
87
+ "--push",
83
88
  ]);
84
89
 
85
90
  /**
@@ -248,6 +253,9 @@ export function parseArgs(args: string[]): ParsedArgs {
248
253
  result.dryRun = true;
249
254
  } else if (arg === "--yes") {
250
255
  result.yes = true;
256
+ } else if (arg === "--push") {
257
+ // `chant lifecycle gc --yes --push` (#4036): push the prune commit.
258
+ result.push = true;
251
259
  } else if (arg === "--confirm-prod") {
252
260
  result.confirmProd = true;
253
261
  } else if (arg === "--strict") {
@@ -375,8 +383,26 @@ export function parseArgs(args: string[]): ParsedArgs {
375
383
  if (!result.forge || result.forge.startsWith("-")) throw new Error("--forge needs a forge: --forge github|gitlab|forgejo");
376
384
  } else if (arg === "--require-review") {
377
385
  result.requireReview = true;
386
+ } else if (arg === "--report-url") {
387
+ // `chant components pr-plan|pr-apply --report-url <url>` (#4007).
388
+ const url = args[++i];
389
+ if (!url || url.startsWith("-")) throw new Error("--report-url needs a URL: --report-url s3://<bucket>/<prefix> or file://<directory>");
390
+ result.reportUrl = url;
391
+ } else if (arg === "--allow-unsigned") {
392
+ // `chant release publish --allow-unsigned` (#4049).
393
+ result.allowUnsigned = true;
394
+ } else if (arg === "--require-provenance") {
395
+ // `chant release publish --require-provenance` (#4049).
396
+ result.requireProvenance = true;
378
397
  } else if (arg === "--pr-loop") {
379
398
  result.prLoop = true;
399
+ } else if (arg === "--pr-build") {
400
+ // `chant build --pr-loop --pr-build` (#4088), and `chant ci run --pr-build` (#4094).
401
+ result.prBuild = true;
402
+ } else if (arg === "--pr-build-command") {
403
+ const line = args[++i];
404
+ if (line === undefined || line === "") throw new Error("--pr-build-command needs a shell line: --pr-build-command '<line>'");
405
+ result.prBuildCommand = line;
380
406
  } else if (arg === "--before-script") {
381
407
  const line = args[++i];
382
408
  if (line === undefined || line === "") throw new Error("--before-script needs a shell line: --before-script '<line>'");
@@ -427,11 +453,11 @@ export function parseArgs(args: string[]): ParsedArgs {
427
453
  // `chant workspace graph --composites` (#2662)
428
454
  result.composites = true;
429
455
  } else if (arg === "--intent") {
430
- // `chant workspace graph --intent <path[:start-end]|path#symbol>` (#2651), or `--intent --record <id>` with no region.
456
+ // `chant workspace graph --intent <path[:start-end]|path#symbol|member/node|sha>` (#2651), or `--intent --record <id>` with no region.
431
457
  const value = args[i + 1];
432
458
  if (value !== undefined && !value.startsWith("-")) result.intent = args[++i];
433
459
  else if (args.includes("--record")) result.intent = "";
434
- else throw new Error("--intent needs a region: --intent <path[:start-end]|path#symbol>, or --intent --record <id>");
460
+ else throw new Error("--intent needs a region: --intent <path[:start-end]|path#symbol|member/node|sha>, or --intent --record <id>");
435
461
  } else if (arg === "--follow-squash") {
436
462
  // `chant workspace graph --intent ... --follow-squash` and `chant workspace runs --follow-squash` (#3035)
437
463
  result.followSquash = true;
@@ -501,6 +527,32 @@ export function parseArgs(args: string[]): ParsedArgs {
501
527
  // `chant ci workflow --chant <command>` (#3573): what the tick step runs chant with.
502
528
  result.ciChant = args[++i];
503
529
  if (!result.ciChant || result.ciChant.startsWith("-")) throw new Error("--chant needs the command that runs chant, such as \"npx tsx packages/core/src/cli/main.ts\": --chant <command>");
530
+ } else if (arg === "--commit") {
531
+ // `chant ci run plan|apply --commit <sha>` (#4094): the commit the job runs at.
532
+ result.commit = args[++i];
533
+ if (!result.commit || result.commit.startsWith("-")) throw new Error("--commit needs the commit the job runs at: --commit <sha>");
534
+ } else if (arg === "--pass-env") {
535
+ // `chant ci run --pass-env <NAME>`, repeatable (#4094): a variable of the runner's environment the job gets.
536
+ const name = args[++i];
537
+ if (!name || name.startsWith("-")) throw new Error("--pass-env needs a variable's name, such as AWS_PROFILE: --pass-env <NAME>");
538
+ (result.passEnv ??= []).push(name);
539
+ } else if (arg === "--repo") {
540
+ // `chant ci serve --repo <owner/repo>` (#4095): the repository whose pull requests and target branch it polls.
541
+ result.ciRepo = args[++i];
542
+ if (!result.ciRepo || !/^[^/\s-][^/\s]*\/[^/\s]+$/.test(result.ciRepo)) throw new Error("--repo needs the repository as owner/name: --repo <owner/repo>");
543
+ } else if (arg === "--max-jobs") {
544
+ // `chant ci serve --max-jobs <n>` (#4095): how many jobs run at once.
545
+ const n = Number(args[++i]);
546
+ if (!Number.isInteger(n) || n < 1) throw new Error("--max-jobs needs a whole number of jobs, 1 or more: --max-jobs <n>");
547
+ result.maxJobs = n;
548
+ } else if (arg === "--author") {
549
+ // `chant ci serve --author <login>`, repeatable (#4095): plan only pull requests these people opened.
550
+ const login = args[++i];
551
+ if (!login || login.startsWith("-")) throw new Error("--author needs a forge login: --author <login>");
552
+ (result.ciAuthors ??= []).push(login);
553
+ } else if (arg === "--rerun-failed") {
554
+ // `chant ci serve --rerun-failed` (#4095): run again the failed apply at the target branch's head, and failed plans of open heads.
555
+ result.rerunFailed = true;
504
556
  } else if (arg === "--install") {
505
557
  // `chant ci workflow --install <command>` (#3573): a step before the tick that installs what --chant runs.
506
558
  result.ciInstall = args[++i];
@@ -149,6 +149,10 @@ export interface ParsedArgs {
149
149
  allowCode?: boolean;
150
150
  /** `chant components promote --allow-skip` (#4030): promote past a step of the member's declared promotion order. */
151
151
  allowSkip?: boolean;
152
+ /** `chant release publish --allow-unsigned` (#4049): publish a release with no signature that verifies, with a warning. Never overrides identity.gates.release. */
153
+ allowUnsigned?: boolean;
154
+ /** `chant release publish --require-provenance` (#4049): refuse a run with no OIDC token, where it warns. */
155
+ requireProvenance?: boolean;
152
156
  /** `chant workspace upgrade --source <repo>[#<member>]` (#2551): move the scope to another template. */
153
157
  source?: string;
154
158
  /** `chant workspace adopt-lineage --tags <glob>` and `hash-index --tags` (#2551): the tags to compare. */
@@ -164,6 +168,8 @@ export interface ParsedArgs {
164
168
  /** `chant lifecycle teardown <env> --yes` — execute the planned deletion
165
169
  * (#1222). Without it the command plans and stops. */
166
170
  yes?: boolean;
171
+ /** `chant lifecycle gc --yes --push` (#4036): push the commit the prune wrote to chant/lifecycle. Without it the prune stays local. */
172
+ push?: boolean;
167
173
  /** `chant lifecycle teardown <env> --yes --confirm-prod` — the non-interactive
168
174
  * form of the extra confirmation a production-like environment name demands
169
175
  * (#1222). Meaningless without `--yes`. */
@@ -308,12 +314,22 @@ export interface ParsedArgs {
308
314
  * request. Needs `--forge`.
309
315
  */
310
316
  requireReview?: boolean;
317
+ /**
318
+ * `chant components pr-plan|pr-apply --report-url <url>` (#4007) — copy
319
+ * the stage's report, change set and run events to `<url>/<pr>/<commit>/`,
320
+ * an `s3://` or `file://` URL, the events as each wave and component settles.
321
+ */
322
+ reportUrl?: string;
311
323
  /**
312
324
  * `chant build --components --generate <lexicon> --pr-loop` (#3183) —
313
325
  * generate the pull-request pipeline: plan on the pull request, apply on
314
326
  * merge bound to the approved digest.
315
327
  */
316
328
  prLoop?: boolean;
329
+ /** `chant build --pr-loop --pr-build` (#4088) — build and lint before pr-plan and pr-apply. */
330
+ prBuild?: boolean;
331
+ /** `chant build --pr-loop --pr-build-command <line>` (#4088) — the build line to run in place of `chant build --env <env>`. */
332
+ prBuildCommand?: string;
317
333
  /**
318
334
  * `chant components fan-out --resume <file>` (#2420) — the attempt record
319
335
  * this fan-out reads before it starts and writes when it finishes. Repeating
@@ -404,7 +420,7 @@ export interface ParsedArgs {
404
420
  noCache?: boolean;
405
421
  /** `chant workspace graph --composites` (#2662): print each composite instance with the components that can deploy it. */
406
422
  composites?: boolean;
407
- /** `chant workspace graph --intent <path[:start-end]|path#symbol>` (#2651): the region the intent graph is over. Empty with `--record`. */
423
+ /** `chant workspace graph --intent <path[:start-end]|path#symbol|member/node|sha>` (#2651): the region the intent graph is over. Empty with `--record`. */
408
424
  intent?: string;
409
425
  /** `chant workspace graph --intent` and `chant workspace runs` (#3035): follow squash merges to their pull requests' original commits, fetching missing pull request refs. */
410
426
  followSquash?: boolean;
@@ -442,8 +458,20 @@ export interface ParsedArgs {
442
458
  branch?: string;
443
459
  /** `chant ci workflow --workflow <name>`, repeatable (#3573): a workflow whose completion triggers a tick, beside those the scan finds. */
444
460
  ciWorkflows?: string[];
445
- /** `chant ci workflow --chant <command>` (#3573): the command the tick step runs chant with, in place of the npx pin. */
461
+ /** `chant ci run plan|apply --commit <sha>` (#4094): the commit the job runs at. Default `HEAD`. */
462
+ commit?: string;
463
+ /** `chant ci run --pass-env <NAME>`, repeatable (#4094): a variable of the runner's environment the job gets, beside the few it always gets. */
464
+ passEnv?: string[];
465
+ /** `chant ci workflow --chant <command>` (#3573): the command the tick step runs chant with, in place of the npx pin. Also `chant ci run --chant` (#4094): what runs chant in the job. */
446
466
  ciChant?: string;
467
+ /** `chant ci serve --repo <owner/repo>` (#4095): the repository it polls. */
468
+ ciRepo?: string;
469
+ /** `chant ci serve --max-jobs <n>` (#4095): how many jobs run at once. Default 1. */
470
+ maxJobs?: number;
471
+ /** `chant ci serve --author <login>`, repeatable (#4095): plan only pull requests these logins opened. */
472
+ ciAuthors?: string[];
473
+ /** `chant ci serve --rerun-failed` (#4095): run again the failed apply at the target's head and failed plans of open heads. */
474
+ rerunFailed?: boolean;
447
475
  /** `chant ci workflow --install <command>` (#3573): a step before the tick that installs what `--chant` runs. */
448
476
  ciInstall?: string;
449
477
  /** `chant ci workflow --token-secret <NAME>` (#3573): the repository secret the tick's tag push authenticates with. */
@@ -561,7 +589,7 @@ export interface ParsedArgs {
561
589
  digest?: string;
562
590
  /** `chant components release record --release-plan <file>` (ws-055, #2733) — path to a release plan JSON file, persisted content-addressed to `_plans/<digest>.json` on chant/lifecycle and read back through the read contract (`chant workspace status --json`). The plan's own `digest` field supplies `--digest` when it is omitted, and must match it when both are given. Distinct from `--plan` (#2300, below), the Op gate-approval plan digest. */
563
591
  releasePlanFile?: string;
564
- /** `chant components promote --release <label|digest>` (#4026): deploy under that release, reading the templates its target for `--to` recorded. */
592
+ /** `chant components promote --release <label|digest>` (#4026): deploy under that release, reading the templates its target for `--to` recorded. `chant run --components --release <label|digest>` (#4043): the run is of that release, which must have been cut from the checked-out commit and have a target for `--env`; the release records the run writes name it. */
565
593
  release?: string;
566
594
  /** `chant release cut --build <command>` (#4026): the shell command run in the member's directory before each environment's templates are read, with `CHANT_ENV` set to the environment. */
567
595
  buildCommand?: string;
@@ -660,7 +688,7 @@ export interface ParsedArgs {
660
688
  plan?: string;
661
689
  /** `chant operator log --op <name>` (#2029) — restrict the tick history to one ConvergeOp by name. Omitted, every discovered ConvergeOp's ticks are merged into one timeline. */
662
690
  op?: string;
663
- /** `chant operator log --since <iso>` (#2029) — only entries at or after this ISO-8601 instant. `chant workspace records --since <rev>` (#2673) — the revision to compare the records with. */
691
+ /** `chant operator log --since <iso>` (#2029) — only entries at or after this ISO-8601 instant. `chant workspace records --since <rev>` (#2673) — the revision to compare the records with. `chant workspace metrics --since <date>` (#4039) — the start of the window the metrics count. */
664
692
  since?: string;
665
693
  /** `chant operator log --limit <n>` (#2029) — keep only the newest n entries (still printed oldest-first). */
666
694
  limit?: number;
@@ -37,18 +37,21 @@ export const stdoutLine = (line: string): void => void process.stdout.write(line
37
37
 
38
38
  /**
39
39
  * The run's stream on stdout when `--events-json` was passed, else
40
- * undefined. The run is named by `runId` when the caller has one (so a
40
+ * undefined. Each line goes to stdout, to this host's store and to `also`
41
+ * when given. The run is named by `runId` when the caller has one (so a
41
42
  * release record and the stream name the same run), else by the CI run or a
42
43
  * local id.
43
44
  */
44
45
  export async function openCliRunEvents(
45
46
  args: Pick<ParsedArgs, "eventsJson">,
46
47
  run: { kind: RunEventKind; name: string; runId?: string; cwd?: string },
48
+ also?: (line: string) => void,
47
49
  ): Promise<RunEventStream | undefined> {
48
50
  if (!args.eventsJson) return undefined;
49
51
  const resolved = await resolveRunEventRun(run);
50
52
  // #4042: the stream is also kept on this host, so `chant run events <run>`
51
53
  // replays it. Only a command that was asked for a stream keeps one.
52
54
  const store = await openRunEventStore(run.cwd ?? process.cwd(), resolved.runId);
53
- return openRunEventStream(resolved, teeLines(stdoutLine, store?.write.bind(store))!);
55
+ // `also`: a third copy of each line, such as `--report-url`'s upload (#4007).
56
+ return openRunEventStream(resolved, teeLines(stdoutLine, store?.write.bind(store), also)!);
54
57
  }
@@ -108,6 +108,15 @@ describe("maybeRecordAutoRelease", () => {
108
108
  expect(input).toMatchObject({ runId: "9001", runOrigin: { forge: "github", repo: "acme/app" }, approver: "alice", admission });
109
109
  });
110
110
 
111
+ test("a run under a release records its releaseDigest, and one under none records none (#4043)", async () => {
112
+ appendReleaseRecordMock.mockResolvedValue({ commit: "c".repeat(40), record: { version: 1 } });
113
+ const releaseDigest = `jcs1-sha256:${"c".repeat(64)}`;
114
+ await maybeRecordAutoRelease({ component: "svc", env: "dev", success: true, records: [publishRecord("sha256:abc")], runId: "r", releaseDigest }, { actor: "alice" });
115
+ await maybeRecordAutoRelease({ component: "svc", env: "dev", success: true, records: [publishRecord("sha256:abc")], runId: "r" }, { actor: "alice" });
116
+ expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({ releaseDigest });
117
+ expect(appendReleaseRecordMock.mock.calls[1][0]).not.toHaveProperty("releaseDigest");
118
+ });
119
+
111
120
  test("an ungated deploy records neither", async () => {
112
121
  appendReleaseRecordMock.mockResolvedValue({ commit: "c".repeat(40), record: { version: 1 } });
113
122
  await maybeRecordAutoRelease({ component: "svc", env: "prod", success: true, records: [publishRecord("sha256:abc")], runId: "r" }, { actor: "alice" });
@@ -101,6 +101,8 @@ export interface AutoReleaseRunInfo {
101
101
  profileOverride?: string;
102
102
  /** The deploy's input-side digest, when `digest` is a rendered-content identity (a pinned helm deploy, chant #1242) — recorded as the release record's `inputDigest` so ledger queries can still join on inputs across clusters whose bytes legitimately differ. */
103
103
  inputDigest?: string;
104
+ /** The release the deploy was made under (#4043), recorded as the release record's `releaseDigest`. Set only when the caller named a release; never inferred. */
105
+ releaseDigest?: string;
104
106
  }
105
107
 
106
108
  /** Opt-out + field-override knobs for auto-release recording, threaded from CLI flags/config (#597: "opt-out-able (flag/config), documented default"). */
@@ -191,6 +193,7 @@ export async function maybeRecordAutoRelease(
191
193
  ...(run.admission ? { admission: run.admission } : {}),
192
194
  ...(run.profileOverride ? { profileOverride: run.profileOverride } : {}),
193
195
  ...(run.inputDigest ? { inputDigest: run.inputDigest } : {}),
196
+ ...(run.releaseDigest ? { releaseDigest: run.releaseDigest } : {}),
194
197
  },
195
198
  { cwd: options.cwd },
196
199
  );
@@ -61,6 +61,15 @@ export interface DeployContext {
61
61
  * Absent outside a fan-out that writes a record.
62
62
  */
63
63
  carry?: (member: string, value: unknown) => void;
64
+ /**
65
+ * Set when a step is planned (#4084): the components or stacks whose
66
+ * outputs this step's input reads through `stackOutput()` or
67
+ * `@<component>.publish.*` and which had nothing to read yet, so the input
68
+ * holds `undefined` there. Their values are known only when the run
69
+ * applies them. A `plan` that needs them can say so instead of planning
70
+ * against missing values.
71
+ */
72
+ unresolvedInputs?: string[];
64
73
  }
65
74
 
66
75
  /**