@intentius/chant 0.124.28 → 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 (395) 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 +15 -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 +40 -3
  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 +61 -10
  39. package/dist/cli/parse-args.d.ts.map +1 -1
  40. package/dist/cli/parse-args.js +24 -2
  41. package/dist/cli/registry.d.ts +15 -3
  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 +25 -0
  65. package/dist/components/pr-pipeline.d.ts.map +1 -1
  66. package/dist/components/pr-pipeline.js +22 -4
  67. package/dist/components/promote.d.ts +23 -0
  68. package/dist/components/promote.d.ts.map +1 -1
  69. package/dist/components/promote.js +60 -2
  70. package/dist/components/release-compile.d.ts +9 -0
  71. package/dist/components/release-compile.d.ts.map +1 -1
  72. package/dist/components/release-compile.js +9 -3
  73. package/dist/components/release-gate.d.ts +11 -1
  74. package/dist/components/release-gate.d.ts.map +1 -1
  75. package/dist/components/release-gate.js +10 -4
  76. package/dist/components/wave-plan.d.ts +5 -0
  77. package/dist/components/wave-plan.d.ts.map +1 -1
  78. package/dist/components/wave-plan.js +10 -2
  79. package/dist/config.d.ts +38 -0
  80. package/dist/config.d.ts.map +1 -1
  81. package/dist/config.js +2 -0
  82. package/dist/flag-params.d.ts +54 -0
  83. package/dist/flag-params.d.ts.map +1 -0
  84. package/dist/flag-params.js +130 -0
  85. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  86. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  87. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  88. package/dist/lifecycle/change-set-store.d.ts +2 -1
  89. package/dist/lifecycle/change-set-store.d.ts.map +1 -1
  90. package/dist/lifecycle/change-set-store.js +2 -1
  91. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  92. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  93. package/dist/lifecycle/delivery-metrics.js +145 -0
  94. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  95. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  96. package/dist/lifecycle/deploy-outcome.js +101 -0
  97. package/dist/lifecycle/gate-change-set.d.ts +10 -1
  98. package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
  99. package/dist/lifecycle/gate-change-set.js +35 -3
  100. package/dist/lifecycle/gate-ledger.d.ts +2 -3
  101. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  102. package/dist/lifecycle/git.d.ts +30 -1
  103. package/dist/lifecycle/git.d.ts.map +1 -1
  104. package/dist/lifecycle/git.js +31 -5
  105. package/dist/lifecycle/index.d.ts +4 -0
  106. package/dist/lifecycle/index.d.ts.map +1 -1
  107. package/dist/lifecycle/index.js +4 -0
  108. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  109. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  110. package/dist/lifecycle/mixed-environment.js +154 -0
  111. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  112. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  113. package/dist/lifecycle/publish-attachments.js +179 -0
  114. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  115. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  116. package/dist/lifecycle/publish-credentials.js +69 -0
  117. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  118. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  119. package/dist/lifecycle/publish-declaration.js +83 -0
  120. package/dist/lifecycle/publish-forge.d.ts +53 -0
  121. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  122. package/dist/lifecycle/publish-forge.js +298 -0
  123. package/dist/lifecycle/publish-npm.d.ts +61 -0
  124. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  125. package/dist/lifecycle/publish-npm.js +242 -0
  126. package/dist/lifecycle/publish-oci.d.ts +64 -0
  127. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  128. package/dist/lifecycle/publish-oci.js +383 -0
  129. package/dist/lifecycle/publish-record.d.ts +66 -0
  130. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  131. package/dist/lifecycle/publish-record.js +84 -0
  132. package/dist/lifecycle/publish-targets.d.ts +45 -0
  133. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  134. package/dist/lifecycle/publish-targets.js +85 -0
  135. package/dist/lifecycle/release-cut.d.ts +48 -1
  136. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  137. package/dist/lifecycle/release-cut.js +116 -23
  138. package/dist/lifecycle/release-join.d.ts +95 -0
  139. package/dist/lifecycle/release-join.d.ts.map +1 -0
  140. package/dist/lifecycle/release-join.js +139 -0
  141. package/dist/lifecycle/release-publish.d.ts +312 -0
  142. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  143. package/dist/lifecycle/release-publish.js +335 -0
  144. package/dist/lifecycle/release-revocation.d.ts +80 -0
  145. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  146. package/dist/lifecycle/release-revocation.js +123 -0
  147. package/dist/lifecycle/release-store.d.ts +12 -2
  148. package/dist/lifecycle/release-store.d.ts.map +1 -1
  149. package/dist/lifecycle/release-store.js +4 -2
  150. package/dist/lifecycle/release-verify.d.ts +61 -0
  151. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  152. package/dist/lifecycle/release-verify.js +92 -0
  153. package/dist/lifecycle/release-workstation.d.ts +91 -0
  154. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  155. package/dist/lifecycle/release-workstation.js +180 -0
  156. package/dist/lifecycle/retention-policy.d.ts +45 -0
  157. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  158. package/dist/lifecycle/retention-policy.js +43 -0
  159. package/dist/lifecycle/retention.d.ts +185 -0
  160. package/dist/lifecycle/retention.d.ts.map +1 -0
  161. package/dist/lifecycle/retention.js +400 -0
  162. package/dist/plan-summary.d.ts.map +1 -1
  163. package/dist/plan-summary.js +8 -2
  164. package/dist/pr-loop.d.ts +7 -0
  165. package/dist/pr-loop.d.ts.map +1 -1
  166. package/dist/pr-loop.js +23 -4
  167. package/dist/pr-report-url.d.ts +82 -0
  168. package/dist/pr-report-url.d.ts.map +1 -0
  169. package/dist/pr-report-url.js +174 -0
  170. package/dist/provenance.d.ts +2 -2
  171. package/dist/provenance.d.ts.map +1 -1
  172. package/dist/run-event-store.d.ts +29 -2
  173. package/dist/run-event-store.d.ts.map +1 -1
  174. package/dist/run-event-store.js +50 -3
  175. package/dist/run-event-stream.d.ts +2 -1
  176. package/dist/run-event-stream.d.ts.map +1 -1
  177. package/dist/run-event-stream.js +5 -1
  178. package/dist/run-event.d.ts +19 -1
  179. package/dist/run-event.d.ts.map +1 -1
  180. package/dist/run-event.js +2 -0
  181. package/dist/wave-runtime.mjs +260 -229
  182. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  183. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  184. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  185. package/dist/workspace/change-set.schema.json +5 -1
  186. package/dist/workspace/check.schema.json +4 -2
  187. package/dist/workspace/checks/records.d.ts +9 -1
  188. package/dist/workspace/checks/records.d.ts.map +1 -1
  189. package/dist/workspace/checks/records.js +35 -1
  190. package/dist/workspace/checks.d.ts +1 -0
  191. package/dist/workspace/checks.d.ts.map +1 -1
  192. package/dist/workspace/checks.js +2 -1
  193. package/dist/workspace/ci-green.d.ts +8 -0
  194. package/dist/workspace/ci-green.d.ts.map +1 -1
  195. package/dist/workspace/ci-green.js +10 -3
  196. package/dist/workspace/declaration.d.ts +16 -0
  197. package/dist/workspace/declaration.d.ts.map +1 -1
  198. package/dist/workspace/declaration.js +26 -0
  199. package/dist/workspace/declaration.schema.json +64 -0
  200. package/dist/workspace/declared-kinds.d.ts +16 -0
  201. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  202. package/dist/workspace/declared-kinds.js +25 -0
  203. package/dist/workspace/delivery-metrics.d.ts +91 -0
  204. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  205. package/dist/workspace/delivery-metrics.js +217 -0
  206. package/dist/workspace/delivery-metrics.schema.json +519 -0
  207. package/dist/workspace/flags-read.d.ts +37 -0
  208. package/dist/workspace/flags-read.d.ts.map +1 -0
  209. package/dist/workspace/flags-read.js +103 -0
  210. package/dist/workspace/flags.d.ts +100 -0
  211. package/dist/workspace/flags.d.ts.map +1 -0
  212. package/dist/workspace/flags.js +98 -0
  213. package/dist/workspace/graph-cli.js +1 -1
  214. package/dist/workspace/intent-cli.d.ts +11 -2
  215. package/dist/workspace/intent-cli.d.ts.map +1 -1
  216. package/dist/workspace/intent-cli.js +54 -5
  217. package/dist/workspace/intent-trace.d.ts +219 -0
  218. package/dist/workspace/intent-trace.d.ts.map +1 -0
  219. package/dist/workspace/intent-trace.js +314 -0
  220. package/dist/workspace/intent.d.ts +30 -6
  221. package/dist/workspace/intent.d.ts.map +1 -1
  222. package/dist/workspace/intent.js +141 -21
  223. package/dist/workspace/intent.schema.json +702 -6
  224. package/dist/workspace/overlap.d.ts +146 -0
  225. package/dist/workspace/overlap.d.ts.map +1 -0
  226. package/dist/workspace/overlap.js +276 -0
  227. package/dist/workspace/reason-codes.d.ts +2 -0
  228. package/dist/workspace/reason-codes.d.ts.map +1 -1
  229. package/dist/workspace/reason-codes.js +3 -0
  230. package/dist/workspace/records.d.ts +15 -2
  231. package/dist/workspace/records.d.ts.map +1 -1
  232. package/dist/workspace/records.js +32 -0
  233. package/dist/workspace/records.schema.json +21 -2
  234. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  235. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  236. package/dist/workspace/releases.d.ts +9 -0
  237. package/dist/workspace/releases.d.ts.map +1 -1
  238. package/dist/workspace/releases.js +9 -0
  239. package/dist/workspace/releases.schema.json +123 -1
  240. package/dist/workspace/run-event.schema.json +10 -3
  241. package/dist/workspace/status-gates.d.ts +8 -0
  242. package/dist/workspace/status-gates.d.ts.map +1 -1
  243. package/dist/workspace/status.d.ts +78 -0
  244. package/dist/workspace/status.d.ts.map +1 -1
  245. package/dist/workspace/status.js +170 -3
  246. package/dist/workspace/status.schema.json +198 -1
  247. package/dist/workspace/wip.d.ts.map +1 -1
  248. package/dist/workspace/wip.js +11 -3
  249. package/package.json +1 -1
  250. package/src/build-params.test.ts +46 -0
  251. package/src/build-params.ts +55 -1
  252. package/src/change-set-graph.ts +10 -2
  253. package/src/change-set.ts +14 -3
  254. package/src/cli/build-params-cli.ts +22 -9
  255. package/src/cli/commands/build.ts +6 -3
  256. package/src/cli/handlers/approve-release.test.ts +5 -1
  257. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  258. package/src/cli/handlers/approve.ts +47 -0
  259. package/src/cli/handlers/build.test.ts +15 -0
  260. package/src/cli/handlers/build.ts +17 -4
  261. package/src/cli/handlers/fan-out.test.ts +180 -1
  262. package/src/cli/handlers/fan-out.ts +38 -3
  263. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  264. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  265. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  266. package/src/cli/handlers/pr.test.ts +148 -1
  267. package/src/cli/handlers/pr.ts +139 -28
  268. package/src/cli/handlers/promote.test.ts +280 -2
  269. package/src/cli/handlers/promote.ts +210 -34
  270. package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
  271. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  272. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  273. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  274. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  275. package/src/cli/handlers/release.e2e.test.ts +146 -32
  276. package/src/cli/handlers/release.ts +415 -28
  277. package/src/cli/handlers/run-auto-release.test.ts +9 -0
  278. package/src/cli/handlers/run-release.test.ts +55 -0
  279. package/src/cli/handlers/run.ts +69 -5
  280. package/src/cli/main.test.ts +1 -0
  281. package/src/cli/main.ts +61 -10
  282. package/src/cli/parse-args.ts +19 -2
  283. package/src/cli/registry.ts +15 -3
  284. package/src/cli/run-events.ts +5 -2
  285. package/src/components/auto-release.test.ts +9 -0
  286. package/src/components/auto-release.ts +3 -0
  287. package/src/components/capability.ts +9 -0
  288. package/src/components/driver.test.ts +24 -0
  289. package/src/components/driver.ts +36 -0
  290. package/src/components/fan-out-inputs.test.ts +85 -0
  291. package/src/components/fan-out-inputs.ts +192 -0
  292. package/src/components/fan-out-record.test.ts +10 -0
  293. package/src/components/fan-out-record.ts +19 -0
  294. package/src/components/fan-out-run.ts +48 -0
  295. package/src/components/fan-out.ts +4 -0
  296. package/src/components/pr-pipeline.ts +35 -4
  297. package/src/components/promote.ts +68 -2
  298. package/src/components/release-compile.ts +17 -3
  299. package/src/components/release-gate.ts +18 -4
  300. package/src/components/wave-plan.ts +15 -2
  301. package/src/config.ts +8 -0
  302. package/src/flag-params.test.ts +144 -0
  303. package/src/flag-params.ts +134 -0
  304. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  305. package/src/lifecycle/change-set-store.ts +2 -1
  306. package/src/lifecycle/delivery-metrics.ts +263 -0
  307. package/src/lifecycle/deploy-outcome.ts +147 -0
  308. package/src/lifecycle/gate-change-set.ts +37 -6
  309. package/src/lifecycle/gate-ledger.ts +2 -2
  310. package/src/lifecycle/git.ts +61 -8
  311. package/src/lifecycle/index.ts +4 -0
  312. package/src/lifecycle/mixed-environment.test.ts +97 -0
  313. package/src/lifecycle/mixed-environment.ts +204 -0
  314. package/src/lifecycle/publish-attachments.ts +196 -0
  315. package/src/lifecycle/publish-credentials.ts +84 -0
  316. package/src/lifecycle/publish-declaration.ts +95 -0
  317. package/src/lifecycle/publish-forge.ts +325 -0
  318. package/src/lifecycle/publish-npm.ts +259 -0
  319. package/src/lifecycle/publish-oci.ts +396 -0
  320. package/src/lifecycle/publish-record.ts +113 -0
  321. package/src/lifecycle/publish-resume.test.ts +168 -0
  322. package/src/lifecycle/publish-targets.test.ts +494 -0
  323. package/src/lifecycle/publish-targets.ts +100 -0
  324. package/src/lifecycle/release-cut.test.ts +74 -2
  325. package/src/lifecycle/release-cut.ts +158 -22
  326. package/src/lifecycle/release-flag.test.ts +112 -0
  327. package/src/lifecycle/release-join.test.ts +68 -0
  328. package/src/lifecycle/release-join.ts +182 -0
  329. package/src/lifecycle/release-publish.test.ts +225 -0
  330. package/src/lifecycle/release-publish.ts +568 -0
  331. package/src/lifecycle/release-revocation.ts +147 -0
  332. package/src/lifecycle/release-store.ts +15 -4
  333. package/src/lifecycle/release-verify.test.ts +110 -0
  334. package/src/lifecycle/release-verify.ts +120 -0
  335. package/src/lifecycle/release-workstation.test.ts +204 -0
  336. package/src/lifecycle/release-workstation.ts +216 -0
  337. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  338. package/src/lifecycle/retention-policy.ts +63 -0
  339. package/src/lifecycle/retention.test.ts +363 -0
  340. package/src/lifecycle/retention.ts +508 -0
  341. package/src/plan-summary.test.ts +20 -0
  342. package/src/plan-summary.ts +8 -1
  343. package/src/pr-loop.ts +25 -4
  344. package/src/pr-report-url.test.ts +131 -0
  345. package/src/pr-report-url.ts +209 -0
  346. package/src/provenance.ts +2 -2
  347. package/src/run-event-store.ts +63 -3
  348. package/src/run-event-stream.ts +4 -1
  349. package/src/run-event.ts +21 -1
  350. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  351. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  352. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  353. package/src/workspace/change-set.schema.json +5 -1
  354. package/src/workspace/check.schema.json +4 -2
  355. package/src/workspace/checks/records.ts +35 -1
  356. package/src/workspace/checks.ts +2 -1
  357. package/src/workspace/ci-green.ts +14 -3
  358. package/src/workspace/declaration.schema.json +64 -0
  359. package/src/workspace/declaration.test.ts +28 -0
  360. package/src/workspace/declaration.ts +38 -0
  361. package/src/workspace/declared-kinds.ts +40 -1
  362. package/src/workspace/delivery-metrics.schema.json +519 -0
  363. package/src/workspace/delivery-metrics.test.ts +236 -0
  364. package/src/workspace/delivery-metrics.ts +286 -0
  365. package/src/workspace/flags-read.ts +121 -0
  366. package/src/workspace/flags.test.ts +141 -0
  367. package/src/workspace/flags.ts +157 -0
  368. package/src/workspace/graph-cli.ts +1 -1
  369. package/src/workspace/intent-cli.ts +46 -5
  370. package/src/workspace/intent-trace.test.ts +173 -0
  371. package/src/workspace/intent-trace.ts +484 -0
  372. package/src/workspace/intent.schema.json +702 -6
  373. package/src/workspace/intent.ts +148 -25
  374. package/src/workspace/older-records.test.ts +412 -0
  375. package/src/workspace/overlap.test.ts +165 -0
  376. package/src/workspace/overlap.ts +340 -0
  377. package/src/workspace/read-contract.e2e.test.ts +2 -1
  378. package/src/workspace/reason-codes.test.ts +5 -0
  379. package/src/workspace/reason-codes.ts +3 -0
  380. package/src/workspace/record-assets.test.ts +2 -1
  381. package/src/workspace/records-contract.e2e.test.ts +2 -1
  382. package/src/workspace/records.schema.json +21 -2
  383. package/src/workspace/records.ts +36 -2
  384. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  385. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  386. package/src/workspace/releases.schema.json +123 -1
  387. package/src/workspace/releases.ts +11 -0
  388. package/src/workspace/run-event.schema.json +10 -3
  389. package/src/workspace/status-contract.test.ts +29 -0
  390. package/src/workspace/status-gates.ts +8 -0
  391. package/src/workspace/status-mixed.test.ts +152 -0
  392. package/src/workspace/status-revoked.test.ts +153 -0
  393. package/src/workspace/status.schema.json +198 -1
  394. package/src/workspace/status.ts +254 -3
  395. package/src/workspace/wip.ts +11 -2
@@ -119,6 +119,23 @@ vi.mock("../../lifecycle/release-store", async () => {
119
119
  resolveRelease: async (ref: string) => (releaseFixture && (ref === releaseFixture.label || ref === releaseFixture.release.releaseDigest) ? releaseFixture : null),
120
120
  readReleaseTemplate: async () => storedTemplate,
121
121
  readRelease: async (digest: string) => knownReleases.get(digest) ?? null,
122
+ // #4035: labels come from the fixture's cuts, never from this checkout's branch.
123
+ readCuts: async () => ({
124
+ cuts: [...knownLabels].map(([releaseDigest, label]) => ({ version: 1, releaseDigest, label, actor: "alice", timestamp: "2026-01-01T00:00:00.000Z", runOrigin: null, signature: null })),
125
+ malformed: 0,
126
+ }),
127
+ };
128
+ });
129
+ const knownLabels = new Map<string, string>();
130
+
131
+ // #4035: deploy outcomes live in memory.
132
+ const outcomes: import("../../lifecycle/deploy-outcome").DeployOutcome[] = [];
133
+ vi.mock("../../lifecycle/deploy-outcome", async () => {
134
+ const actual = await vi.importActual<typeof import("../../lifecycle/deploy-outcome")>("../../lifecycle/deploy-outcome");
135
+ return {
136
+ ...actual,
137
+ appendDeployOutcome: async (o: import("../../lifecycle/deploy-outcome").DeployOutcome) => { outcomes.push(o); return "c0ffee1"; },
138
+ readDeployOutcomes: async (env: string) => outcomes.filter((o) => o.env === env),
122
139
  };
123
140
  });
124
141
  vi.mock("../../components/release-promote", async () => {
@@ -130,6 +147,13 @@ vi.mock("../../components/release-promote", async () => {
130
147
  };
131
148
  });
132
149
 
150
+ // #4033: the revoked commits come from a map, not from this checkout's tags.
151
+ const revokedCommits = new Map<string, import("../../lifecycle/release-revocation").ReleaseRevocation>();
152
+ vi.mock("../../lifecycle/release-revocation", async () => {
153
+ const actual = await vi.importActual<typeof import("../../lifecycle/release-revocation")>("../../lifecycle/release-revocation");
154
+ return { ...actual, readRevokedCommits: async () => revokedCommits };
155
+ });
156
+
133
157
  // The run handler pulls in every runtime; the promote needs only its exit code.
134
158
  vi.mock("./run", () => ({ GATED_EXIT_CODE: 3 }));
135
159
 
@@ -185,6 +209,9 @@ beforeEach(() => {
185
209
  storedChangeSets.clear();
186
210
  storeFails = false;
187
211
  knownReleases.clear();
212
+ knownLabels.clear();
213
+ outcomes.length = 0;
214
+ revokedCommits.clear();
188
215
  removedTemplates.length = 0;
189
216
  ran.length = 0;
190
217
  stepInputs.length = 0;
@@ -499,6 +526,33 @@ describe("chant components promote --release binds its gate (#4045)", () => {
499
526
  });
500
527
  });
501
528
 
529
+ test("a release cut from a revoked commit is refused before anything runs, so its pending gate never passes (#4033)", async () => {
530
+ expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
531
+ const pending = releaseGatePort!.appended[0];
532
+ await releaseGatePort!.appendResolution!({
533
+ op: "release", gate: "prod-release", resolvedBy: "alice", timestamp: new Date(Date.now() + 1000).toISOString(),
534
+ planDigest: pending.planDigest, environment: "prod", releaseDigest: r1.releaseDigest,
535
+ });
536
+ // The commit went red after the approval: a run from chant runs (#4044) names its run in opRun, with id null.
537
+ revokedCommits.set(r1.commit, {
538
+ commit: r1.commit,
539
+ tag: `ci/revoked/${r1.commit}`,
540
+ tagged: "2026-10-16T12:00:00.000Z",
541
+ phase: "e2e",
542
+ run: { name: "smoke", id: null, conclusion: "failure", url: null, opRun: { id: "r-12", env: "staging", signer: "runner:ci" } },
543
+ signer: "runner:ci",
544
+ });
545
+ ran.length = 0;
546
+ errors.length = 0;
547
+ expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(1);
548
+ const said = errors.join("\n");
549
+ expect(said).toMatch(/release v1\.0\.0 was cut from commit abc123000000, which ci\/revoked\/abc1230{34} revokes \(phase e2e failed in smoke \(Op run r-12 in staging\)\)/);
550
+ expect(said).toMatch(/\[release-revoked\]/);
551
+ expect(said).toMatch(/chant ci last-green/);
552
+ expect(ran).toEqual([]);
553
+ expect(appendReleaseRecordMock).not.toHaveBeenCalled();
554
+ });
555
+
502
556
  test("an approval for one release can't be used by another", async () => {
503
557
  expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
504
558
  const pending = releaseGatePort!.appended[0];
@@ -585,7 +639,7 @@ describe("chant components rollback", () => {
585
639
 
586
640
  test("an environment and --component are required", async () => {
587
641
  expect(await runComponentsRollback(rb({ component: undefined }))).toBe(1);
588
- expect(errors.join("\n")).toMatch(/--component <name> are required/);
642
+ expect(errors.join("\n")).toMatch(/--component <name> or --release <label\|digest> are required/);
589
643
  });
590
644
 
591
645
  test("redeploys the previous release from its recorded digest and records what it restored", async () => {
@@ -602,6 +656,24 @@ describe("chant components rollback", () => {
602
656
  expect(appendReleaseRecordMock.mock.calls[0][0]).not.toHaveProperty("promotedFrom");
603
657
  });
604
658
 
659
+ test("copies the release off the record it restores, and writes none for a record of no release (#4043)", async () => {
660
+ const RELEASE = `jcs1-sha256:${"b".repeat(64)}`;
661
+ readReleaseLedgerMock.mockResolvedValue({
662
+ records: [{ ...prod("sha256:old", "2026-01-01T00:00:00.000Z", "run-1"), releaseDigest: RELEASE }, prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2")],
663
+ malformed: 0,
664
+ });
665
+ expect(await runComponentsRollback(rb({}))).toBe(0);
666
+ expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({ digest: "sha256:old", releaseDigest: RELEASE });
667
+
668
+ appendReleaseRecordMock.mockClear();
669
+ readReleaseLedgerMock.mockResolvedValue({
670
+ records: [prod("sha256:old", "2026-01-01T00:00:00.000Z", "run-1"), prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2")],
671
+ malformed: 0,
672
+ });
673
+ expect(await runComponentsRollback(rb({}))).toBe(0);
674
+ expect(appendReleaseRecordMock.mock.calls[0][0]).not.toHaveProperty("releaseDigest");
675
+ });
676
+
605
677
  test("a comma list is refused: rollback acts on one component (#3950)", async () => {
606
678
  expect(await runComponentsRollback(rb({ component: "api,web" }))).toBe(1);
607
679
  expect(errors.join("\n")).toMatch(/--component takes one component name, not a list/);
@@ -668,6 +740,16 @@ describe("chant components redeploy", () => {
668
740
  expect(errors.join("\n")).toMatch(/Redeployed .*api.* in prod at sha256:new/);
669
741
  });
670
742
 
743
+ test("copies the release off the record it redeploys (#4043)", async () => {
744
+ const RELEASE = `jcs1-sha256:${"b".repeat(64)}`;
745
+ readReleaseLedgerMock.mockResolvedValue({
746
+ records: [{ ...prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2"), releaseDigest: RELEASE }],
747
+ malformed: 0,
748
+ });
749
+ expect(await runComponentsRedeploy(rd({}))).toBe(0);
750
+ expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({ digest: "sha256:new", releaseDigest: RELEASE, redeploys: { runId: "run-2" } });
751
+ });
752
+
671
753
  test("the same digest rollback refuses is the one redeploy accepts", async () => {
672
754
  expect(await runComponentsRollback(rd({ path: "rollback", digest: "sha256:new" }))).toBe(1);
673
755
  expect(errors.join("\n")).toMatch(/already the current release/);
@@ -704,10 +786,13 @@ describe("chant components redeploy", () => {
704
786
  expect(errors.join("\n")).toMatch(/then run the same redeploy again/);
705
787
  });
706
788
 
707
- test("a failed redeploy records nothing and exits 1", async () => {
789
+ test("a failed redeploy records no release, records the component failed (#4035), and exits 1", async () => {
708
790
  failApplyFor = "api";
709
791
  expect(await runComponentsRedeploy(rd({}))).toBe(1);
710
792
  expect(appendReleaseRecordMock).not.toHaveBeenCalled();
793
+ expect(outcomes).toEqual([
794
+ expect.objectContaining({ env: "prod", verb: "redeploy", status: "fail", components: [{ component: "api", outcome: "failed", digest: "sha256:new", releaseDigest: null }] }),
795
+ ]);
711
796
  expect(errors.join("\n")).toMatch(/redeploy failed at "api"/);
712
797
  });
713
798
  });
@@ -780,3 +865,196 @@ describe("--events-json on promote, rollback and redeploy (#4041)", () => {
780
865
  expect(out.filter((l) => l.startsWith('{"$schema"'))).toEqual([]);
781
866
  });
782
867
  });
868
+
869
+ describe("chant components rollback --release (#4035)", () => {
870
+ const prodTemplate = `{"env":"prod"}\n`;
871
+ const gated: DriverComponent = {
872
+ ...api,
873
+ deploy: [
874
+ ...api.deploy.slice(0, 2),
875
+ { phase: "Apply", steps: [{ kind: "gate", gate: "prod-release" }, { kind: "apply", template: "dist/api.template.json", imageRef: "@Publish.digest" }] },
876
+ ],
877
+ };
878
+ const releaseAt = (commit: string) =>
879
+ makeRelease({
880
+ member: null,
881
+ commit: commit.padEnd(40, "0"),
882
+ artifactDigests: [{ component: "api", kind: "image", path: "api.tar", digest: "sha256:api" }],
883
+ targets: [{ env: "prod", templates: [{ component: "api", path: "dist/api.template.json", digest: contentDigest(prodTemplate) }], params: [] }],
884
+ });
885
+ const r1 = releaseAt("abc123");
886
+ const r2 = releaseAt("def456");
887
+ const prod = (digest: string, timestamp: string, runId: string, release: { releaseDigest: string }): ReleaseRecord =>
888
+ ({ ...staging, env: "prod", digest, timestamp, runId, releaseDigest: release.releaseDigest });
889
+ let ledger: ReleaseRecord[];
890
+ const revoke = (r: { commit: string }) =>
891
+ revokedCommits.set(r.commit, { commit: r.commit, tag: `ci/revoked/${r.commit}`, tagged: "2026-10-16T12:00:00.000Z", phase: "e2e", run: null, signer: null });
892
+
893
+ beforeEach(async () => {
894
+ const { memoryGateLedgerPort } = await vi.importActual<typeof import("../../op/gate")>("../../op/gate");
895
+ releaseGatePort = memoryGateLedgerPort();
896
+ resolveTargetsMock.mockResolvedValue({ success: true, targets: [gated] });
897
+ releaseFixture = { release: r1, label: "v1.0.0" };
898
+ storedTemplate = prodTemplate;
899
+ for (const [r, label] of [[r1, "v1.0.0"], [r2, "v1.1.0"]] as const) {
900
+ knownReleases.set(r.releaseDigest, r);
901
+ knownLabels.set(r.releaseDigest, label);
902
+ }
903
+ ledger = [prod("sha256:old", "2026-01-01T00:00:00.000Z", "run-1", r1), prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2", r2)];
904
+ readReleaseLedgerMock.mockImplementation(async (env: string) => ({ records: env === "prod" ? ledger : [], malformed: 0 }));
905
+ });
906
+
907
+ const rb = (a: Partial<ParsedArgs>) => ctx({ migrateFrom: undefined, migrateTo: undefined, path: "rollback", extraPositional: "prod", ...a });
908
+
909
+ test("restores every component of the release through the release gate, and records the release it restored", async () => {
910
+ expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(3);
911
+ expect(ran).toEqual([]);
912
+ const [doc] = [...storedChangeSets.values()];
913
+ expect(doc).toMatchObject({ releaseDigest: r1.releaseDigest, environment: "prod" });
914
+ expect(doc.entries[0]).toMatchObject({ address: "api", action: "update" });
915
+ expect(releaseGatePort!.appended).toEqual([
916
+ expect.objectContaining({ op: "release", gate: "prod-release", environment: "prod", planDigest: doc.digest, releaseDigest: r1.releaseDigest }),
917
+ ]);
918
+ expect(errors.join("\n")).toMatch(/chant approve release prod-release --env prod/);
919
+ expect(errors.join("\n")).toMatch(/then run the same rollback again/);
920
+ expect(appendReleaseRecordMock).not.toHaveBeenCalled();
921
+ // Stopped at its gate with nothing deployed: the environment is unchanged, so no outcome.
922
+ expect(outcomes).toEqual([]);
923
+
924
+ await releaseGatePort!.appendResolution!({
925
+ op: "release", gate: "prod-release", resolvedBy: "alice", timestamp: new Date(Date.now() + 1000).toISOString(),
926
+ planDigest: doc.digest, environment: "prod", releaseDigest: r1.releaseDigest,
927
+ });
928
+ expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(0);
929
+ // No build and no publish: the recorded digest of the release, and the release's own template.
930
+ expect(ran).toEqual(["prod:apply"]);
931
+ const pinned = `.chant/release-templates/${r1.targets[0].templates[0].digest.replace(/:/g, "_")}/api.template.json`;
932
+ expect(stepInputs.find((x) => x.kind === "apply")?.input).toMatchObject({ template: pinned, imageRef: "sha256:old" });
933
+ expect(appendReleaseRecordMock).toHaveBeenCalledTimes(1);
934
+ expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({
935
+ component: "api",
936
+ env: "prod",
937
+ digest: "sha256:old",
938
+ releaseDigest: r1.releaseDigest,
939
+ restores: { env: "prod", runId: "run-1", timestamp: "2026-01-01T00:00:00.000Z" },
940
+ approver: "alice",
941
+ admission: { op: "release", gate: "prod-release" },
942
+ });
943
+ expect(errors.join("\n")).toMatch(/Rolled back .*api.* in prod to sha256:old/);
944
+ });
945
+
946
+ test("--release takes neither --component nor --digest", async () => {
947
+ expect(await runComponentsRollback(rb({ release: "v1.0.0", component: "api" }))).toBe(1);
948
+ expect(errors.join("\n")).toMatch(/--release rolls back every component of a release, so it does not take --component/);
949
+ expect(await runComponentsRollback(rb({ release: "v1.0.0", digest: "sha256:old" }))).toBe(1);
950
+ expect(errors.join("\n")).toMatch(/--digest picks one release of one component/);
951
+ expect(ran).toEqual([]);
952
+ });
953
+
954
+ test("the release the environment runs is refused, unless a run left one of its components unsettled", async () => {
955
+ releaseFixture = { release: r2, label: "v1.1.0" };
956
+ expect(await runComponentsRollback(rb({ release: "v1.1.0" }))).toBe(1);
957
+ expect(errors.join("\n")).toMatch(/"prod" already runs release v1\.1\.0; nothing to roll back/);
958
+ expect(storedChangeSets.size).toBe(0);
959
+
960
+ outcomes.push({
961
+ version: 1, env: "prod", verb: "promote", runId: "run-3", timestamp: "2026-01-03T00:00:00.000Z", actor: "bob",
962
+ releaseDigest: `jcs1-sha256:${"9".repeat(64)}`, status: "fail",
963
+ components: [{ component: "api", outcome: "failed", digest: "sha256:newer", releaseDigest: `jcs1-sha256:${"9".repeat(64)}` }],
964
+ });
965
+ // The ledger records v1.1.0, but the failed run left api unknown: a hole, so the gate waits for an approval.
966
+ expect(await runComponentsRollback(rb({ release: "v1.1.0" }))).toBe(3);
967
+ const [doc] = [...storedChangeSets.values()];
968
+ expect(doc.entries).toEqual([]);
969
+ expect(doc.members[0].holes).toEqual([expect.objectContaining({ address: "api", reason: expect.stringMatching(/the promote run run-3 left api failed in "prod"/) })]);
970
+ expect(releaseGatePort!.resolved).toEqual([]);
971
+ expect(releaseGatePort!.appended.at(-1)).toMatchObject({ releaseDigest: r2.releaseDigest, environment: "prod", planDigest: doc.digest });
972
+ });
973
+
974
+ test("a release that never deployed a component to the environment is refused", async () => {
975
+ ledger = [ledger[1]];
976
+ expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(1);
977
+ expect(errors.join("\n")).toMatch(/release v1\.0\.0 never deployed "api" to "prod", so no recorded digest can be restored; promote it instead: chant components promote --to prod --release v1\.0\.0/);
978
+ expect(ran).toEqual([]);
979
+ });
980
+
981
+ test("a revoked release is refused before anything runs, naming a release to restore instead", async () => {
982
+ revoke(r1);
983
+ expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(1);
984
+ const said = errors.join("\n");
985
+ expect(said).toMatch(/release v1\.0\.0 was cut from commit abc123000000, which ci\/revoked\/abc1230{34} revokes .*\[release-revoked\]/);
986
+ expect(said).toMatch(/chant components rollback prod --release v1\.1\.0/);
987
+ expect(ran).toEqual([]);
988
+ expect(storedChangeSets.size).toBe(0);
989
+ });
990
+
991
+ test("rollback --component and redeploy refuse to put back a revoked release (#4033)", async () => {
992
+ revoke(r2);
993
+ const rd = ctx({ migrateFrom: undefined, migrateTo: undefined, path: "redeploy", extraPositional: "prod", component: "api" });
994
+ expect(await runComponentsRedeploy(rd)).toBe(1);
995
+ expect(errors.join("\n")).toMatch(/release v1\.1\.0 was cut from commit def456000000, .*\[release-revoked\]/);
996
+ expect(errors.join("\n")).toMatch(/Restore a release cut from a green commit instead: chant components rollback prod --release v1\.0\.0/);
997
+
998
+ revokedCommits.clear();
999
+ revoke(r1);
1000
+ errors.length = 0;
1001
+ expect(await runComponentsRollback(rb({ component: "api" }))).toBe(1);
1002
+ expect(errors.join("\n")).toMatch(/release v1\.0\.0 was cut from commit abc123000000, .*\[release-revoked\]/);
1003
+ expect(ran).toEqual([]);
1004
+ expect(appendReleaseRecordMock).not.toHaveBeenCalled();
1005
+ });
1006
+ });
1007
+
1008
+ describe("a promote that applies partly records what the environment runs (#4035)", () => {
1009
+ const web: DriverComponent = { ...api, name: "web" };
1010
+ const release = makeRelease({
1011
+ member: null,
1012
+ commit: "abc123".padEnd(40, "0"),
1013
+ artifactDigests: ["api", "web"].map((component) => ({ component, kind: "image", path: `${component}.tar`, digest: `sha256:${component}` })),
1014
+ targets: [{ env: "prod", templates: [], params: [] }],
1015
+ });
1016
+
1017
+ beforeEach(() => {
1018
+ resolveTargetsMock.mockResolvedValue({ success: true, targets: [api, web] });
1019
+ readReleaseLedgerMock.mockResolvedValue({ records: [staging, { ...staging, component: "web" }], malformed: 0 });
1020
+ releaseFixture = { release, label: "v2.0.0" };
1021
+ });
1022
+
1023
+ test("the component that deployed is recorded, the one that failed is recorded failed, and the lines that settle it are printed", async () => {
1024
+ failApplyFor = "web";
1025
+ expect(await runComponentsPromote(ctx({ release: "v2.0.0" }))).toBe(1);
1026
+ expect(appendReleaseRecordMock).toHaveBeenCalledTimes(1);
1027
+ const [record] = appendReleaseRecordMock.mock.calls[0];
1028
+ expect(record).toMatchObject({ component: "api", env: "prod", releaseDigest: release.releaseDigest });
1029
+ expect(outcomes).toEqual([{
1030
+ version: 1,
1031
+ env: "prod",
1032
+ verb: "promote",
1033
+ runId: "run-9",
1034
+ timestamp: record.timestamp,
1035
+ actor: "bob",
1036
+ releaseDigest: release.releaseDigest,
1037
+ status: "fail",
1038
+ components: [
1039
+ { component: "api", outcome: "deployed", digest: "sha256:api", releaseDigest: release.releaseDigest },
1040
+ { component: "web", outcome: "failed", digest: "sha256:api", releaseDigest: release.releaseDigest },
1041
+ ],
1042
+ }]);
1043
+ expect(pushLifecycleMock).toHaveBeenCalledTimes(1);
1044
+ const said = errors.join("\n");
1045
+ expect(said).toMatch(/"prod" now runs a mix: api deployed, web failed/);
1046
+ expect(said).toMatch(/settle web: chant components redeploy prod --component web, or run the same promote again/);
1047
+ });
1048
+
1049
+ test("--json carries the outcome", async () => {
1050
+ failApplyFor = "web";
1051
+ expect(await runComponentsPromote(ctx({ release: "v2.0.0", json: true }))).toBe(1);
1052
+ expect(JSON.parse(logs.join("\n")).outcome).toMatchObject({ status: "fail", components: [{ outcome: "deployed" }, { outcome: "failed" }] });
1053
+ });
1054
+
1055
+ test("a promote that deploys everything records no outcome", async () => {
1056
+ expect(await runComponentsPromote(ctx({ release: "v2.0.0" }))).toBe(0);
1057
+ expect(appendReleaseRecordMock).toHaveBeenCalledTimes(2);
1058
+ expect(outcomes).toEqual([]);
1059
+ });
1060
+ });