@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,213 @@
1
+ /**
2
+ * Where a run's events are kept on this host, and how to read them back
3
+ * (#4042). `chant run events <run>` replays a run from here and `--follow`
4
+ * tails it.
5
+ *
6
+ * Each run gets one file, `<git-common-dir>/chant/run-events/<run id>.jsonl`,
7
+ * holding the lines the one writer (`./run-event.ts`) produced, byte for byte
8
+ * as `--events-json` prints them. It sits beside the in-flight records
9
+ * (`./op/run-live.ts`), outside the working tree and off `chant/lifecycle`:
10
+ * the run ledger's growth is #4036's concern and these files add none to it.
11
+ * Unlike the in-flight record they are kept after the run settles. They are
12
+ * never pruned here; retention is #4036's.
13
+ *
14
+ * Every write is best-effort. A run whose events cannot be kept still runs.
15
+ */
16
+
17
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
18
+ import { join } from "node:path";
19
+ import { gitCommonDir } from "./op/run-live";
20
+ import type { RunEvent } from "./run-event";
21
+
22
+ /** The directory name under `<git-common-dir>/chant/` that holds the files. */
23
+ export const RUN_EVENTS_DIR = "run-events";
24
+
25
+ /** The file name a run id is kept under: anything that is not a plain file-name character becomes `_`. */
26
+ export function runEventFileName(runId: string): string {
27
+ return `${runId.replace(/[^A-Za-z0-9._@-]/g, "_")}.jsonl`;
28
+ }
29
+
30
+ /**
31
+ * The environment variable that names the directory run events are kept in,
32
+ * in place of the checkout's. The test suite sets it to a per-run temp
33
+ * directory, so no test writes to the real repository's store (worktrees
34
+ * share one git directory).
35
+ */
36
+ export const RUN_EVENTS_DIR_ENV = "CHANT_RUN_EVENTS_DIR";
37
+
38
+ /** The directory the checkout at `cwd` keeps run events in (`CHANT_RUN_EVENTS_DIR` when set), or null outside a checkout. */
39
+ export async function runEventsDir(cwd: string): Promise<string | null> {
40
+ const override = process.env[RUN_EVENTS_DIR_ENV];
41
+ if (override) return override;
42
+ const common = await gitCommonDir(cwd);
43
+ return common ? join(common, "chant", RUN_EVENTS_DIR) : null;
44
+ }
45
+
46
+ /** An open file a run's lines are appended to. */
47
+ export interface RunEventStore {
48
+ readonly path: string;
49
+ /** Append one line (it already ends with a newline). Never throws. */
50
+ write(line: string): void;
51
+ }
52
+
53
+ /** Open the file for `runId`, or undefined outside a checkout or where it cannot be created. */
54
+ export async function openRunEventStore(cwd: string, runId: string): Promise<RunEventStore | undefined> {
55
+ let dir: string | null;
56
+ try {
57
+ dir = await runEventsDir(cwd);
58
+ } catch {
59
+ return undefined;
60
+ }
61
+ if (!dir) return undefined;
62
+ const path = join(dir, runEventFileName(runId));
63
+ try {
64
+ mkdirSync(dir, { recursive: true });
65
+ // A run id used again (a re-run of the same CI run) replaces the earlier stream.
66
+ writeFileSync(path, "");
67
+ } catch {
68
+ return undefined;
69
+ }
70
+ return {
71
+ path,
72
+ write(line) {
73
+ try {
74
+ appendFileSync(path, line);
75
+ } catch {
76
+ // The directory is gone or unwritable: the line is dropped.
77
+ }
78
+ },
79
+ };
80
+ }
81
+
82
+ /** A write function that hands each line to every function given, or undefined when none is. */
83
+ export function teeLines(...writes: Array<((line: string) => void) | undefined>): ((line: string) => void) | undefined {
84
+ const live = writes.filter((w): w is (line: string) => void => w !== undefined);
85
+ if (live.length === 0) return undefined;
86
+ if (live.length === 1) return live[0];
87
+ return (line) => {
88
+ for (const w of live) w(line);
89
+ };
90
+ }
91
+
92
+ /** The events in `text`, one per whole line. Returns what is left of an unfinished last line. */
93
+ export function parseRunEventLines(text: string): { events: RunEvent[]; rest: string } {
94
+ const events: RunEvent[] = [];
95
+ const parts = text.split("\n");
96
+ const rest = parts.pop() ?? "";
97
+ for (const part of parts) {
98
+ if (part.trim() === "") continue;
99
+ try {
100
+ events.push(JSON.parse(part) as RunEvent);
101
+ } catch {
102
+ // A line that is not JSON is not an event: skipped.
103
+ }
104
+ }
105
+ return { events, rest };
106
+ }
107
+
108
+ /** A stored run, as far as its first line says. */
109
+ export interface StoredRun {
110
+ runId: string;
111
+ name: string;
112
+ file: string;
113
+ /** The file's last write, in ms. */
114
+ mtimeMs: number;
115
+ }
116
+
117
+ /** The runs stored for the checkout at `cwd`, newest first. */
118
+ export async function listStoredRuns(cwd: string): Promise<StoredRun[]> {
119
+ const dir = await runEventsDir(cwd);
120
+ if (!dir || !existsSync(dir)) return [];
121
+ const runs: StoredRun[] = [];
122
+ for (const entry of readdirSync(dir)) {
123
+ if (!entry.endsWith(".jsonl")) continue;
124
+ const file = join(dir, entry);
125
+ try {
126
+ const first = readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
127
+ if (first === "") continue;
128
+ const head = JSON.parse(first) as Partial<RunEvent>;
129
+ if (typeof head.runId !== "string") continue;
130
+ runs.push({ runId: head.runId, name: String(head.name ?? ""), file, mtimeMs: statSync(file).mtimeMs });
131
+ } catch {
132
+ // Unreadable or not an event file: not listed.
133
+ }
134
+ }
135
+ return runs.sort((a, b) => b.mtimeMs - a.mtimeMs);
136
+ }
137
+
138
+ /**
139
+ * The file for `ref`: a run id, else the name of an Op or a component
140
+ * selection, for which the newest stored run of that name stands. Null when
141
+ * nothing matches (a run that has not written its first line yet included).
142
+ */
143
+ export async function findStoredRun(cwd: string, ref: string): Promise<StoredRun | null> {
144
+ const dir = await runEventsDir(cwd);
145
+ if (!dir) return null;
146
+ const direct = join(dir, runEventFileName(ref));
147
+ if (existsSync(direct)) {
148
+ const byId = (await listStoredRuns(cwd)).find((r) => r.file === direct);
149
+ return byId ?? { runId: ref, name: "", file: direct, mtimeMs: statSync(direct).mtimeMs };
150
+ }
151
+ return (await listStoredRuns(cwd)).find((r) => r.name === ref) ?? null;
152
+ }
153
+
154
+ /** Whether `event` ends a stream. */
155
+ export const isRunSettle = (event: RunEvent): boolean => event.type === "run-settle";
156
+
157
+ /**
158
+ * Read `file` from the start and hand each event to `onEvent` (with its raw
159
+ * line). Without `follow` it stops at the end of the file. With it, it keeps
160
+ * reading what is appended, by polling every `pollMs`, until `run-settle` is
161
+ * read, `signal` aborts, or `idleMs` passes with nothing new (when given).
162
+ * Resolves with the `run-settle` event, or undefined when the stream had
163
+ * none (a replay of a run still going, or a follow that was cut short).
164
+ */
165
+ export async function readRunEvents(
166
+ file: string,
167
+ onEvent: (event: RunEvent, line: string) => void,
168
+ opts: { follow?: boolean; pollMs?: number; idleMs?: number; signal?: AbortSignal } = {},
169
+ ): Promise<RunEvent | undefined> {
170
+ const pollMs = opts.pollMs ?? 100;
171
+ let offset = 0;
172
+ let rest = "";
173
+ let lastGrowth = Date.now();
174
+ for (;;) {
175
+ let size = 0;
176
+ try {
177
+ size = statSync(file).size;
178
+ } catch {
179
+ // Not there yet (a follow started before the run opened its file).
180
+ }
181
+ if (size > offset) {
182
+ const buf = readFileSync(file);
183
+ const chunk = buf.subarray(offset, size).toString("utf8");
184
+ offset = size;
185
+ lastGrowth = Date.now();
186
+ const text = rest + chunk;
187
+ const parts = text.split("\n");
188
+ rest = parts.pop() ?? "";
189
+ for (const part of parts) {
190
+ if (part.trim() === "") continue;
191
+ let event: RunEvent;
192
+ try {
193
+ event = JSON.parse(part) as RunEvent;
194
+ } catch {
195
+ continue;
196
+ }
197
+ onEvent(event, `${part}\n`);
198
+ if (isRunSettle(event)) return event;
199
+ }
200
+ } else if (!opts.follow) {
201
+ return undefined;
202
+ } else if (opts.signal?.aborted) {
203
+ return undefined;
204
+ } else if (opts.idleMs !== undefined && Date.now() - lastGrowth > opts.idleMs) {
205
+ return undefined;
206
+ } else {
207
+ await new Promise<void>((resolve) => {
208
+ const timer = setTimeout(resolve, pollMs);
209
+ opts.signal?.addEventListener("abort", () => { clearTimeout(timer); resolve(); }, { once: true });
210
+ });
211
+ }
212
+ }
213
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * #4041: the emitters' side of the run-event contract. The stream holds a
3
+ * run to the shape the schema promises, the older `--progress-json` lines
4
+ * come back unchanged from the bodies the driver now emits, and a run is
5
+ * named by the commit and CI run #4020 resolves.
6
+ */
7
+
8
+ import { describe, expect, test } from "vitest";
9
+ import type { RunProgressEvent } from "./components/run-progress";
10
+ import { memoryGateLedgerPort, type GateCheck } from "./op/gate";
11
+ import type { RunEventBody, RunEventRun } from "./run-event";
12
+ import {
13
+ emitGateStep,
14
+ gateEventsOf,
15
+ joinRunEventSinks,
16
+ openRunEventStream,
17
+ progressOfRunEvent,
18
+ progressSinkOf,
19
+ resolveRunEventRun,
20
+ runEventRunOf,
21
+ } from "./run-event-stream";
22
+ import { checkRunEventLines } from "./workspace/__fixtures__/run-event-check";
23
+
24
+ const SHA = "0123456789abcdef0123456789abcdef01234567";
25
+ const RUN: RunEventRun = { runId: "local-1", kind: "component", name: "api", gitSha: SHA, runtime: "local", runOrigin: { forge: "local" } };
26
+
27
+ function capture(run: RunEventRun = RUN) {
28
+ const lines: string[] = [];
29
+ let t = Date.parse("2026-10-16T12:00:00.000Z");
30
+ const stream = openRunEventStream(run, (l) => lines.push(l), { now: () => new Date((t += 1000)), chant: "0.124.28" });
31
+ return { stream, lines };
32
+ }
33
+
34
+ describe("openRunEventStream", () => {
35
+ test("opens with run-start, drops a second one, times run-settle, and writes nothing after it", () => {
36
+ const { stream, lines } = capture();
37
+ stream.emit({ type: "run-start", waves: [["api"]] });
38
+ stream.emit({ type: "run-start", waves: [["other"]] });
39
+ stream.emit({ type: "phase-start", component: "api", phase: "Apply" });
40
+ stream.emit({ type: "phase-settle", component: "api", phase: "Apply", status: "ok" });
41
+ stream.emit({ type: "run-settle", status: "ok", durationMs: null });
42
+ stream.emit({ type: "phase-start", component: "api", phase: "late" });
43
+ stream.settle("failed", "too late");
44
+ const events = checkRunEventLines(lines);
45
+ expect(events.map((e) => e.type)).toEqual(["run-start", "phase-start", "phase-settle", "run-settle"]);
46
+ expect(events[0]).toMatchObject({ waves: [["api"]] });
47
+ // Untimed by the emitter, so the stream timed it from run-start.
48
+ expect(events.at(-1)).toMatchObject({ status: "ok", durationMs: expect.any(Number) });
49
+ expect((events.at(-1) as { durationMs: number }).durationMs).toBeGreaterThan(0);
50
+ expect(stream.settled).toBe(true);
51
+ });
52
+
53
+ test("a stream whose emitter never said run-start still opens with one, and settle ends it with the error", () => {
54
+ const { stream, lines } = capture();
55
+ stream.emit({ type: "step-start", component: null, phase: "Build", step: "make", index: 0 });
56
+ stream.emit({ type: "step-settle", component: null, phase: "Build", step: "make", index: 0, status: "failed", durationMs: 5, error: "boom" });
57
+ stream.settle("failed", "boom");
58
+ const events = checkRunEventLines(lines);
59
+ expect(events[0]).toMatchObject({ type: "run-start", waves: null });
60
+ expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "failed", error: "boom" });
61
+ });
62
+
63
+ test("nested drops the inner run's framing, so the outer run settles it", () => {
64
+ const { stream, lines } = capture();
65
+ stream.emit({ type: "run-start", waves: [["a"]] });
66
+ stream.nested({ type: "run-start", waves: [["a"]] });
67
+ stream.nested({ type: "wave-start", wave: 1, components: ["a"] });
68
+ stream.nested({ type: "wave-settle", wave: 1, status: "ok" });
69
+ stream.nested({ type: "run-settle", status: "ok", durationMs: null });
70
+ expect(stream.settled).toBe(false);
71
+ stream.settle("gated");
72
+ expect(checkRunEventLines(lines).map((e) => e.type)).toEqual(["run-start", "wave-start", "wave-settle", "run-settle"]);
73
+ });
74
+ });
75
+
76
+ describe("progressOfRunEvent: --progress-json keeps its shape", () => {
77
+ test("each body the driver emits maps back to the RunProgressEvent it replaced, key order and all", () => {
78
+ const pairs: Array<[RunEventBody, RunProgressEvent]> = [
79
+ [{ type: "run-start", waves: [["a"]] }, { type: "run-start", waves: [["a"]] }],
80
+ [{ type: "wave-start", wave: 1, components: ["a"] }, { type: "wave-start", wave: 1, components: ["a"] }],
81
+ [{ type: "component-start", wave: 1, component: "a" }, { type: "component-start", wave: 1, component: "a" }],
82
+ [{ type: "phase-start", component: "a", phase: "Apply" }, { type: "phase-start", component: "a", phase: "Apply" }],
83
+ [{ type: "step-start", component: "a", phase: "Apply", step: "apply", index: 0 }, { type: "step", component: "a", phase: "Apply", step: "apply", status: "running" }],
84
+ [
85
+ { type: "step-settle", component: "a", phase: "Apply", step: "apply", index: 0, status: "failed", durationMs: 4, error: "no" },
86
+ { type: "step", component: "a", phase: "Apply", step: "apply", status: "failed", error: "no" },
87
+ ],
88
+ [{ type: "phase-settle", component: "a", phase: "Apply", status: "gated" }, { type: "phase-done", component: "a", phase: "Apply", status: "gated" }],
89
+ [{ type: "component-settle", wave: 1, component: "a", status: "failed" }, { type: "component-done", wave: 1, component: "a", status: "failed" }],
90
+ [{ type: "wave-settle", wave: 1, status: "ok" }, { type: "wave-done", wave: 1, status: "ok" }],
91
+ [{ type: "run-settle", status: "ok", durationMs: 12 }, { type: "run-done", status: "ok" }],
92
+ ];
93
+ for (const [body, old] of pairs) expect(JSON.stringify(progressOfRunEvent(body))).toBe(JSON.stringify(old));
94
+ });
95
+
96
+ test("what the older stream never printed maps to nothing: gate steps, skipped steps, gate decisions, phases outside a component", () => {
97
+ const none: RunEventBody[] = [
98
+ { type: "step-start", component: "a", phase: "Apply", step: "gate:prod", index: 0 },
99
+ { type: "step-settle", component: "a", phase: "Apply", step: "gate:prod", index: 0, status: "gated", durationMs: 1 },
100
+ { type: "step-settle", component: "a", phase: "Apply", step: "apply", index: 1, status: "skipped", skip: "not-run", durationMs: null },
101
+ { type: "gate-waiting", component: "a", phase: "Apply", step: "gate:prod", op: "a", gate: "prod", point: null },
102
+ { type: "phase-start", component: null, phase: "gate" },
103
+ { type: "phase-settle", component: null, phase: "gate", status: "ok" },
104
+ ];
105
+ for (const body of none) expect(progressOfRunEvent(body)).toBeUndefined();
106
+ });
107
+
108
+ test("progressSinkOf and joinRunEventSinks hand each body to every sink, and to none when none is given", () => {
109
+ const old: RunProgressEvent[] = [];
110
+ const bodies: RunEventBody[] = [];
111
+ const sink = joinRunEventSinks((b) => bodies.push(b), progressSinkOf((e) => old.push(e)), undefined)!;
112
+ sink({ type: "run-start", waves: [] });
113
+ sink({ type: "gate-waiting", component: null, phase: "gate", step: "gate:g", op: "fan-out", gate: "g", point: null });
114
+ expect(bodies).toHaveLength(2);
115
+ expect(old).toEqual([{ type: "run-start", waves: [] }]);
116
+ expect(joinRunEventSinks(undefined, progressSinkOf(undefined))).toBeUndefined();
117
+ });
118
+ });
119
+
120
+ describe("a gate's decision as events", () => {
121
+ const at = { component: null, phase: "gate", step: "gate:pr-apply", op: "pr-12", gate: "pr-apply" };
122
+
123
+ test("approved: gate-resolved and an ok settle; not approved: gate-waiting and a gated settle", () => {
124
+ const approved: GateCheck = {
125
+ satisfied: true,
126
+ resolution: { version: 1, kind: "resolution", op: "pr-12", gate: "pr-apply", resolvedBy: "github:alice", timestamp: "2026-10-16T11:00:00.000Z" },
127
+ } as GateCheck;
128
+ expect(gateEventsOf(approved, at, { durationMs: 3 })).toEqual([
129
+ { type: "gate-resolved", component: null, phase: "gate", step: "gate:pr-apply", op: "pr-12", gate: "pr-apply", resolvedBy: "github:alice", resolvedAt: "2026-10-16T11:00:00.000Z", via: null },
130
+ { type: "step-settle", component: null, phase: "gate", step: "gate:pr-apply", status: "ok", durationMs: 3 },
131
+ ]);
132
+ });
133
+
134
+ test("emitGateStep frames the decision as a gate phase, schema-valid, start before settle", async () => {
135
+ const { stream, lines } = capture();
136
+ const port = memoryGateLedgerPort();
137
+ const check = await emitGateStep(stream.emit, { op: "fan-out", gate: "ship" }, () =>
138
+ import("./op/gate").then((g) => g.evaluateGate(port, { op: "fan-out", gate: "ship", now: "2026-10-16T12:00:00.000Z" })),
139
+ );
140
+ expect(check.satisfied).toBe(false);
141
+ stream.settle("gated");
142
+ const events = checkRunEventLines(lines);
143
+ expect(events.map((e) => e.type)).toEqual(["run-start", "phase-start", "step-start", "gate-waiting", "step-settle", "phase-settle", "run-settle"]);
144
+ expect(events[4]).toMatchObject({ status: "gated", step: "gate:ship" });
145
+ });
146
+ });
147
+
148
+ describe("a run's identity (#4020's sources)", () => {
149
+ test("outside CI: a local id, the local origin and the local runtime", async () => {
150
+ const run = await resolveRunEventRun({ kind: "component", name: "api", cwd: "/nonexistent", env: {} });
151
+ expect(run).toMatchObject({ kind: "component", name: "api", gitSha: null, runtime: "local", runOrigin: { forge: "local" } });
152
+ expect(run.runId).toMatch(/^local-\d+$/);
153
+ });
154
+
155
+ test("in a GitHub job: the CI run's id and origin, the commit the job is for, and the ci runtime", async () => {
156
+ const env = { GITHUB_RUN_ID: "77", GITHUB_REPOSITORY: "acme/app", GITHUB_SERVER_URL: "https://github.com", GITHUB_SHA: SHA };
157
+ const run = await resolveRunEventRun({ kind: "component", name: "api", cwd: "/nonexistent", env });
158
+ expect(run).toEqual({
159
+ runId: "77",
160
+ kind: "component",
161
+ name: "api",
162
+ gitSha: SHA,
163
+ runtime: "ci",
164
+ runOrigin: { forge: "github", repo: "acme/app", url: "https://github.com/acme/app/actions/runs/77" },
165
+ });
166
+ // An id of the caller's own, not the CI run's, lives in no CI id space.
167
+ expect((await resolveRunEventRun({ kind: "component", name: "api", runId: "run-9", cwd: "/nonexistent", env })).runOrigin).toEqual({ forge: "local" });
168
+ });
169
+
170
+ test("a steward's run is the op id space on the steward runtime", () => {
171
+ expect(runEventRunOf({ kind: "op", name: "deploy", runId: "conv-1", steward: true })).toEqual({
172
+ runId: "conv-1", kind: "op", name: "deploy", gitSha: null, runtime: "steward", runOrigin: { forge: "op" },
173
+ });
174
+ });
175
+ });