@intentius/chant 0.124.26 → 0.124.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (459) hide show
  1. package/README.md +1 -1
  2. package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
  3. package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
  4. package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  6. package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
  7. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  8. package/dist/__golden__/wave-loop/op-held-on.json +2 -0
  9. package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
  10. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  11. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  12. package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
  13. package/dist/__golden__/wave-loop/op-per-run.json +7 -0
  14. package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
  15. package/dist/__golden__/wave-loop/op-sealed.json +3 -0
  16. package/dist/__golden__/wave-loop/op-spend.json +3 -0
  17. package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
  18. package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
  19. package/dist/change-set-graph.d.ts +56 -0
  20. package/dist/change-set-graph.d.ts.map +1 -0
  21. package/dist/change-set-graph.js +128 -0
  22. package/dist/change-set.d.ts +52 -3
  23. package/dist/change-set.d.ts.map +1 -1
  24. package/dist/change-set.js +35 -4
  25. package/dist/chant-version.d.ts +9 -0
  26. package/dist/chant-version.d.ts.map +1 -0
  27. package/dist/chant-version.js +16 -0
  28. package/dist/cli/commands/build.d.ts +8 -0
  29. package/dist/cli/commands/build.d.ts.map +1 -1
  30. package/dist/cli/commands/build.js +9 -8
  31. package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
  32. package/dist/cli/commands/check-lexicon-docs.js +30 -2
  33. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  34. package/dist/cli/commands/check-lexicon.js +13 -2
  35. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
  36. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
  38. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon.js +17 -8
  40. package/dist/cli/handlers/approve.d.ts.map +1 -1
  41. package/dist/cli/handlers/approve.js +19 -4
  42. package/dist/cli/handlers/build.d.ts.map +1 -1
  43. package/dist/cli/handlers/build.js +28 -0
  44. package/dist/cli/handlers/change-set.d.ts +10 -0
  45. package/dist/cli/handlers/change-set.d.ts.map +1 -1
  46. package/dist/cli/handlers/change-set.js +51 -1
  47. package/dist/cli/handlers/fan-out.d.ts +1 -1
  48. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  49. package/dist/cli/handlers/fan-out.js +14 -3
  50. package/dist/cli/handlers/pr.d.ts +8 -2
  51. package/dist/cli/handlers/pr.d.ts.map +1 -1
  52. package/dist/cli/handlers/pr.js +148 -24
  53. package/dist/cli/handlers/promote.d.ts.map +1 -1
  54. package/dist/cli/handlers/promote.js +256 -21
  55. package/dist/cli/handlers/release.d.ts +34 -0
  56. package/dist/cli/handlers/release.d.ts.map +1 -0
  57. package/dist/cli/handlers/release.js +203 -0
  58. package/dist/cli/handlers/run.d.ts +28 -2
  59. package/dist/cli/handlers/run.d.ts.map +1 -1
  60. package/dist/cli/handlers/run.js +170 -13
  61. package/dist/cli/main.d.ts.map +1 -1
  62. package/dist/cli/main.js +88 -13
  63. package/dist/cli/mcp/workspace-tools.js +1 -1
  64. package/dist/cli/parse-args.d.ts.map +1 -1
  65. package/dist/cli/parse-args.js +49 -3
  66. package/dist/cli/registry.d.ts +29 -3
  67. package/dist/cli/registry.d.ts.map +1 -1
  68. package/dist/cli/run-events.d.ts +29 -0
  69. package/dist/cli/run-events.d.ts.map +1 -0
  70. package/dist/cli/run-events.js +47 -0
  71. package/dist/cli/version.d.ts +3 -5
  72. package/dist/cli/version.d.ts.map +1 -1
  73. package/dist/cli/version.js +3 -13
  74. package/dist/codegen/docs-in-site.d.ts +71 -0
  75. package/dist/codegen/docs-in-site.d.ts.map +1 -0
  76. package/dist/codegen/docs-in-site.js +177 -0
  77. package/dist/codegen/docs-types.d.ts +14 -1
  78. package/dist/codegen/docs-types.d.ts.map +1 -1
  79. package/dist/codegen/docs.d.ts +18 -1
  80. package/dist/codegen/docs.d.ts.map +1 -1
  81. package/dist/codegen/docs.js +40 -7
  82. package/dist/codegen/rehype-base-url.mjs +15 -9
  83. package/dist/components/auto-release.d.ts +4 -1
  84. package/dist/components/auto-release.d.ts.map +1 -1
  85. package/dist/components/auto-release.js +2 -0
  86. package/dist/components/cli-support.d.ts +6 -0
  87. package/dist/components/cli-support.d.ts.map +1 -1
  88. package/dist/components/cli-support.js +10 -9
  89. package/dist/components/driver.d.ts +42 -9
  90. package/dist/components/driver.d.ts.map +1 -1
  91. package/dist/components/driver.js +81 -54
  92. package/dist/components/fan-out-run.d.ts +7 -0
  93. package/dist/components/fan-out-run.d.ts.map +1 -1
  94. package/dist/components/fan-out-run.js +35 -26
  95. package/dist/components/pr-run.d.ts +11 -0
  96. package/dist/components/pr-run.d.ts.map +1 -1
  97. package/dist/components/pr-run.js +20 -1
  98. package/dist/components/promote-order.d.ts +38 -0
  99. package/dist/components/promote-order.d.ts.map +1 -0
  100. package/dist/components/promote-order.js +62 -0
  101. package/dist/components/promote.d.ts +15 -2
  102. package/dist/components/promote.d.ts.map +1 -1
  103. package/dist/components/promote.js +17 -0
  104. package/dist/components/release-compile.d.ts +133 -0
  105. package/dist/components/release-compile.d.ts.map +1 -0
  106. package/dist/components/release-compile.js +376 -0
  107. package/dist/components/release-gate.d.ts +125 -0
  108. package/dist/components/release-gate.d.ts.map +1 -0
  109. package/dist/components/release-gate.js +282 -0
  110. package/dist/components/release-promote.d.ts +96 -0
  111. package/dist/components/release-promote.d.ts.map +1 -0
  112. package/dist/components/release-promote.js +204 -0
  113. package/dist/components/run-progress.d.ts +7 -0
  114. package/dist/components/run-progress.d.ts.map +1 -1
  115. package/dist/components/wave-plan.d.ts +3 -1
  116. package/dist/components/wave-plan.d.ts.map +1 -1
  117. package/dist/components/wave-plan.js +1 -1
  118. package/dist/config.d.ts +7 -0
  119. package/dist/config.d.ts.map +1 -1
  120. package/dist/config.js +1 -0
  121. package/dist/index.d.ts +1 -0
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +2 -0
  124. package/dist/lifecycle/change-set-store.d.ts +67 -0
  125. package/dist/lifecycle/change-set-store.d.ts.map +1 -0
  126. package/dist/lifecycle/change-set-store.js +96 -0
  127. package/dist/lifecycle/commit-sha.d.ts +13 -0
  128. package/dist/lifecycle/commit-sha.d.ts.map +1 -0
  129. package/dist/lifecycle/commit-sha.js +21 -0
  130. package/dist/lifecycle/gate-change-set.d.ts +69 -0
  131. package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
  132. package/dist/lifecycle/gate-change-set.js +106 -0
  133. package/dist/lifecycle/gate-ledger.d.ts +49 -1
  134. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  135. package/dist/lifecycle/gate-ledger.js +18 -0
  136. package/dist/lifecycle/release-cut.d.ts +107 -0
  137. package/dist/lifecycle/release-cut.d.ts.map +1 -0
  138. package/dist/lifecycle/release-cut.js +273 -0
  139. package/dist/lifecycle/release-ledger.d.ts +54 -0
  140. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  141. package/dist/lifecycle/release-ledger.js +53 -20
  142. package/dist/lifecycle/release-store.d.ts +190 -0
  143. package/dist/lifecycle/release-store.d.ts.map +1 -0
  144. package/dist/lifecycle/release-store.js +318 -0
  145. package/dist/lifecycle/run-ledger.d.ts +4 -0
  146. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  147. package/dist/lifecycle/run-ledger.js +2 -0
  148. package/dist/op/activities/change-coverage.d.ts.map +1 -1
  149. package/dist/op/activities/change-coverage.js +4 -2
  150. package/dist/op/activities/source-release.d.ts.map +1 -1
  151. package/dist/op/activities/source-release.js +7 -3
  152. package/dist/op/activities/source-rollback.d.ts.map +1 -1
  153. package/dist/op/activities/source-rollback.js +7 -3
  154. package/dist/op/activity-registry.d.ts +6 -3
  155. package/dist/op/activity-registry.d.ts.map +1 -1
  156. package/dist/op/activity-registry.js +7 -5
  157. package/dist/op/discover.d.ts.map +1 -1
  158. package/dist/op/discover.js +14 -7
  159. package/dist/op/gate-name.d.ts +11 -2
  160. package/dist/op/gate-name.d.ts.map +1 -1
  161. package/dist/op/gate-name.js +12 -3
  162. package/dist/op/gate-run-locator.d.ts +22 -0
  163. package/dist/op/gate-run-locator.d.ts.map +1 -1
  164. package/dist/op/gate-run-locator.js +31 -0
  165. package/dist/op/gate.d.ts +27 -3
  166. package/dist/op/gate.d.ts.map +1 -1
  167. package/dist/op/gate.js +57 -3
  168. package/dist/op/local-executor.d.ts +36 -1
  169. package/dist/op/local-executor.d.ts.map +1 -1
  170. package/dist/op/local-executor.js +158 -52
  171. package/dist/op/op-waves-run.d.ts.map +1 -1
  172. package/dist/op/op-waves-run.js +4 -0
  173. package/dist/op/run-live.d.ts +2 -0
  174. package/dist/op/run-live.d.ts.map +1 -1
  175. package/dist/op/run-live.js +1 -1
  176. package/dist/op/runtime.d.ts +46 -0
  177. package/dist/op/runtime.d.ts.map +1 -1
  178. package/dist/op/runtimes/local.d.ts.map +1 -1
  179. package/dist/op/runtimes/local.js +12 -2
  180. package/dist/point-decision.d.ts +2 -0
  181. package/dist/point-decision.d.ts.map +1 -1
  182. package/dist/point-decision.js +9 -2
  183. package/dist/run-event-store.d.ts +77 -0
  184. package/dist/run-event-store.d.ts.map +1 -0
  185. package/dist/run-event-store.js +204 -0
  186. package/dist/run-event-stream.d.ts +132 -0
  187. package/dist/run-event-stream.d.ts.map +1 -0
  188. package/dist/run-event-stream.js +238 -0
  189. package/dist/run-event.d.ts +267 -0
  190. package/dist/run-event.d.ts.map +1 -0
  191. package/dist/run-event.js +207 -0
  192. package/dist/wave-runtime.mjs +225 -242
  193. package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
  194. package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
  195. package/dist/workspace/__fixtures__/run-event-check.js +59 -0
  196. package/dist/workspace/change-set-show.d.ts +56 -0
  197. package/dist/workspace/change-set-show.d.ts.map +1 -0
  198. package/dist/workspace/change-set-show.js +54 -0
  199. package/dist/workspace/change-set-show.schema.json +102 -0
  200. package/dist/workspace/change-set.schema.json +28 -1
  201. package/dist/workspace/changes-cli.d.ts.map +1 -1
  202. package/dist/workspace/changes-cli.js +10 -1
  203. package/dist/workspace/changes.d.ts +37 -0
  204. package/dist/workspace/changes.d.ts.map +1 -1
  205. package/dist/workspace/changes.js +49 -2
  206. package/dist/workspace/changes.schema.json +135 -0
  207. package/dist/workspace/ci-green-cli.d.ts +19 -4
  208. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  209. package/dist/workspace/ci-green-cli.js +75 -12
  210. package/dist/workspace/ci-green-forge.d.ts +20 -3
  211. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  212. package/dist/workspace/ci-green-forge.js +6 -4
  213. package/dist/workspace/ci-green-workflow.d.ts +11 -0
  214. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  215. package/dist/workspace/ci-green-workflow.js +6 -0
  216. package/dist/workspace/ci-green.d.ts +273 -13
  217. package/dist/workspace/ci-green.d.ts.map +1 -1
  218. package/dist/workspace/ci-green.js +449 -38
  219. package/dist/workspace/ci-last-green.schema.json +32 -2
  220. package/dist/workspace/ci-list.schema.json +525 -0
  221. package/dist/workspace/commit-links.d.ts +85 -0
  222. package/dist/workspace/commit-links.d.ts.map +1 -0
  223. package/dist/workspace/commit-links.js +163 -0
  224. package/dist/workspace/declaration.d.ts +19 -0
  225. package/dist/workspace/declaration.d.ts.map +1 -1
  226. package/dist/workspace/declaration.js +1 -1
  227. package/dist/workspace/declaration.schema.json +12 -0
  228. package/dist/workspace/identity-gate-rule.d.ts +37 -0
  229. package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
  230. package/dist/workspace/identity-gate-rule.js +72 -7
  231. package/dist/workspace/identity.d.ts +12 -0
  232. package/dist/workspace/identity.d.ts.map +1 -1
  233. package/dist/workspace/identity.js +13 -1
  234. package/dist/workspace/intent.d.ts +6 -1
  235. package/dist/workspace/intent.d.ts.map +1 -1
  236. package/dist/workspace/intent.js +51 -1
  237. package/dist/workspace/ls.d.ts +4 -0
  238. package/dist/workspace/ls.d.ts.map +1 -1
  239. package/dist/workspace/ls.js +2 -0
  240. package/dist/workspace/ls.schema.json +3 -1
  241. package/dist/workspace/member-pipeline.d.ts +6 -0
  242. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  243. package/dist/workspace/member-pipeline.js +6 -0
  244. package/dist/workspace/promotion-order.d.ts +41 -0
  245. package/dist/workspace/promotion-order.d.ts.map +1 -0
  246. package/dist/workspace/promotion-order.js +95 -0
  247. package/dist/workspace/reason-codes.d.ts +2 -0
  248. package/dist/workspace/reason-codes.d.ts.map +1 -1
  249. package/dist/workspace/reason-codes.js +4 -1
  250. package/dist/workspace/releases.d.ts +72 -0
  251. package/dist/workspace/releases.d.ts.map +1 -0
  252. package/dist/workspace/releases.js +94 -0
  253. package/dist/workspace/releases.schema.json +391 -0
  254. package/dist/workspace/run-event.schema.json +288 -0
  255. package/dist/workspace/status-gates.d.ts +26 -1
  256. package/dist/workspace/status-gates.d.ts.map +1 -1
  257. package/dist/workspace/status-gates.js +31 -2
  258. package/dist/workspace/status-stewards.d.ts +4 -0
  259. package/dist/workspace/status-stewards.d.ts.map +1 -1
  260. package/dist/workspace/status-stewards.js +2 -0
  261. package/dist/workspace/status.d.ts +39 -0
  262. package/dist/workspace/status.d.ts.map +1 -1
  263. package/dist/workspace/status.js +48 -1
  264. package/dist/workspace/status.schema.json +70 -1
  265. package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
  266. package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
  267. package/dist/workspace/trust/ci-tag-statement.js +123 -0
  268. package/dist/workspace/trust/op-run-statement.d.ts +91 -0
  269. package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
  270. package/dist/workspace/trust/op-run-statement.js +143 -0
  271. package/dist/workspace/trust/rotation.d.ts +18 -2
  272. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  273. package/dist/workspace/trust/rotation.js +65 -16
  274. package/dist/workspace/trust/seal.d.ts +11 -0
  275. package/dist/workspace/trust/seal.d.ts.map +1 -1
  276. package/dist/workspace/trust/seal.js +9 -0
  277. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  278. package/dist/workspace/trust/ssh-commit.js +10 -10
  279. package/package.json +1 -1
  280. package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
  281. package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
  282. package/src/__golden__/wave-loop/fan-out-only.json +6 -0
  283. package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  284. package/src/__golden__/wave-loop/op-cost-member.json +3 -0
  285. package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  286. package/src/__golden__/wave-loop/op-held-on.json +2 -0
  287. package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
  288. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  289. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  290. package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
  291. package/src/__golden__/wave-loop/op-per-run.json +7 -0
  292. package/src/__golden__/wave-loop/op-pr-review.json +7 -0
  293. package/src/__golden__/wave-loop/op-sealed.json +3 -0
  294. package/src/__golden__/wave-loop/op-spend.json +3 -0
  295. package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
  296. package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
  297. package/src/change-set-graph.test.ts +72 -0
  298. package/src/change-set-graph.ts +133 -0
  299. package/src/change-set.ts +76 -6
  300. package/src/chant-version.ts +16 -0
  301. package/src/cli/commands/build.ts +19 -8
  302. package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
  303. package/src/cli/commands/check-lexicon-docs.ts +39 -2
  304. package/src/cli/commands/check-lexicon.ts +14 -2
  305. package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
  306. package/src/cli/commands/init-lexicon.test.ts +25 -0
  307. package/src/cli/commands/init-lexicon.ts +18 -8
  308. package/src/cli/compiled-cli.e2e.test.ts +31 -6
  309. package/src/cli/handlers/approve-release.test.ts +80 -0
  310. package/src/cli/handlers/approve.ts +18 -2
  311. package/src/cli/handlers/build.test.ts +52 -0
  312. package/src/cli/handlers/build.ts +29 -0
  313. package/src/cli/handlers/change-set.ts +49 -1
  314. package/src/cli/handlers/fan-out.test.ts +48 -0
  315. package/src/cli/handlers/fan-out.ts +15 -3
  316. package/src/cli/handlers/operator.test.ts +37 -0
  317. package/src/cli/handlers/pr.test.ts +194 -0
  318. package/src/cli/handlers/pr.ts +168 -34
  319. package/src/cli/handlers/promote.test.ts +389 -5
  320. package/src/cli/handlers/promote.ts +279 -20
  321. package/src/cli/handlers/release-implicit.e2e.test.ts +153 -0
  322. package/src/cli/handlers/release.e2e.test.ts +85 -0
  323. package/src/cli/handlers/release.ts +206 -0
  324. package/src/cli/handlers/run-auto-release.test.ts +71 -0
  325. package/src/cli/handlers/run-events-cmd.test.ts +92 -0
  326. package/src/cli/handlers/run.test.ts +30 -0
  327. package/src/cli/handlers/run.ts +176 -15
  328. package/src/cli/main.test.ts +7 -0
  329. package/src/cli/main.ts +88 -13
  330. package/src/cli/mcp/never-over-mcp.test.ts +2 -0
  331. package/src/cli/mcp/workspace-tools.ts +1 -1
  332. package/src/cli/parse-args.ts +37 -3
  333. package/src/cli/registry.ts +29 -3
  334. package/src/cli/run-events.ts +54 -0
  335. package/src/cli/version.ts +3 -13
  336. package/src/codegen/docs-in-site.test.ts +166 -0
  337. package/src/codegen/docs-in-site.ts +182 -0
  338. package/src/codegen/docs-site/AgentPrompt.astro +26 -1
  339. package/src/codegen/docs-types.ts +14 -1
  340. package/src/codegen/docs.ts +60 -7
  341. package/src/codegen/rehype-base-url.d.mts +3 -1
  342. package/src/codegen/rehype-base-url.mjs +15 -9
  343. package/src/codegen/rehype-base-url.test.ts +34 -0
  344. package/src/components/auto-release.test.ts +21 -0
  345. package/src/components/auto-release.ts +6 -1
  346. package/src/components/cli-support.test.ts +82 -0
  347. package/src/components/cli-support.ts +16 -9
  348. package/src/components/driver.ts +129 -56
  349. package/src/components/fan-out-run.ts +56 -35
  350. package/src/components/pr-run.ts +30 -1
  351. package/src/components/promote-order.test.ts +90 -0
  352. package/src/components/promote-order.ts +75 -0
  353. package/src/components/promote.test.ts +10 -1
  354. package/src/components/promote.ts +26 -1
  355. package/src/components/release-compile.test.ts +147 -0
  356. package/src/components/release-compile.ts +445 -0
  357. package/src/components/release-gate.test.ts +215 -0
  358. package/src/components/release-gate.ts +341 -0
  359. package/src/components/release-promote.test.ts +159 -0
  360. package/src/components/release-promote.ts +232 -0
  361. package/src/components/run-progress.ts +7 -0
  362. package/src/components/wave-plan.ts +1 -1
  363. package/src/config.ts +7 -1
  364. package/src/index.ts +2 -0
  365. package/src/lifecycle/change-set-store.test.ts +217 -0
  366. package/src/lifecycle/change-set-store.ts +112 -0
  367. package/src/lifecycle/commit-sha.ts +23 -0
  368. package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
  369. package/src/lifecycle/gate-change-set.ts +133 -0
  370. package/src/lifecycle/gate-ledger.test.ts +33 -0
  371. package/src/lifecycle/gate-ledger.ts +66 -1
  372. package/src/lifecycle/release-cut-signed.test.ts +180 -0
  373. package/src/lifecycle/release-cut.test.ts +411 -0
  374. package/src/lifecycle/release-cut.ts +367 -0
  375. package/src/lifecycle/release-ledger.test.ts +14 -0
  376. package/src/lifecycle/release-ledger.ts +85 -17
  377. package/src/lifecycle/release-store.ts +423 -0
  378. package/src/lifecycle/run-ledger.test.ts +10 -0
  379. package/src/lifecycle/run-ledger.ts +6 -0
  380. package/src/op/activities/change-coverage.ts +4 -1
  381. package/src/op/activities/source-release.ts +8 -3
  382. package/src/op/activities/source-rollback.ts +8 -4
  383. package/src/op/activity-registry.ts +7 -5
  384. package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
  385. package/src/op/discover-root.test.ts +39 -0
  386. package/src/op/discover.ts +15 -8
  387. package/src/op/gate-name.ts +13 -3
  388. package/src/op/gate-release.test.ts +126 -0
  389. package/src/op/gate-run-locator.test.ts +110 -0
  390. package/src/op/gate-run-locator.ts +35 -0
  391. package/src/op/gate.ts +88 -6
  392. package/src/op/local-executor.ts +207 -52
  393. package/src/op/op-waves-run.ts +4 -0
  394. package/src/op/run-events.test.ts +166 -0
  395. package/src/op/run-live.ts +1 -1
  396. package/src/op/runtime.ts +39 -0
  397. package/src/op/runtimes/local.test.ts +16 -0
  398. package/src/op/runtimes/local.ts +13 -2
  399. package/src/point-decision.ts +12 -2
  400. package/src/run-event-store.test.ts +144 -0
  401. package/src/run-event-store.ts +213 -0
  402. package/src/run-event-stream.test.ts +175 -0
  403. package/src/run-event-stream.ts +306 -0
  404. package/src/run-event.test.ts +277 -0
  405. package/src/run-event.ts +451 -0
  406. package/src/wave-loop-golden.test.ts +9 -0
  407. package/src/workspace/__fixtures__/run-event-check.ts +58 -0
  408. package/src/workspace/ancestry-index.test.ts +93 -0
  409. package/src/workspace/change-set-show.schema.json +102 -0
  410. package/src/workspace/change-set-show.ts +72 -0
  411. package/src/workspace/change-set.schema.json +28 -1
  412. package/src/workspace/changes-cli.ts +10 -1
  413. package/src/workspace/changes.schema.json +135 -0
  414. package/src/workspace/changes.test.ts +1 -1
  415. package/src/workspace/changes.ts +84 -2
  416. package/src/workspace/ci-green-cli.ts +75 -13
  417. package/src/workspace/ci-green-forge.ts +24 -4
  418. package/src/workspace/ci-green-runs.test.ts +242 -0
  419. package/src/workspace/ci-green-signed.test.ts +302 -0
  420. package/src/workspace/ci-green-workflow.test.ts +16 -0
  421. package/src/workspace/ci-green-workflow.ts +12 -0
  422. package/src/workspace/ci-green.test.ts +131 -2
  423. package/src/workspace/ci-green.ts +624 -42
  424. package/src/workspace/ci-last-green.schema.json +32 -2
  425. package/src/workspace/ci-list.schema.json +525 -0
  426. package/src/workspace/commit-links.test.ts +279 -0
  427. package/src/workspace/commit-links.ts +183 -0
  428. package/src/workspace/declaration.schema.json +12 -0
  429. package/src/workspace/declaration.ts +22 -2
  430. package/src/workspace/identity-gate-rule.ts +91 -7
  431. package/src/workspace/identity.ts +14 -1
  432. package/src/workspace/intent.ts +48 -1
  433. package/src/workspace/ls.schema.json +3 -1
  434. package/src/workspace/ls.ts +6 -0
  435. package/src/workspace/member-pipeline.test.ts +17 -0
  436. package/src/workspace/member-pipeline.ts +9 -0
  437. package/src/workspace/promotion-order.ts +97 -0
  438. package/src/workspace/read-contract.e2e.test.ts +4 -1
  439. package/src/workspace/reason-codes.test.ts +2 -1
  440. package/src/workspace/reason-codes.ts +4 -1
  441. package/src/workspace/releases.schema.json +391 -0
  442. package/src/workspace/releases.test.ts +108 -0
  443. package/src/workspace/releases.ts +122 -0
  444. package/src/workspace/run-event.schema.json +288 -0
  445. package/src/workspace/status-contract.test.ts +52 -1
  446. package/src/workspace/status-gates.ts +56 -1
  447. package/src/workspace/status-stewards.ts +6 -0
  448. package/src/workspace/status.schema.json +70 -1
  449. package/src/workspace/status.ts +80 -1
  450. package/src/workspace/trust/ci-tag-statement.ts +151 -0
  451. package/src/workspace/trust/op-run-statement.ts +148 -0
  452. package/src/workspace/trust/rotation.ts +61 -14
  453. package/src/workspace/trust/seal.ts +10 -0
  454. package/src/workspace/trust/ssh-commit.ts +10 -9
  455. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
  456. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
  457. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
  458. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
  459. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
@@ -0,0 +1,391 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://intentius.io/chant/schemas/workspace/releases/v1/releases.schema.json",
4
+ "title": "chant release list output",
5
+ "description": "What `chant release list --json` prints (#4026, ws-104): every release of one member, each the compiled output of one green commit, with its artifact digests, one target per environment, its cuts and the environments that run it. It reads the local chant/lifecycle branch and never fetches. Version 1 of the read contract. Readers ignore fields they do not know; a field is only ever added within a version. The codes are a closed list, each in the one closed list of `reason-codes.ts`. Contract version 1 of this document is written by chant 0.124.28 and newer; the read contract as a whole by chant 0.81.0 and newer.",
6
+ "oneOf": [
7
+ {
8
+ "$ref": "#/$defs/result"
9
+ },
10
+ {
11
+ "$ref": "#/$defs/failure"
12
+ }
13
+ ],
14
+ "$defs": {
15
+ "result": {
16
+ "type": "object",
17
+ "description": "Exit code 0, with or without releases.",
18
+ "required": [
19
+ "$schema",
20
+ "contract",
21
+ "chant",
22
+ "member",
23
+ "releases",
24
+ "reason"
25
+ ],
26
+ "properties": {
27
+ "$schema": {
28
+ "const": "https://intentius.io/chant/schemas/workspace/releases/v1/releases.schema.json"
29
+ },
30
+ "contract": {
31
+ "const": 1
32
+ },
33
+ "chant": {
34
+ "type": "string"
35
+ },
36
+ "error": false,
37
+ "member": {
38
+ "type": [
39
+ "string",
40
+ "null"
41
+ ],
42
+ "description": "The workspace member read, its path of names joined with / when nested, or null for a project outside a workspace (the flat ledger layout)."
43
+ },
44
+ "releases": {
45
+ "type": "array",
46
+ "description": "Newest cut first. A release stored with no cut comes last.",
47
+ "items": {
48
+ "$ref": "#/$defs/release"
49
+ }
50
+ },
51
+ "reason": {
52
+ "description": "Null when everything was read. ledger-malformed when some release files or cut lines could not be read; the rest are listed.",
53
+ "oneOf": [
54
+ {
55
+ "type": "null"
56
+ },
57
+ {
58
+ "type": "object",
59
+ "required": [
60
+ "code",
61
+ "message"
62
+ ],
63
+ "properties": {
64
+ "code": {
65
+ "enum": [
66
+ "ledger-malformed"
67
+ ]
68
+ },
69
+ "message": {
70
+ "type": "string"
71
+ }
72
+ }
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ },
78
+ "release": {
79
+ "type": "object",
80
+ "required": [
81
+ "releaseDigest",
82
+ "label",
83
+ "member",
84
+ "commit",
85
+ "artifactDigests",
86
+ "targets",
87
+ "cuts",
88
+ "environments"
89
+ ],
90
+ "properties": {
91
+ "releaseDigest": {
92
+ "type": "string",
93
+ "pattern": "^(jcs1-)?sha256:[0-9a-f]{64}$",
94
+ "description": "computePlanDigest(\"release\", subject) over member, commit, artifactDigests, and each target's env, templates and the name and value of each param. The file is _releases/<releaseDigest>.json on chant/lifecycle, with : written as _."
95
+ },
96
+ "label": {
97
+ "type": [
98
+ "string",
99
+ "null"
100
+ ],
101
+ "description": "The release's label, from its cuts, or null. A label names one release in a member and a release takes at most one label."
102
+ },
103
+ "member": {
104
+ "type": [
105
+ "string",
106
+ "null"
107
+ ]
108
+ },
109
+ "commit": {
110
+ "type": "string",
111
+ "pattern": "^[0-9a-f]{40,64}$",
112
+ "description": "The full sha of the green commit the release was cut from."
113
+ },
114
+ "artifactDigests": {
115
+ "type": "array",
116
+ "description": "Each image and asset built once from the commit, sorted by component, then path. A config-only release carries the previous release's entries.",
117
+ "items": {
118
+ "type": "object",
119
+ "required": [
120
+ "component",
121
+ "kind",
122
+ "path",
123
+ "digest"
124
+ ],
125
+ "properties": {
126
+ "component": {
127
+ "type": "string"
128
+ },
129
+ "kind": {
130
+ "enum": [
131
+ "image",
132
+ "asset"
133
+ ]
134
+ },
135
+ "path": {
136
+ "type": "string"
137
+ },
138
+ "digest": {
139
+ "type": "string",
140
+ "minLength": 1
141
+ }
142
+ }
143
+ }
144
+ },
145
+ "targets": {
146
+ "type": "array",
147
+ "description": "One per environment the member declares, sorted by env.",
148
+ "items": {
149
+ "$ref": "#/$defs/target"
150
+ }
151
+ },
152
+ "cuts": {
153
+ "type": "array",
154
+ "description": "Each cut of the release, oldest first.",
155
+ "items": {
156
+ "$ref": "#/$defs/cut"
157
+ }
158
+ },
159
+ "environments": {
160
+ "type": "array",
161
+ "description": "The environments that run the release, sorted by env: in each, the components whose latest deploy record names this release in releaseDigest. A promote with --release writes that field. An environment can run components of several releases.",
162
+ "items": {
163
+ "type": "object",
164
+ "required": [
165
+ "env",
166
+ "components"
167
+ ],
168
+ "properties": {
169
+ "env": {
170
+ "type": "string"
171
+ },
172
+ "components": {
173
+ "type": "array",
174
+ "items": {
175
+ "type": "string"
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ },
183
+ "target": {
184
+ "type": "object",
185
+ "required": [
186
+ "env",
187
+ "templates",
188
+ "params"
189
+ ],
190
+ "properties": {
191
+ "env": {
192
+ "type": "string"
193
+ },
194
+ "templates": {
195
+ "type": "array",
196
+ "description": "The files the environment's deploys read, compiled for it, sorted by component, then path.",
197
+ "items": {
198
+ "type": "object",
199
+ "required": [
200
+ "component",
201
+ "path",
202
+ "digest"
203
+ ],
204
+ "properties": {
205
+ "component": {
206
+ "type": [
207
+ "string",
208
+ "null"
209
+ ],
210
+ "description": "The component whose deploy reads it, or null for a project with no components."
211
+ },
212
+ "path": {
213
+ "type": "string",
214
+ "description": "The path the deploy step names, relative to the member's directory."
215
+ },
216
+ "digest": {
217
+ "type": "string",
218
+ "pattern": "^(jcs1-)?sha256:[0-9a-f]{64}$",
219
+ "description": "sha256 of the file's bytes."
220
+ }
221
+ }
222
+ }
223
+ },
224
+ "params": {
225
+ "type": "array",
226
+ "description": "The resolved build parameters the environment was compiled with, sorted by name. source is outside the release digest.",
227
+ "items": {
228
+ "type": "object",
229
+ "required": [
230
+ "name",
231
+ "value",
232
+ "source"
233
+ ],
234
+ "properties": {
235
+ "name": {
236
+ "type": "string"
237
+ },
238
+ "value": {
239
+ "oneOf": [
240
+ {
241
+ "type": "string"
242
+ },
243
+ {
244
+ "type": "number"
245
+ },
246
+ {
247
+ "type": "boolean"
248
+ }
249
+ ]
250
+ },
251
+ "source": {
252
+ "type": "string",
253
+ "description": "cli, params-file, env or default."
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+ },
260
+ "cut": {
261
+ "type": "object",
262
+ "required": [
263
+ "label",
264
+ "actor",
265
+ "timestamp",
266
+ "runOrigin",
267
+ "signature"
268
+ ],
269
+ "properties": {
270
+ "label": {
271
+ "type": [
272
+ "string",
273
+ "null"
274
+ ]
275
+ },
276
+ "actor": {
277
+ "type": "string"
278
+ },
279
+ "timestamp": {
280
+ "type": "string",
281
+ "description": "ISO 8601."
282
+ },
283
+ "runOrigin": {
284
+ "oneOf": [
285
+ {
286
+ "type": "null"
287
+ },
288
+ {
289
+ "type": "object",
290
+ "required": [
291
+ "forge"
292
+ ],
293
+ "properties": {
294
+ "forge": {
295
+ "enum": [
296
+ "github",
297
+ "gitlab",
298
+ "op",
299
+ "local"
300
+ ]
301
+ },
302
+ "repo": {
303
+ "type": "string"
304
+ },
305
+ "url": {
306
+ "type": "string"
307
+ }
308
+ }
309
+ }
310
+ ],
311
+ "description": "The CI run the cut ran in: the forge (github for GitHub and Forgejo, gitlab), the repo or project, and a link to the run. Null outside CI."
312
+ },
313
+ "signature": {
314
+ "type": [
315
+ "string",
316
+ "null"
317
+ ],
318
+ "description": "Added by #4031, written by chant 0.124.28 and newer. The cut's armored ssh signature (ssh-keygen -Y sign, namespace chant-release) by the actor's key, over the release digest, the label (empty for none), the actor and the timestamp, joined by LF with no final newline. Null for an unsigned cut. A reader checks it against .chant/allowed_signers at base; signatureCheck is chant's own check."
319
+ },
320
+ "signatureCheck": {
321
+ "type": "object",
322
+ "description": "Added within contract version 1 by #4031, written by chant 0.124.28 and newer. Whether signature verifies for actor against the signers file at base and, when the declaration at base names a release gate in identity.gates with a class, whether the role grants in .chant/trust.json at base put actor in it. Judged on every read.",
323
+ "required": [
324
+ "status",
325
+ "key",
326
+ "reason"
327
+ ],
328
+ "properties": {
329
+ "status": {
330
+ "enum": [
331
+ "verified",
332
+ "unsigned",
333
+ "unverified"
334
+ ],
335
+ "description": "verified: the signature verifies, and the actor is in the release gate's class when it names one. unsigned: the cut has no signature. unverified: it has one that does not count, and reason says why."
336
+ },
337
+ "key": {
338
+ "type": [
339
+ "string",
340
+ "null"
341
+ ],
342
+ "description": "The fingerprint (SHA256:...) of the key in the signature, or null for an unsigned cut or a signature that can't be read. Reported, never trusted."
343
+ },
344
+ "reason": {
345
+ "type": "string"
346
+ }
347
+ }
348
+ }
349
+ }
350
+ },
351
+ "failure": {
352
+ "type": "object",
353
+ "description": "Nothing was read. Exit code 1.",
354
+ "required": [
355
+ "$schema",
356
+ "contract",
357
+ "chant",
358
+ "error"
359
+ ],
360
+ "properties": {
361
+ "$schema": {
362
+ "const": "https://intentius.io/chant/schemas/workspace/releases/v1/releases.schema.json"
363
+ },
364
+ "contract": {
365
+ "const": 1
366
+ },
367
+ "chant": {
368
+ "type": "string"
369
+ },
370
+ "error": {
371
+ "type": "object",
372
+ "required": [
373
+ "code",
374
+ "message"
375
+ ],
376
+ "properties": {
377
+ "code": {
378
+ "enum": [
379
+ "not-a-git-repository",
380
+ "ledger-unreadable"
381
+ ]
382
+ },
383
+ "message": {
384
+ "type": "string"
385
+ }
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The `releases` read (#4026, ws-104): releases with their targets, cuts and
3
+ * the environments that run them, held to `releases.schema.json`.
4
+ */
5
+
6
+ import { describe, test, expect } from "vitest";
7
+ import { withTestDir } from "@intentius/chant-test-utils";
8
+ import { spawnSync } from "node:child_process";
9
+ import { writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { contract } from "./__fixtures__/contract-repo";
12
+ import releasesSchema from "./releases.schema.json";
13
+ import { releasesDocument, RELEASES_SCHEMA_ID } from "./releases";
14
+ import { appendCut, makeRelease, persistRelease } from "../lifecycle/release-store";
15
+ import { appendReleaseRecord } from "../lifecycle/release-ledger";
16
+ import { writeLedgerFiles } from "../lifecycle/git";
17
+
18
+ const { expectValid } = contract(releasesSchema);
19
+
20
+ function initRepo(dir: string): void {
21
+ for (const args of [["init", "-q", "-b", "main"], ["config", "user.email", "t@chant.dev"], ["config", "user.name", "T"]]) spawnSync("git", args, { cwd: dir });
22
+ writeFileSync(join(dir, "README.md"), "x\n");
23
+ spawnSync("git", ["add", "."], { cwd: dir });
24
+ spawnSync("git", ["commit", "-q", "-m", "init"], { cwd: dir });
25
+ }
26
+
27
+ const release = (commit: string) =>
28
+ makeRelease({
29
+ member: null,
30
+ commit,
31
+ artifactDigests: [{ component: "api", kind: "image", path: "api.tar", digest: `sha256:${"a".repeat(64)}` }],
32
+ targets: [
33
+ { env: "staging", templates: [{ component: "api", path: "t.json", digest: `sha256:${"b".repeat(64)}` }], params: [{ name: "tier", value: "small", source: "default" }] },
34
+ { env: "prod", templates: [{ component: "api", path: "t.json", digest: `sha256:${"c".repeat(64)}` }], params: [{ name: "tier", value: "large", source: "default" }] },
35
+ ],
36
+ });
37
+
38
+ const deploy = (env: string, component: string, releaseDigest?: string, timestamp = "2026-10-16T12:00:00.000Z") =>
39
+ appendReleaseRecord({ component, env, digest: `sha256:${"a".repeat(64)}`, gitSha: "1".repeat(40), runId: "r", timestamp, actor: "ci", ...(releaseDigest ? { releaseDigest } : {}) }, { cwd: dirOf.current });
40
+ const dirOf = { current: "" };
41
+
42
+ describe("releasesDocument", () => {
43
+ test("lists each release with its targets, cuts and the environments whose latest deploy names it", async () => {
44
+ await withTestDir(async (dir) => {
45
+ dirOf.current = dir;
46
+ initRepo(dir);
47
+ const r1 = release("1".repeat(40));
48
+ const r2 = release("2".repeat(40));
49
+ await persistRelease(r1, [], { cwd: dir });
50
+ await persistRelease(r2, [], { cwd: dir });
51
+ await appendCut({ releaseDigest: r1.releaseDigest, label: "v1", actor: "alice", timestamp: "2026-10-16T10:00:00.000Z", runOrigin: null }, { cwd: dir });
52
+ await appendCut({ releaseDigest: r2.releaseDigest, label: "v2", actor: "bob", timestamp: "2026-10-16T11:00:00.000Z", runOrigin: { forge: "github", repo: "o/r" } }, { cwd: dir });
53
+ await deploy("staging", "api", r2.releaseDigest);
54
+ await deploy("prod", "api", r1.releaseDigest);
55
+ await deploy("staging", "web");
56
+
57
+ const doc = await releasesDocument({ cwd: dir });
58
+ expectValid(doc);
59
+ if ("error" in doc) throw new Error(doc.error.message);
60
+ expect(doc).toMatchObject({ $schema: RELEASES_SCHEMA_ID, contract: 1, member: null, reason: null });
61
+ expect(doc.releases.map((r) => [r.label, r.environments])).toEqual([
62
+ ["v2", [{ env: "staging", components: ["api"] }]],
63
+ ["v1", [{ env: "prod", components: ["api"] }]],
64
+ ]);
65
+ expect(doc.releases[0]).toMatchObject({
66
+ releaseDigest: r2.releaseDigest,
67
+ commit: "2".repeat(40),
68
+ artifactDigests: r2.artifactDigests,
69
+ targets: r2.targets,
70
+ cuts: [{ label: "v2", actor: "bob", timestamp: "2026-10-16T11:00:00.000Z", runOrigin: { forge: "github", repo: "o/r" }, signature: null }],
71
+ });
72
+ });
73
+ });
74
+
75
+ test("a release file that is not a release and a cut line that is not a cut are left out under ledger-malformed", async () => {
76
+ await withTestDir(async (dir) => {
77
+ dirOf.current = dir;
78
+ initRepo(dir);
79
+ const r = release("3".repeat(40));
80
+ await persistRelease(r, [], { cwd: dir });
81
+ await writeLedgerFiles(
82
+ [
83
+ { path: `_releases/jcs1-sha256_${"9".repeat(64)}.json`, content: "{not json" },
84
+ { path: "_releases/cuts.jsonl", content: `{"version":1}\n` },
85
+ ],
86
+ "broken",
87
+ { cwd: dir },
88
+ );
89
+ const doc = await releasesDocument({ cwd: dir });
90
+ expectValid(doc);
91
+ if ("error" in doc) throw new Error(doc.error.message);
92
+ expect(doc.releases.map((x) => x.releaseDigest)).toEqual([r.releaseDigest]);
93
+ expect(doc.reason).toEqual({ code: "ledger-malformed", message: "1 release file(s) and 1 cut line(s) could not be read and are left out" });
94
+ });
95
+ });
96
+
97
+ test("an empty ledger lists no releases, and outside git the read fails with not-a-git-repository", async () => {
98
+ await withTestDir(async (dir) => {
99
+ const outside = await releasesDocument({ cwd: dir });
100
+ expectValid(outside);
101
+ expect(outside).toMatchObject({ error: { code: "not-a-git-repository" } });
102
+ initRepo(dir);
103
+ const empty = await releasesDocument({ cwd: dir });
104
+ expectValid(empty);
105
+ expect(empty).toMatchObject({ releases: [], reason: null });
106
+ });
107
+ });
108
+ });
@@ -0,0 +1,122 @@
1
+ /**
2
+ * The `releases` read (#4026, ws-104): every release of one member with its
3
+ * targets, its cuts, and the environments that run it.
4
+ *
5
+ * `chant release list --json` prints this document. It reads the local
6
+ * `chant/lifecycle` branch and never fetches. An environment runs a release
7
+ * for the components whose latest deploy record there names the release in
8
+ * `releaseDigest`. A promote under a release writes that field; deploys made
9
+ * any other way do not name a release yet (#4043), so they list nowhere.
10
+ *
11
+ * Each cut carries `signatureCheck` (#4031): whether its signature verifies
12
+ * for its actor against the signers at base, and, when the declaration names
13
+ * a `release` gate with a class, whether the actor is in it.
14
+ *
15
+ * A release file that is not a release, or whose content does not hash to its
16
+ * digest, and a cut line that is not a cut, are left out and counted under
17
+ * `reason` with `ledger-malformed`; the rest are listed. A read that fails
18
+ * outright is the failure `ledger-unreadable`.
19
+ */
20
+
21
+ import { execFileSync } from "node:child_process";
22
+ import { latestPerComponent, listReleaseEnvironments, readReleaseLedger } from "../lifecycle/release-ledger";
23
+ import { readAllReleases, readCuts, type Release, type ReleaseCut } from "../lifecycle/release-store";
24
+ import { resolveMemberLedger } from "../lifecycle/member-ledger";
25
+ import { readerVersion } from "./declaration";
26
+ import { checkReleaseSignature, type GateAdmissionRule, type ReleaseSignatureCheck } from "./identity-gate-rule";
27
+
28
+ export const RELEASES_CONTRACT_VERSION = 1;
29
+ export const RELEASES_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/releases/v1/releases.schema.json";
30
+
31
+ /** Why the read stopped. Closed; each code is in `reason-codes.ts`. */
32
+ export const RELEASES_ERROR_CODES = ["not-a-git-repository", "ledger-unreadable"] as const;
33
+ /** Why some entries were left out. */
34
+ export const RELEASES_REASON_CODES = ["ledger-malformed"] as const;
35
+
36
+ /** One release as the document lists it. */
37
+ export interface ReleaseEntry extends Omit<Release, "version"> {
38
+ /** The label its cuts gave it, or null. */
39
+ label: string | null;
40
+ /** Its cuts, oldest first, each with whether its signature verifies against the signers at base (#4031). */
41
+ cuts: Array<Omit<ReleaseCut, "version" | "releaseDigest"> & { signatureCheck: ReleaseSignatureCheck }>;
42
+ /** The environments that run it, and in each the components whose latest deploy names it. Sorted by env. */
43
+ environments: Array<{ env: string; components: string[] }>;
44
+ }
45
+
46
+ export type ReleasesDocument =
47
+ | {
48
+ $schema: string;
49
+ contract: number;
50
+ chant: string;
51
+ /** The member read, its path of names joined with `/`, or null for a project outside a workspace. */
52
+ member: string | null;
53
+ /** Newest cut first; a release with no cut comes last. */
54
+ releases: ReleaseEntry[];
55
+ reason: { code: (typeof RELEASES_REASON_CODES)[number]; message: string } | null;
56
+ }
57
+ | { $schema: string; contract: number; chant: string; error: { code: (typeof RELEASES_ERROR_CODES)[number]; message: string } };
58
+
59
+ /**
60
+ * The rule a cut's signature is judged by (#4031): the declaration's
61
+ * `identity.gates.release` and the signers and role grants, all read at base.
62
+ */
63
+ export async function releaseRule(cwd: string): Promise<GateAdmissionRule> {
64
+ const { scopeSource } = await import("./write-scope");
65
+ const { releaseRuleFrom } = await import("./identity");
66
+ return releaseRuleFrom(scopeSource(cwd)).rule;
67
+ }
68
+
69
+ /** Read the releases of the member at `cwd`. */
70
+ export async function releasesDocument(req: { cwd: string }): Promise<ReleasesDocument> {
71
+ const head = { $schema: RELEASES_SCHEMA_ID, contract: RELEASES_CONTRACT_VERSION, chant: readerVersion() };
72
+ try {
73
+ execFileSync("git", ["rev-parse", "--git-dir"], { cwd: req.cwd, stdio: "ignore" });
74
+ } catch {
75
+ return { ...head, error: { code: "not-a-git-repository", message: "releases are read from the chant/lifecycle branch, and this directory is not in a git repository" } };
76
+ }
77
+ try {
78
+ const ledger = await resolveMemberLedger(req.cwd);
79
+ const loc = { cwd: req.cwd };
80
+ const { releases, malformed: badReleases } = await readAllReleases(loc);
81
+ const { cuts, malformed: badCuts } = await readCuts(loc);
82
+
83
+ const running = new Map<string, Map<string, string[]>>();
84
+ for (const env of await listReleaseEnvironments(loc)) {
85
+ const { records } = await readReleaseLedger(env, loc);
86
+ for (const record of latestPerComponent(records).values()) {
87
+ const digest = (record as { releaseDigest?: unknown }).releaseDigest;
88
+ if (typeof digest !== "string") continue;
89
+ const envs = running.get(digest) ?? new Map<string, string[]>();
90
+ envs.set(env, [...(envs.get(env) ?? []), record.component].sort());
91
+ running.set(digest, envs);
92
+ }
93
+ }
94
+
95
+ const rule = await releaseRule(req.cwd);
96
+ const entries: ReleaseEntry[] = releases.map(({ version: _v, ...release }) => {
97
+ const own = cuts.filter((c) => c.releaseDigest === release.releaseDigest);
98
+ return {
99
+ ...release,
100
+ label: own.find((c) => c.label !== null)?.label ?? null,
101
+ cuts: own.map(({ version: _cv, releaseDigest: _d, ...cut }) => ({ ...cut, signatureCheck: checkReleaseSignature(rule, { releaseDigest: release.releaseDigest, ...cut }) })),
102
+ environments: [...(running.get(release.releaseDigest) ?? new Map<string, string[]>())]
103
+ .map(([env, components]) => ({ env, components }))
104
+ .sort((a, b) => (a.env < b.env ? -1 : 1)),
105
+ };
106
+ });
107
+ const latest = (e: ReleaseEntry): string => e.cuts.map((c) => c.timestamp).sort().at(-1) ?? "";
108
+ entries.sort((a, b) => (latest(b) !== latest(a) ? (latest(b) < latest(a) ? -1 : 1) : a.releaseDigest < b.releaseDigest ? -1 : 1));
109
+
110
+ const bad = badReleases + badCuts;
111
+ return {
112
+ ...head,
113
+ member: ledger.members.length > 0 ? ledger.members.join("/") : null,
114
+ releases: entries,
115
+ reason: bad > 0
116
+ ? { code: "ledger-malformed", message: `${badReleases} release file(s) and ${badCuts} cut line(s) could not be read and are left out` }
117
+ : null,
118
+ };
119
+ } catch (err) {
120
+ return { ...head, error: { code: "ledger-unreadable", message: `reading the releases failed: ${err instanceof Error ? err.message : String(err)}` } };
121
+ }
122
+ }