@intentius/chant 0.124.27 → 0.124.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (681) hide show
  1. package/README.md +1 -1
  2. package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
  3. package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
  4. package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  6. package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
  7. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  8. package/dist/__golden__/wave-loop/op-held-on.json +2 -0
  9. package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
  10. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  11. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  12. package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
  13. package/dist/__golden__/wave-loop/op-per-run.json +7 -0
  14. package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
  15. package/dist/__golden__/wave-loop/op-sealed.json +3 -0
  16. package/dist/__golden__/wave-loop/op-spend.json +3 -0
  17. package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
  18. package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
  19. package/dist/build-params.d.ts +34 -1
  20. package/dist/build-params.d.ts.map +1 -1
  21. package/dist/build-params.js +31 -1
  22. package/dist/change-set-graph.d.ts +63 -0
  23. package/dist/change-set-graph.d.ts.map +1 -0
  24. package/dist/change-set-graph.js +130 -0
  25. package/dist/change-set.d.ts +62 -3
  26. package/dist/change-set.d.ts.map +1 -1
  27. package/dist/change-set.js +36 -4
  28. package/dist/chant-version.d.ts +9 -0
  29. package/dist/chant-version.d.ts.map +1 -0
  30. package/dist/chant-version.js +16 -0
  31. package/dist/cli/build-params-cli.d.ts +7 -1
  32. package/dist/cli/build-params-cli.d.ts.map +1 -1
  33. package/dist/cli/build-params-cli.js +17 -8
  34. package/dist/cli/commands/build.d.ts +8 -0
  35. package/dist/cli/commands/build.d.ts.map +1 -1
  36. package/dist/cli/commands/build.js +15 -11
  37. package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
  38. package/dist/cli/commands/check-lexicon-docs.js +30 -2
  39. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  40. package/dist/cli/commands/check-lexicon.js +13 -2
  41. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
  42. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
  43. package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
  44. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  45. package/dist/cli/commands/init-lexicon.js +17 -8
  46. package/dist/cli/handlers/approve.d.ts.map +1 -1
  47. package/dist/cli/handlers/approve.js +64 -4
  48. package/dist/cli/handlers/build.d.ts.map +1 -1
  49. package/dist/cli/handlers/build.js +43 -4
  50. package/dist/cli/handlers/change-set.d.ts +10 -0
  51. package/dist/cli/handlers/change-set.d.ts.map +1 -1
  52. package/dist/cli/handlers/change-set.js +51 -1
  53. package/dist/cli/handlers/fan-out.d.ts +10 -1
  54. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  55. package/dist/cli/handlers/fan-out.js +54 -6
  56. package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
  57. package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
  58. package/dist/cli/handlers/lifecycle-gc.js +143 -0
  59. package/dist/cli/handlers/pr.d.ts +16 -2
  60. package/dist/cli/handlers/pr.d.ts.map +1 -1
  61. package/dist/cli/handlers/pr.js +269 -37
  62. package/dist/cli/handlers/promote.d.ts +7 -0
  63. package/dist/cli/handlers/promote.d.ts.map +1 -1
  64. package/dist/cli/handlers/promote.js +440 -28
  65. package/dist/cli/handlers/release.d.ts +91 -0
  66. package/dist/cli/handlers/release.d.ts.map +1 -0
  67. package/dist/cli/handlers/release.js +589 -0
  68. package/dist/cli/handlers/run.d.ts +38 -2
  69. package/dist/cli/handlers/run.d.ts.map +1 -1
  70. package/dist/cli/handlers/run.js +234 -16
  71. package/dist/cli/main.d.ts.map +1 -1
  72. package/dist/cli/main.js +143 -17
  73. package/dist/cli/mcp/workspace-tools.js +1 -1
  74. package/dist/cli/parse-args.d.ts.map +1 -1
  75. package/dist/cli/parse-args.js +73 -5
  76. package/dist/cli/registry.d.ts +43 -5
  77. package/dist/cli/registry.d.ts.map +1 -1
  78. package/dist/cli/run-events.d.ts +30 -0
  79. package/dist/cli/run-events.d.ts.map +1 -0
  80. package/dist/cli/run-events.js +49 -0
  81. package/dist/cli/version.d.ts +3 -5
  82. package/dist/cli/version.d.ts.map +1 -1
  83. package/dist/cli/version.js +3 -13
  84. package/dist/codegen/docs-in-site.d.ts +71 -0
  85. package/dist/codegen/docs-in-site.d.ts.map +1 -0
  86. package/dist/codegen/docs-in-site.js +177 -0
  87. package/dist/codegen/docs-types.d.ts +14 -1
  88. package/dist/codegen/docs-types.d.ts.map +1 -1
  89. package/dist/codegen/docs.d.ts +18 -1
  90. package/dist/codegen/docs.d.ts.map +1 -1
  91. package/dist/codegen/docs.js +40 -7
  92. package/dist/codegen/rehype-base-url.mjs +15 -9
  93. package/dist/components/auto-release.d.ts +6 -1
  94. package/dist/components/auto-release.d.ts.map +1 -1
  95. package/dist/components/auto-release.js +3 -0
  96. package/dist/components/capability.d.ts +9 -0
  97. package/dist/components/capability.d.ts.map +1 -1
  98. package/dist/components/cli-support.d.ts +6 -0
  99. package/dist/components/cli-support.d.ts.map +1 -1
  100. package/dist/components/cli-support.js +10 -9
  101. package/dist/components/driver.d.ts +50 -9
  102. package/dist/components/driver.d.ts.map +1 -1
  103. package/dist/components/driver.js +116 -54
  104. package/dist/components/fan-out-inputs.d.ts +69 -0
  105. package/dist/components/fan-out-inputs.d.ts.map +1 -0
  106. package/dist/components/fan-out-inputs.js +169 -0
  107. package/dist/components/fan-out-record.d.ts +13 -0
  108. package/dist/components/fan-out-record.d.ts.map +1 -1
  109. package/dist/components/fan-out-record.js +16 -0
  110. package/dist/components/fan-out-run.d.ts +7 -0
  111. package/dist/components/fan-out-run.d.ts.map +1 -1
  112. package/dist/components/fan-out-run.js +85 -26
  113. package/dist/components/fan-out.d.ts +4 -0
  114. package/dist/components/fan-out.d.ts.map +1 -1
  115. package/dist/components/pr-pipeline.d.ts +25 -0
  116. package/dist/components/pr-pipeline.d.ts.map +1 -1
  117. package/dist/components/pr-pipeline.js +22 -4
  118. package/dist/components/pr-run.d.ts +11 -0
  119. package/dist/components/pr-run.d.ts.map +1 -1
  120. package/dist/components/pr-run.js +20 -1
  121. package/dist/components/promote-order.d.ts +38 -0
  122. package/dist/components/promote-order.d.ts.map +1 -0
  123. package/dist/components/promote-order.js +62 -0
  124. package/dist/components/promote.d.ts +38 -2
  125. package/dist/components/promote.d.ts.map +1 -1
  126. package/dist/components/promote.js +77 -2
  127. package/dist/components/release-compile.d.ts +142 -0
  128. package/dist/components/release-compile.d.ts.map +1 -0
  129. package/dist/components/release-compile.js +382 -0
  130. package/dist/components/release-gate.d.ts +135 -0
  131. package/dist/components/release-gate.d.ts.map +1 -0
  132. package/dist/components/release-gate.js +288 -0
  133. package/dist/components/release-promote.d.ts +96 -0
  134. package/dist/components/release-promote.d.ts.map +1 -0
  135. package/dist/components/release-promote.js +204 -0
  136. package/dist/components/run-progress.d.ts +7 -0
  137. package/dist/components/run-progress.d.ts.map +1 -1
  138. package/dist/components/wave-plan.d.ts +8 -1
  139. package/dist/components/wave-plan.d.ts.map +1 -1
  140. package/dist/components/wave-plan.js +11 -3
  141. package/dist/config.d.ts +45 -0
  142. package/dist/config.d.ts.map +1 -1
  143. package/dist/config.js +3 -0
  144. package/dist/flag-params.d.ts +54 -0
  145. package/dist/flag-params.d.ts.map +1 -0
  146. package/dist/flag-params.js +130 -0
  147. package/dist/index.d.ts +1 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -0
  150. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  151. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  152. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  153. package/dist/lifecycle/change-set-store.d.ts +68 -0
  154. package/dist/lifecycle/change-set-store.d.ts.map +1 -0
  155. package/dist/lifecycle/change-set-store.js +97 -0
  156. package/dist/lifecycle/commit-sha.d.ts +13 -0
  157. package/dist/lifecycle/commit-sha.d.ts.map +1 -0
  158. package/dist/lifecycle/commit-sha.js +21 -0
  159. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  160. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  161. package/dist/lifecycle/delivery-metrics.js +145 -0
  162. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  163. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  164. package/dist/lifecycle/deploy-outcome.js +101 -0
  165. package/dist/lifecycle/gate-change-set.d.ts +78 -0
  166. package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
  167. package/dist/lifecycle/gate-change-set.js +138 -0
  168. package/dist/lifecycle/gate-ledger.d.ts +51 -4
  169. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  170. package/dist/lifecycle/gate-ledger.js +18 -0
  171. package/dist/lifecycle/git.d.ts +30 -1
  172. package/dist/lifecycle/git.d.ts.map +1 -1
  173. package/dist/lifecycle/git.js +31 -5
  174. package/dist/lifecycle/index.d.ts +4 -0
  175. package/dist/lifecycle/index.d.ts.map +1 -1
  176. package/dist/lifecycle/index.js +4 -0
  177. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  178. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  179. package/dist/lifecycle/mixed-environment.js +154 -0
  180. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  181. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  182. package/dist/lifecycle/publish-attachments.js +179 -0
  183. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  184. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  185. package/dist/lifecycle/publish-credentials.js +69 -0
  186. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  187. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  188. package/dist/lifecycle/publish-declaration.js +83 -0
  189. package/dist/lifecycle/publish-forge.d.ts +53 -0
  190. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  191. package/dist/lifecycle/publish-forge.js +298 -0
  192. package/dist/lifecycle/publish-npm.d.ts +61 -0
  193. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  194. package/dist/lifecycle/publish-npm.js +242 -0
  195. package/dist/lifecycle/publish-oci.d.ts +64 -0
  196. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  197. package/dist/lifecycle/publish-oci.js +383 -0
  198. package/dist/lifecycle/publish-record.d.ts +66 -0
  199. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  200. package/dist/lifecycle/publish-record.js +84 -0
  201. package/dist/lifecycle/publish-targets.d.ts +45 -0
  202. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  203. package/dist/lifecycle/publish-targets.js +85 -0
  204. package/dist/lifecycle/release-cut.d.ts +154 -0
  205. package/dist/lifecycle/release-cut.d.ts.map +1 -0
  206. package/dist/lifecycle/release-cut.js +366 -0
  207. package/dist/lifecycle/release-join.d.ts +95 -0
  208. package/dist/lifecycle/release-join.d.ts.map +1 -0
  209. package/dist/lifecycle/release-join.js +139 -0
  210. package/dist/lifecycle/release-ledger.d.ts +54 -0
  211. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  212. package/dist/lifecycle/release-ledger.js +53 -20
  213. package/dist/lifecycle/release-publish.d.ts +312 -0
  214. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  215. package/dist/lifecycle/release-publish.js +335 -0
  216. package/dist/lifecycle/release-revocation.d.ts +80 -0
  217. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  218. package/dist/lifecycle/release-revocation.js +123 -0
  219. package/dist/lifecycle/release-store.d.ts +200 -0
  220. package/dist/lifecycle/release-store.d.ts.map +1 -0
  221. package/dist/lifecycle/release-store.js +320 -0
  222. package/dist/lifecycle/release-verify.d.ts +61 -0
  223. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  224. package/dist/lifecycle/release-verify.js +92 -0
  225. package/dist/lifecycle/release-workstation.d.ts +91 -0
  226. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  227. package/dist/lifecycle/release-workstation.js +180 -0
  228. package/dist/lifecycle/retention-policy.d.ts +45 -0
  229. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  230. package/dist/lifecycle/retention-policy.js +43 -0
  231. package/dist/lifecycle/retention.d.ts +185 -0
  232. package/dist/lifecycle/retention.d.ts.map +1 -0
  233. package/dist/lifecycle/retention.js +400 -0
  234. package/dist/lifecycle/run-ledger.d.ts +4 -0
  235. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  236. package/dist/lifecycle/run-ledger.js +2 -0
  237. package/dist/op/activities/change-coverage.d.ts.map +1 -1
  238. package/dist/op/activities/change-coverage.js +4 -2
  239. package/dist/op/activities/source-release.d.ts.map +1 -1
  240. package/dist/op/activities/source-release.js +7 -3
  241. package/dist/op/activities/source-rollback.d.ts.map +1 -1
  242. package/dist/op/activities/source-rollback.js +7 -3
  243. package/dist/op/gate-name.d.ts +11 -2
  244. package/dist/op/gate-name.d.ts.map +1 -1
  245. package/dist/op/gate-name.js +12 -3
  246. package/dist/op/gate-run-locator.d.ts +22 -0
  247. package/dist/op/gate-run-locator.d.ts.map +1 -1
  248. package/dist/op/gate-run-locator.js +31 -0
  249. package/dist/op/gate.d.ts +27 -3
  250. package/dist/op/gate.d.ts.map +1 -1
  251. package/dist/op/gate.js +57 -3
  252. package/dist/op/local-executor.d.ts +36 -1
  253. package/dist/op/local-executor.d.ts.map +1 -1
  254. package/dist/op/local-executor.js +158 -52
  255. package/dist/op/op-waves-run.d.ts.map +1 -1
  256. package/dist/op/op-waves-run.js +4 -0
  257. package/dist/op/run-live.d.ts +2 -0
  258. package/dist/op/run-live.d.ts.map +1 -1
  259. package/dist/op/run-live.js +1 -1
  260. package/dist/op/runtime.d.ts +46 -0
  261. package/dist/op/runtime.d.ts.map +1 -1
  262. package/dist/op/runtimes/local.d.ts.map +1 -1
  263. package/dist/op/runtimes/local.js +8 -0
  264. package/dist/plan-summary.d.ts.map +1 -1
  265. package/dist/plan-summary.js +8 -2
  266. package/dist/point-decision.d.ts +2 -0
  267. package/dist/point-decision.d.ts.map +1 -1
  268. package/dist/point-decision.js +9 -2
  269. package/dist/pr-loop.d.ts +7 -0
  270. package/dist/pr-loop.d.ts.map +1 -1
  271. package/dist/pr-loop.js +23 -4
  272. package/dist/pr-report-url.d.ts +82 -0
  273. package/dist/pr-report-url.d.ts.map +1 -0
  274. package/dist/pr-report-url.js +174 -0
  275. package/dist/provenance.d.ts +2 -2
  276. package/dist/provenance.d.ts.map +1 -1
  277. package/dist/run-event-store.d.ts +104 -0
  278. package/dist/run-event-store.d.ts.map +1 -0
  279. package/dist/run-event-store.js +251 -0
  280. package/dist/run-event-stream.d.ts +133 -0
  281. package/dist/run-event-stream.d.ts.map +1 -0
  282. package/dist/run-event-stream.js +242 -0
  283. package/dist/run-event.d.ts +285 -0
  284. package/dist/run-event.d.ts.map +1 -0
  285. package/dist/run-event.js +209 -0
  286. package/dist/wave-runtime.mjs +262 -226
  287. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  288. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  289. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  290. package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
  291. package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
  292. package/dist/workspace/__fixtures__/run-event-check.js +59 -0
  293. package/dist/workspace/change-set-show.d.ts +56 -0
  294. package/dist/workspace/change-set-show.d.ts.map +1 -0
  295. package/dist/workspace/change-set-show.js +54 -0
  296. package/dist/workspace/change-set-show.schema.json +102 -0
  297. package/dist/workspace/change-set.schema.json +32 -1
  298. package/dist/workspace/changes-cli.d.ts.map +1 -1
  299. package/dist/workspace/changes-cli.js +10 -1
  300. package/dist/workspace/changes.d.ts +37 -0
  301. package/dist/workspace/changes.d.ts.map +1 -1
  302. package/dist/workspace/changes.js +49 -2
  303. package/dist/workspace/changes.schema.json +135 -0
  304. package/dist/workspace/check.schema.json +4 -2
  305. package/dist/workspace/checks/records.d.ts +9 -1
  306. package/dist/workspace/checks/records.d.ts.map +1 -1
  307. package/dist/workspace/checks/records.js +35 -1
  308. package/dist/workspace/checks.d.ts +1 -0
  309. package/dist/workspace/checks.d.ts.map +1 -1
  310. package/dist/workspace/checks.js +2 -1
  311. package/dist/workspace/ci-green-cli.d.ts +19 -4
  312. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  313. package/dist/workspace/ci-green-cli.js +75 -12
  314. package/dist/workspace/ci-green-forge.d.ts +20 -3
  315. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  316. package/dist/workspace/ci-green-forge.js +6 -4
  317. package/dist/workspace/ci-green-workflow.d.ts +11 -0
  318. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  319. package/dist/workspace/ci-green-workflow.js +6 -0
  320. package/dist/workspace/ci-green.d.ts +281 -13
  321. package/dist/workspace/ci-green.d.ts.map +1 -1
  322. package/dist/workspace/ci-green.js +456 -38
  323. package/dist/workspace/ci-last-green.schema.json +32 -2
  324. package/dist/workspace/ci-list.schema.json +525 -0
  325. package/dist/workspace/commit-links.d.ts +85 -0
  326. package/dist/workspace/commit-links.d.ts.map +1 -0
  327. package/dist/workspace/commit-links.js +163 -0
  328. package/dist/workspace/declaration.d.ts +35 -0
  329. package/dist/workspace/declaration.d.ts.map +1 -1
  330. package/dist/workspace/declaration.js +27 -1
  331. package/dist/workspace/declaration.schema.json +76 -0
  332. package/dist/workspace/declared-kinds.d.ts +16 -0
  333. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  334. package/dist/workspace/declared-kinds.js +25 -0
  335. package/dist/workspace/delivery-metrics.d.ts +91 -0
  336. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  337. package/dist/workspace/delivery-metrics.js +217 -0
  338. package/dist/workspace/delivery-metrics.schema.json +519 -0
  339. package/dist/workspace/flags-read.d.ts +37 -0
  340. package/dist/workspace/flags-read.d.ts.map +1 -0
  341. package/dist/workspace/flags-read.js +103 -0
  342. package/dist/workspace/flags.d.ts +100 -0
  343. package/dist/workspace/flags.d.ts.map +1 -0
  344. package/dist/workspace/flags.js +98 -0
  345. package/dist/workspace/graph-cli.js +1 -1
  346. package/dist/workspace/identity-gate-rule.d.ts +37 -0
  347. package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
  348. package/dist/workspace/identity-gate-rule.js +72 -7
  349. package/dist/workspace/identity.d.ts +12 -0
  350. package/dist/workspace/identity.d.ts.map +1 -1
  351. package/dist/workspace/identity.js +13 -1
  352. package/dist/workspace/intent-cli.d.ts +11 -2
  353. package/dist/workspace/intent-cli.d.ts.map +1 -1
  354. package/dist/workspace/intent-cli.js +54 -5
  355. package/dist/workspace/intent-trace.d.ts +219 -0
  356. package/dist/workspace/intent-trace.d.ts.map +1 -0
  357. package/dist/workspace/intent-trace.js +314 -0
  358. package/dist/workspace/intent.d.ts +36 -7
  359. package/dist/workspace/intent.d.ts.map +1 -1
  360. package/dist/workspace/intent.js +192 -22
  361. package/dist/workspace/intent.schema.json +702 -6
  362. package/dist/workspace/ls.d.ts +4 -0
  363. package/dist/workspace/ls.d.ts.map +1 -1
  364. package/dist/workspace/ls.js +2 -0
  365. package/dist/workspace/ls.schema.json +3 -1
  366. package/dist/workspace/member-pipeline.d.ts +6 -0
  367. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  368. package/dist/workspace/member-pipeline.js +6 -0
  369. package/dist/workspace/overlap.d.ts +146 -0
  370. package/dist/workspace/overlap.d.ts.map +1 -0
  371. package/dist/workspace/overlap.js +276 -0
  372. package/dist/workspace/promotion-order.d.ts +41 -0
  373. package/dist/workspace/promotion-order.d.ts.map +1 -0
  374. package/dist/workspace/promotion-order.js +95 -0
  375. package/dist/workspace/reason-codes.d.ts +4 -0
  376. package/dist/workspace/reason-codes.d.ts.map +1 -1
  377. package/dist/workspace/reason-codes.js +7 -1
  378. package/dist/workspace/records.d.ts +15 -2
  379. package/dist/workspace/records.d.ts.map +1 -1
  380. package/dist/workspace/records.js +32 -0
  381. package/dist/workspace/records.schema.json +21 -2
  382. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  383. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  384. package/dist/workspace/releases.d.ts +81 -0
  385. package/dist/workspace/releases.d.ts.map +1 -0
  386. package/dist/workspace/releases.js +103 -0
  387. package/dist/workspace/releases.schema.json +513 -0
  388. package/dist/workspace/run-event.schema.json +295 -0
  389. package/dist/workspace/status-gates.d.ts +34 -1
  390. package/dist/workspace/status-gates.d.ts.map +1 -1
  391. package/dist/workspace/status-gates.js +31 -2
  392. package/dist/workspace/status-stewards.d.ts +4 -0
  393. package/dist/workspace/status-stewards.d.ts.map +1 -1
  394. package/dist/workspace/status-stewards.js +2 -0
  395. package/dist/workspace/status.d.ts +117 -0
  396. package/dist/workspace/status.d.ts.map +1 -1
  397. package/dist/workspace/status.js +218 -4
  398. package/dist/workspace/status.schema.json +268 -2
  399. package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
  400. package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
  401. package/dist/workspace/trust/ci-tag-statement.js +123 -0
  402. package/dist/workspace/trust/op-run-statement.d.ts +91 -0
  403. package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
  404. package/dist/workspace/trust/op-run-statement.js +143 -0
  405. package/dist/workspace/trust/rotation.d.ts +18 -2
  406. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  407. package/dist/workspace/trust/rotation.js +65 -16
  408. package/dist/workspace/trust/seal.d.ts +11 -0
  409. package/dist/workspace/trust/seal.d.ts.map +1 -1
  410. package/dist/workspace/trust/seal.js +9 -0
  411. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  412. package/dist/workspace/trust/ssh-commit.js +10 -10
  413. package/dist/workspace/wip.d.ts.map +1 -1
  414. package/dist/workspace/wip.js +11 -3
  415. package/package.json +1 -1
  416. package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
  417. package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
  418. package/src/__golden__/wave-loop/fan-out-only.json +6 -0
  419. package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  420. package/src/__golden__/wave-loop/op-cost-member.json +3 -0
  421. package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  422. package/src/__golden__/wave-loop/op-held-on.json +2 -0
  423. package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
  424. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  425. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  426. package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
  427. package/src/__golden__/wave-loop/op-per-run.json +7 -0
  428. package/src/__golden__/wave-loop/op-pr-review.json +7 -0
  429. package/src/__golden__/wave-loop/op-sealed.json +3 -0
  430. package/src/__golden__/wave-loop/op-spend.json +3 -0
  431. package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
  432. package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
  433. package/src/build-params.test.ts +46 -0
  434. package/src/build-params.ts +55 -1
  435. package/src/change-set-graph.test.ts +72 -0
  436. package/src/change-set-graph.ts +141 -0
  437. package/src/change-set.ts +87 -6
  438. package/src/chant-version.ts +16 -0
  439. package/src/cli/build-params-cli.ts +22 -9
  440. package/src/cli/commands/build.ts +25 -11
  441. package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
  442. package/src/cli/commands/check-lexicon-docs.ts +39 -2
  443. package/src/cli/commands/check-lexicon.ts +14 -2
  444. package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
  445. package/src/cli/commands/init-lexicon.test.ts +25 -0
  446. package/src/cli/commands/init-lexicon.ts +18 -8
  447. package/src/cli/handlers/approve-release.test.ts +84 -0
  448. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  449. package/src/cli/handlers/approve.ts +65 -2
  450. package/src/cli/handlers/build.test.ts +67 -0
  451. package/src/cli/handlers/build.ts +46 -4
  452. package/src/cli/handlers/change-set.ts +49 -1
  453. package/src/cli/handlers/fan-out.test.ts +228 -1
  454. package/src/cli/handlers/fan-out.ts +53 -6
  455. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  456. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  457. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  458. package/src/cli/handlers/operator.test.ts +37 -0
  459. package/src/cli/handlers/pr.test.ts +341 -0
  460. package/src/cli/handlers/pr.ts +292 -47
  461. package/src/cli/handlers/promote.test.ts +669 -7
  462. package/src/cli/handlers/promote.ts +464 -29
  463. package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
  464. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  465. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  466. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  467. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  468. package/src/cli/handlers/release.e2e.test.ts +199 -0
  469. package/src/cli/handlers/release.ts +593 -0
  470. package/src/cli/handlers/run-auto-release.test.ts +80 -0
  471. package/src/cli/handlers/run-events-cmd.test.ts +92 -0
  472. package/src/cli/handlers/run-release.test.ts +55 -0
  473. package/src/cli/handlers/run.test.ts +30 -0
  474. package/src/cli/handlers/run.ts +243 -18
  475. package/src/cli/main.test.ts +8 -0
  476. package/src/cli/main.ts +143 -17
  477. package/src/cli/mcp/never-over-mcp.test.ts +2 -0
  478. package/src/cli/mcp/workspace-tools.ts +1 -1
  479. package/src/cli/parse-args.ts +56 -5
  480. package/src/cli/registry.ts +43 -5
  481. package/src/cli/run-events.ts +57 -0
  482. package/src/cli/version.ts +3 -13
  483. package/src/codegen/docs-in-site.test.ts +166 -0
  484. package/src/codegen/docs-in-site.ts +182 -0
  485. package/src/codegen/docs-site/AgentPrompt.astro +26 -1
  486. package/src/codegen/docs-types.ts +14 -1
  487. package/src/codegen/docs.ts +60 -7
  488. package/src/codegen/rehype-base-url.d.mts +3 -1
  489. package/src/codegen/rehype-base-url.mjs +15 -9
  490. package/src/codegen/rehype-base-url.test.ts +34 -0
  491. package/src/components/auto-release.test.ts +30 -0
  492. package/src/components/auto-release.ts +9 -1
  493. package/src/components/capability.ts +9 -0
  494. package/src/components/cli-support.test.ts +82 -0
  495. package/src/components/cli-support.ts +16 -9
  496. package/src/components/driver.test.ts +24 -0
  497. package/src/components/driver.ts +165 -56
  498. package/src/components/fan-out-inputs.test.ts +85 -0
  499. package/src/components/fan-out-inputs.ts +192 -0
  500. package/src/components/fan-out-record.test.ts +10 -0
  501. package/src/components/fan-out-record.ts +19 -0
  502. package/src/components/fan-out-run.ts +104 -35
  503. package/src/components/fan-out.ts +4 -0
  504. package/src/components/pr-pipeline.ts +35 -4
  505. package/src/components/pr-run.ts +30 -1
  506. package/src/components/promote-order.test.ts +90 -0
  507. package/src/components/promote-order.ts +75 -0
  508. package/src/components/promote.test.ts +10 -1
  509. package/src/components/promote.ts +94 -3
  510. package/src/components/release-compile.test.ts +147 -0
  511. package/src/components/release-compile.ts +459 -0
  512. package/src/components/release-gate.test.ts +215 -0
  513. package/src/components/release-gate.ts +355 -0
  514. package/src/components/release-promote.test.ts +159 -0
  515. package/src/components/release-promote.ts +232 -0
  516. package/src/components/run-progress.ts +7 -0
  517. package/src/components/wave-plan.ts +16 -3
  518. package/src/config.ts +15 -1
  519. package/src/flag-params.test.ts +144 -0
  520. package/src/flag-params.ts +134 -0
  521. package/src/index.ts +2 -0
  522. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  523. package/src/lifecycle/change-set-store.test.ts +217 -0
  524. package/src/lifecycle/change-set-store.ts +113 -0
  525. package/src/lifecycle/commit-sha.ts +23 -0
  526. package/src/lifecycle/delivery-metrics.ts +263 -0
  527. package/src/lifecycle/deploy-outcome.ts +147 -0
  528. package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
  529. package/src/lifecycle/gate-change-set.ts +164 -0
  530. package/src/lifecycle/gate-ledger.test.ts +33 -0
  531. package/src/lifecycle/gate-ledger.ts +68 -3
  532. package/src/lifecycle/git.ts +61 -8
  533. package/src/lifecycle/index.ts +4 -0
  534. package/src/lifecycle/mixed-environment.test.ts +97 -0
  535. package/src/lifecycle/mixed-environment.ts +204 -0
  536. package/src/lifecycle/publish-attachments.ts +196 -0
  537. package/src/lifecycle/publish-credentials.ts +84 -0
  538. package/src/lifecycle/publish-declaration.ts +95 -0
  539. package/src/lifecycle/publish-forge.ts +325 -0
  540. package/src/lifecycle/publish-npm.ts +259 -0
  541. package/src/lifecycle/publish-oci.ts +396 -0
  542. package/src/lifecycle/publish-record.ts +113 -0
  543. package/src/lifecycle/publish-resume.test.ts +168 -0
  544. package/src/lifecycle/publish-targets.test.ts +494 -0
  545. package/src/lifecycle/publish-targets.ts +100 -0
  546. package/src/lifecycle/release-cut-signed.test.ts +180 -0
  547. package/src/lifecycle/release-cut.test.ts +483 -0
  548. package/src/lifecycle/release-cut.ts +503 -0
  549. package/src/lifecycle/release-flag.test.ts +112 -0
  550. package/src/lifecycle/release-join.test.ts +68 -0
  551. package/src/lifecycle/release-join.ts +182 -0
  552. package/src/lifecycle/release-ledger.test.ts +14 -0
  553. package/src/lifecycle/release-ledger.ts +85 -17
  554. package/src/lifecycle/release-publish.test.ts +225 -0
  555. package/src/lifecycle/release-publish.ts +568 -0
  556. package/src/lifecycle/release-revocation.ts +147 -0
  557. package/src/lifecycle/release-store.ts +434 -0
  558. package/src/lifecycle/release-verify.test.ts +110 -0
  559. package/src/lifecycle/release-verify.ts +120 -0
  560. package/src/lifecycle/release-workstation.test.ts +204 -0
  561. package/src/lifecycle/release-workstation.ts +216 -0
  562. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  563. package/src/lifecycle/retention-policy.ts +63 -0
  564. package/src/lifecycle/retention.test.ts +363 -0
  565. package/src/lifecycle/retention.ts +508 -0
  566. package/src/lifecycle/run-ledger.test.ts +10 -0
  567. package/src/lifecycle/run-ledger.ts +6 -0
  568. package/src/op/activities/change-coverage.ts +4 -1
  569. package/src/op/activities/source-release.ts +8 -3
  570. package/src/op/activities/source-rollback.ts +8 -4
  571. package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
  572. package/src/op/gate-name.ts +13 -3
  573. package/src/op/gate-release.test.ts +126 -0
  574. package/src/op/gate-run-locator.test.ts +110 -0
  575. package/src/op/gate-run-locator.ts +35 -0
  576. package/src/op/gate.ts +88 -6
  577. package/src/op/local-executor.ts +207 -52
  578. package/src/op/op-waves-run.ts +4 -0
  579. package/src/op/run-events.test.ts +166 -0
  580. package/src/op/run-live.ts +1 -1
  581. package/src/op/runtime.ts +39 -0
  582. package/src/op/runtimes/local.test.ts +16 -0
  583. package/src/op/runtimes/local.ts +9 -0
  584. package/src/plan-summary.test.ts +20 -0
  585. package/src/plan-summary.ts +8 -1
  586. package/src/point-decision.ts +12 -2
  587. package/src/pr-loop.ts +25 -4
  588. package/src/pr-report-url.test.ts +131 -0
  589. package/src/pr-report-url.ts +209 -0
  590. package/src/provenance.ts +2 -2
  591. package/src/run-event-store.test.ts +144 -0
  592. package/src/run-event-store.ts +273 -0
  593. package/src/run-event-stream.test.ts +175 -0
  594. package/src/run-event-stream.ts +309 -0
  595. package/src/run-event.test.ts +277 -0
  596. package/src/run-event.ts +471 -0
  597. package/src/wave-loop-golden.test.ts +9 -0
  598. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  599. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  600. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  601. package/src/workspace/__fixtures__/run-event-check.ts +58 -0
  602. package/src/workspace/ancestry-index.test.ts +93 -0
  603. package/src/workspace/change-set-show.schema.json +102 -0
  604. package/src/workspace/change-set-show.ts +72 -0
  605. package/src/workspace/change-set.schema.json +32 -1
  606. package/src/workspace/changes-cli.ts +10 -1
  607. package/src/workspace/changes.schema.json +135 -0
  608. package/src/workspace/changes.test.ts +1 -1
  609. package/src/workspace/changes.ts +84 -2
  610. package/src/workspace/check.schema.json +4 -2
  611. package/src/workspace/checks/records.ts +35 -1
  612. package/src/workspace/checks.ts +2 -1
  613. package/src/workspace/ci-green-cli.ts +75 -13
  614. package/src/workspace/ci-green-forge.ts +24 -4
  615. package/src/workspace/ci-green-runs.test.ts +242 -0
  616. package/src/workspace/ci-green-signed.test.ts +302 -0
  617. package/src/workspace/ci-green-workflow.test.ts +16 -0
  618. package/src/workspace/ci-green-workflow.ts +12 -0
  619. package/src/workspace/ci-green.test.ts +131 -2
  620. package/src/workspace/ci-green.ts +635 -42
  621. package/src/workspace/ci-last-green.schema.json +32 -2
  622. package/src/workspace/ci-list.schema.json +525 -0
  623. package/src/workspace/commit-links.test.ts +279 -0
  624. package/src/workspace/commit-links.ts +183 -0
  625. package/src/workspace/declaration.schema.json +76 -0
  626. package/src/workspace/declaration.test.ts +28 -0
  627. package/src/workspace/declaration.ts +60 -2
  628. package/src/workspace/declared-kinds.ts +40 -1
  629. package/src/workspace/delivery-metrics.schema.json +519 -0
  630. package/src/workspace/delivery-metrics.test.ts +236 -0
  631. package/src/workspace/delivery-metrics.ts +286 -0
  632. package/src/workspace/flags-read.ts +121 -0
  633. package/src/workspace/flags.test.ts +141 -0
  634. package/src/workspace/flags.ts +157 -0
  635. package/src/workspace/graph-cli.ts +1 -1
  636. package/src/workspace/identity-gate-rule.ts +91 -7
  637. package/src/workspace/identity.ts +14 -1
  638. package/src/workspace/intent-cli.ts +46 -5
  639. package/src/workspace/intent-trace.test.ts +173 -0
  640. package/src/workspace/intent-trace.ts +484 -0
  641. package/src/workspace/intent.schema.json +702 -6
  642. package/src/workspace/intent.ts +196 -26
  643. package/src/workspace/ls.schema.json +3 -1
  644. package/src/workspace/ls.ts +6 -0
  645. package/src/workspace/member-pipeline.test.ts +17 -0
  646. package/src/workspace/member-pipeline.ts +9 -0
  647. package/src/workspace/older-records.test.ts +412 -0
  648. package/src/workspace/overlap.test.ts +165 -0
  649. package/src/workspace/overlap.ts +340 -0
  650. package/src/workspace/promotion-order.ts +97 -0
  651. package/src/workspace/read-contract.e2e.test.ts +5 -1
  652. package/src/workspace/reason-codes.test.ts +7 -1
  653. package/src/workspace/reason-codes.ts +7 -1
  654. package/src/workspace/record-assets.test.ts +2 -1
  655. package/src/workspace/records-contract.e2e.test.ts +2 -1
  656. package/src/workspace/records.schema.json +21 -2
  657. package/src/workspace/records.ts +36 -2
  658. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  659. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  660. package/src/workspace/releases.schema.json +513 -0
  661. package/src/workspace/releases.test.ts +108 -0
  662. package/src/workspace/releases.ts +133 -0
  663. package/src/workspace/run-event.schema.json +295 -0
  664. package/src/workspace/status-contract.test.ts +81 -1
  665. package/src/workspace/status-gates.ts +64 -1
  666. package/src/workspace/status-mixed.test.ts +152 -0
  667. package/src/workspace/status-revoked.test.ts +153 -0
  668. package/src/workspace/status-stewards.ts +6 -0
  669. package/src/workspace/status.schema.json +268 -2
  670. package/src/workspace/status.ts +334 -4
  671. package/src/workspace/trust/ci-tag-statement.ts +151 -0
  672. package/src/workspace/trust/op-run-statement.ts +148 -0
  673. package/src/workspace/trust/rotation.ts +61 -14
  674. package/src/workspace/trust/seal.ts +10 -0
  675. package/src/workspace/trust/ssh-commit.ts +10 -9
  676. package/src/workspace/wip.ts +11 -2
  677. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
  678. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
  679. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
  680. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
  681. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
@@ -1,32 +1,61 @@
1
1
  /**
2
2
  * `chant ci` (#3573, ws-103): which commits passed CI.
3
3
  *
4
- * ci last-green [--json] the newest commit on the branch with a green tag and no revoked tag
5
- * ci tick [--dry-run] [--forge github] tag the commits that turned green, revoke the ones that turned red
4
+ * ci last-green [--json] [--from <rev>] the newest commit on the branch with a green tag and no revoked tag
5
+ * ci list [--json] [--limit <n>] [--since <rev>] [--forge github] [--source checks|runs]
6
+ * the branch's commits within the window, each with its state and phase results
7
+ * ci tick [--dry-run] [--forge github] [--source checks|runs] [--key <runner key.pem>]
8
+ * tag the commits that turned green, revoke the ones that turned red
6
9
  * ci workflow [--workflow <name>]... [--output <file>] [--chant <command>] [--install <command>]
7
- * [--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>]
10
+ * [--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>] [--runner-key-secret <NAME>]
8
11
  * write the root CI file that runs the tick
9
12
  *
10
- * `last-green --json` prints `ci-last-green.schema.json`. The tick and the
13
+ * The tick signs its tags with a runner key (#4031) from `--key <file>` or
14
+ * the PEM in `CHANT_RUNNER_KEY`, which the generated workflow sets from the
15
+ * secret `--runner-key-secret` names.
16
+ *
17
+ * `--source runs` (or `ci.green.source: "runs"`) reads phase results from
18
+ * chant's own signed Op runs on `chant/lifecycle` in place of the forge's
19
+ * check runs (#4044); a tick from runs needs no forge token.
20
+ *
21
+ * `last-green --json` prints `ci-last-green.schema.json` and `list --json` prints
22
+ * `ci-list.schema.json`. The tick and the
11
23
  * workflow print text.
12
24
  */
13
25
 
14
- import { existsSync, realpathSync } from "node:fs";
26
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
15
27
  import { join, relative, resolve, sep } from "node:path";
16
28
  import { formatError } from "../cli/format";
17
29
  import type { CommandContext } from "../cli/registry";
18
30
  import { findWorkspaceRoot } from "../project-root";
19
- import { ciTick, CiGreenError, formatTick, lastGreen } from "./ci-green";
31
+ import { ciList, ciTick, CiGreenError, formatList, formatTick, lastGreen, RUNNER_KEY_ENV } from "./ci-green";
20
32
  import { ciForgeFromEnv } from "./ci-green-forge";
21
33
  import { ACTIONS_NAME, CI_GREEN_WORKFLOW_FILE, ciGreenHeader, type PushToken, readWorkflowSources, renderCiGreenWorkflow, workflowsHolding, writeWorkflowFile } from "./ci-green-workflow";
22
- import { readDeclaration, readerVersion, WorkspaceReadError } from "./declaration";
34
+ import { readDeclaration, readerVersion, WorkspaceReadError, type CiSource } from "./declaration";
23
35
  import { recordGeneratedFiles, resolveMemberContext, shellArg } from "./member-pipeline";
24
36
  import { gitTop, workingTree } from "./tree";
25
37
 
26
- const USAGE = "chant ci last-green [--json] | ci tick [--dry-run] [--forge github] | ci workflow [--workflow <name>]... [--output <file>] [--chant <command>] [--install <command>] [--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>]";
38
+ const USAGE =
39
+ "chant ci last-green [--json] [--from <rev>] | ci list [--json] [--limit <n>] [--since <rev>] [--forge github] [--source checks|runs] | ci tick [--dry-run] [--forge github] [--source checks|runs] [--key <runner key.pem>] | " +
40
+ "ci workflow [--workflow <name>]... [--output <file>] [--chant <command>] [--install <command>] [--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>] [--runner-key-secret <NAME>]";
41
+
42
+ export { RUNNER_KEY_ENV };
43
+
44
+ /** `--source`, checked (#4044): `checks` or `runs`, or undefined to follow `ci.green.source`. */
45
+ function sourceOf(args: CommandContext["args"]): CiSource | undefined {
46
+ const v = args.source;
47
+ if (v === undefined) return undefined;
48
+ if (v !== "checks" && v !== "runs") throw new Error(`--source takes checks (the forge's check runs) or runs (chant's own Op runs), not ${JSON.stringify(v)}`);
49
+ return v;
50
+ }
51
+
52
+ /** Each ignored tag as a warning line for stderr (#4031). */
53
+ function warnIgnored(ignored: readonly { tag: string; status: string; reason: string }[]): void {
54
+ for (const t of ignored) console.error(`warning: ignored ${t.tag} (${t.status}): ${t.reason}`);
55
+ }
27
56
 
28
57
  export async function runCiLastGreen(ctx: CommandContext): Promise<number> {
29
- const doc = lastGreen({ cwd: process.cwd() });
58
+ const doc = lastGreen({ cwd: process.cwd(), ...(ctx.args.migrateFrom !== undefined ? { from: ctx.args.migrateFrom } : {}) });
30
59
  if (ctx.args.json) {
31
60
  console.log(JSON.stringify(doc, null, 2));
32
61
  return "error" in doc ? 1 : 0;
@@ -35,6 +64,7 @@ export async function runCiLastGreen(ctx: CommandContext): Promise<number> {
35
64
  console.error(formatError({ message: doc.error.message, hint: USAGE }));
36
65
  return 1;
37
66
  }
67
+ warnIgnored(doc.ignored);
38
68
  if (!doc.commit) {
39
69
  console.error(`No first-parent commit of ${doc.ref} has a ci/green tag without a ci/revoked one. Fetch the tags first: git fetch origin --tags`);
40
70
  return 1;
@@ -43,12 +73,39 @@ export async function runCiLastGreen(ctx: CommandContext): Promise<number> {
43
73
  return 0;
44
74
  }
45
75
 
76
+ export async function runCiList(ctx: CommandContext): Promise<number> {
77
+ try {
78
+ const limit = ctx.args.limit;
79
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1)) throw new Error("--limit takes a whole number of commits, 1 or more");
80
+ // Only an explicit --forge reaches the network; without it the list reads local refs and tags.
81
+ const forge = ctx.args.forge !== undefined ? ciForgeFromEnv(ctx.args.forge) : undefined;
82
+ const source = sourceOf(ctx.args);
83
+ const doc = await ciList({ cwd: process.cwd(), limit, since: ctx.args.since, forge, ...(source ? { source } : {}) });
84
+ if (ctx.args.json) {
85
+ console.log(JSON.stringify(doc, null, 2));
86
+ return "error" in doc ? 1 : 0;
87
+ }
88
+ if ("error" in doc) {
89
+ console.error(formatError({ message: doc.error.message, hint: USAGE }));
90
+ return 1;
91
+ }
92
+ console.log(formatList(doc));
93
+ return 0;
94
+ } catch (err) {
95
+ console.error(formatError({ message: `chant ci list: ${describe(err)}`, hint: USAGE }));
96
+ return 1;
97
+ }
98
+ }
99
+
46
100
  const describe = (err: unknown) => (err instanceof WorkspaceReadError ? err.describe() : err instanceof Error ? err.message : String(err));
47
101
 
48
102
  export async function runCiTick(ctx: CommandContext): Promise<number> {
49
103
  try {
50
- const forge = ciForgeFromEnv(ctx.args.forge ?? "github");
51
- const result = await ciTick({ cwd: process.cwd(), forge, dryRun: ctx.args.dryRun === true });
104
+ const source = sourceOf(ctx.args);
105
+ // The forge is made only when the source is check runs, so a tick from chant runs needs no forge token.
106
+ const forge = () => ciForgeFromEnv(ctx.args.forge ?? "github");
107
+ const runnerKey = ctx.args.key !== undefined ? readFileSync(resolve(ctx.args.key), "utf-8") : process.env[RUNNER_KEY_ENV] || undefined;
108
+ const result = await ciTick({ cwd: process.cwd(), forge, dryRun: ctx.args.dryRun === true, ...(source ? { source } : {}), ...(runnerKey !== undefined ? { runnerKey } : {}) });
52
109
  console.log(formatTick(result, ctx.args.dryRun === true));
53
110
  return 0;
54
111
  } catch (err) {
@@ -60,7 +117,7 @@ export async function runCiTick(ctx: CommandContext): Promise<number> {
60
117
  /** The push token the flags ask for, or none for the Actions token. Throws on a half-given or clashing pair. */
61
118
  function pushToken(args: CommandContext["args"]): PushToken | undefined {
62
119
  const { ciTokenSecret: secret, ciAppIdVar: appIdVar, ciAppKeySecret: appKeySecret } = args;
63
- for (const [flag, name] of [["--token-secret", secret], ["--app-id-var", appIdVar], ["--app-key-secret", appKeySecret]] as const) {
120
+ for (const [flag, name] of [["--token-secret", secret], ["--app-id-var", appIdVar], ["--app-key-secret", appKeySecret], ["--runner-key-secret", args.ciRunnerKeySecret]] as const) {
64
121
  if (name !== undefined && !ACTIONS_NAME.test(name)) throw new Error(`${flag} takes a secret or variable name, letters, digits and underscores, not ${JSON.stringify(name)}`);
65
122
  }
66
123
  if (secret !== undefined && (appIdVar !== undefined || appKeySecret !== undefined)) throw new Error("--token-secret and --app-id-var/--app-key-secret each name the push token; pass one or the other");
@@ -102,11 +159,16 @@ export async function runCiWorkflow(ctx: CommandContext): Promise<number> {
102
159
  if (ctx.args.ciInstall) parts.push("--install", ctx.args.ciInstall);
103
160
  if (push && "secret" in push) parts.push("--token-secret", push.secret);
104
161
  if (push && "appIdVar" in push) parts.push("--app-id-var", push.appIdVar, "--app-key-secret", push.appKeySecret);
162
+ const runnerKeySecret = ctx.args.ciRunnerKeySecret;
163
+ if (runnerKeySecret) parts.push("--runner-key-secret", runnerKeySecret);
164
+ if (green.signedSince && !runnerKeySecret) {
165
+ console.error(`warning: ci.green.signedSince is set, so the tick needs a runner key; pass --runner-key-secret <NAME> naming the repository secret that holds it, or every tick will fail`);
166
+ }
105
167
  const command = parts.map(shellArg).join(" ");
106
168
  const workspaceRoot = relative(repoRoot, root).split(sep).join("/") || ".";
107
169
  // The install runs in the workspace root, so its lockfile there keys setup-node's npm cache.
108
170
  const npmLock = ctx.args.ciInstall && existsSync(join(root, "package-lock.json")) ? (workspaceRoot === "." ? "package-lock.json" : `${workspaceRoot}/package-lock.json`) : undefined;
109
- const yaml = renderCiGreenWorkflow({ green, workflows, chantVersion: readerVersion(), workspaceRoot, chant: ctx.args.ciChant, install: ctx.args.ciInstall, npmLock, push });
171
+ const yaml = renderCiGreenWorkflow({ green, workflows, chantVersion: readerVersion(), workspaceRoot, chant: ctx.args.ciChant, install: ctx.args.ciInstall, npmLock, push, ...(runnerKeySecret ? { runnerKeySecret } : {}) });
110
172
  const written = writeWorkflowFile(target, ciGreenHeader(command, member ? "member" : "root") + yaml);
111
173
  if (member) recordGeneratedFiles(member.memberRoot, [{ path: fromRepo, command }]);
112
174
  console.log(`Wrote ${written}: ticks when ${workflows.join(", ")} complete${workflows.length === 1 ? "s" : ""} on ${green.branch}, and every 15 minutes.`);
@@ -6,15 +6,18 @@
6
6
  * The tags themselves are git's, pushed to the remote like any other ref, so
7
7
  * the forge is asked for check runs and nothing else. GitHub is the one
8
8
  * forge implemented. Forgejo reports commit statuses rather than check runs,
9
- * and GitLab reports pipeline jobs; each can implement {@link CiForge} by
10
- * mapping those to {@link CheckRun}s, and until one does `--forge` refuses
11
- * them by name.
9
+ * and GitLab reports pipeline jobs, and neither gets a reader here (#4021): a
10
+ * project on either runs its pipelines as chant Ops and sets
11
+ * `ci.green.source` to `runs`, so the tick reads signed Op runs from the run
12
+ * ledger instead (#4044, `./ci-green.ts`). `--forge` refuses them by name and
13
+ * says so.
12
14
  *
13
15
  * The client takes a `fetch` like the pull-request loop's (`../pr-forge.ts`),
14
16
  * so the tests drive it with a recording fake and no network.
15
17
  */
16
18
 
17
19
  import { ForgeApiError, ForgeEnvironmentError, type ForgeFetch, type ForgeKind } from "../pr-forge";
20
+ import type { OpRunRecord } from "../op/runtime";
18
21
 
19
22
  /** One attempt of one check run on a commit. */
20
23
  export interface CheckRun {
@@ -27,6 +30,21 @@ export interface CheckRun {
27
30
  conclusion: string | null;
28
31
  completedAt: string | null;
29
32
  url: string | null;
33
+ /**
34
+ * Set when the "check run" is a chant Op run (#4044, `ci.green.source:
35
+ * "runs"`): the run's id and env, the runner whose statement it carries,
36
+ * and that statement. `name` is then the Op's name and `id` the run's end
37
+ * time in milliseconds, which orders a re-run after the run it replaced.
38
+ */
39
+ opRun?: OpRunRef;
40
+ }
41
+
42
+ /** The Op run a {@link CheckRun} stands for (#4044). */
43
+ export interface OpRunRef {
44
+ id: string;
45
+ env: string;
46
+ signer: string;
47
+ statement: NonNullable<OpRunRecord["statement"]>;
30
48
  }
31
49
 
32
50
  export interface CiForge {
@@ -87,7 +105,9 @@ export function githubCiForge(opts: GithubCiForgeOptions): CiForge {
87
105
  /** Thrown for a forge `chant ci tick` has no check-run client for yet. */
88
106
  export class CiForgeUnsupportedError extends Error {
89
107
  constructor(kind: string) {
90
- super(`check runs are read from GitHub only so far, and --forge asked for ${kind}; see #3573`);
108
+ super(
109
+ `check runs are read from GitHub only, and --forge asked for ${kind}. A ${kind} project runs its pipelines as chant Ops and sets ci.green.source to "runs" (or passes --source runs), so the tick reads signed Op runs from chant/lifecycle instead`,
110
+ );
91
111
  this.name = "CiForgeUnsupportedError";
92
112
  }
93
113
  }
@@ -0,0 +1,242 @@
1
+ /**
2
+ * #4044: chant run results as a status source for `ci tick`. With
3
+ * `ci.green.source: "runs"` (or `--source runs`) the tick reads Op runs from
4
+ * the run ledger on `chant/lifecycle` instead of a forge's check runs. A run
5
+ * counts only when its record carries a statement signed by a runner key
6
+ * `.chant/trust.json` lists for a principal holding the `runner` role; the
7
+ * tick ignores and reports any other. Every tag it makes is signed, and names
8
+ * the runs behind it.
9
+ *
10
+ * Each test works in its own temp repository with a bare origin, and every
11
+ * key is a throwaway one. Nothing is written in process.cwd().
12
+ */
13
+
14
+ import { execFileSync } from "node:child_process";
15
+ import { createPublicKey, generateKeyPairSync } from "node:crypto";
16
+ import { afterAll, describe, expect, test } from "vitest";
17
+ import { cleanScratch, contract, declaration, git, scratchDir, writeFiles } from "./__fixtures__/contract-repo";
18
+ import { ciList, ciTick, formatList, formatTick, opRunSigner, readOpRunSource } from "./ci-green";
19
+ import { ciForgeFromEnv } from "./ci-green-forge";
20
+ import { sshFromEd25519 } from "./trust/dsse";
21
+ import { verifyCiTagStatement } from "./trust/ci-tag-statement";
22
+ import { verifyOpRunStatement } from "./trust/op-run-statement";
23
+ import { appendRunRecord, readRunLedger } from "../lifecycle/run-ledger";
24
+ import { runOpLocally, OpRunFailure } from "../op/local-executor";
25
+ import type { ActivityFn } from "../op/activity-registry";
26
+ import type { OpConfig } from "../op/types";
27
+ import type { OpRunRecord } from "../op/runtime";
28
+ import listSchema from "./ci-list.schema.json";
29
+
30
+ afterAll(cleanScratch);
31
+
32
+ const NOW = new Date("2026-10-06T12:00:00Z");
33
+ const hoursAgo = (h: number) => new Date(NOW.getTime() - h * 3_600_000).toISOString();
34
+ const GREEN = { branch: "main", phases: { lint: ["lint"], test: ["test"] }, require: ["lint", "test"], source: "runs" };
35
+
36
+ /** A throwaway runner key: its PEM and its ssh public key line. */
37
+ function runnerKey(): { pem: string; ssh: string } {
38
+ const { privateKey } = generateKeyPairSync("ed25519");
39
+ const jwk = createPublicKey(privateKey).export({ format: "jwk" }) as { x: string };
40
+ return { pem: privateKey.export({ type: "pkcs8", format: "pem" }).toString(), ssh: sshFromEd25519(Buffer.from(jwk.x, "base64url")) };
41
+ }
42
+
43
+ const BOT = runnerKey();
44
+ const RUNNERS = [{ principal: "ci-bot", class: "runner" as const, key: BOT.ssh }];
45
+
46
+ function commitAt(root: string, hours: number, file: string): string {
47
+ writeFiles(root, { [file]: `${file} ${hours}\n` });
48
+ const date = hoursAgo(hours);
49
+ const env = { ...process.env, GIT_AUTHOR_DATE: date, GIT_COMMITTER_DATE: date };
50
+ for (const args of [["add", "-A"], ["commit", "-q", "-m", file]]) {
51
+ execFileSync("git", ["-c", "user.name=t", "-c", "user.email=t@example.com", "-c", "commit.gpgsign=false", ...args], { cwd: root, env, stdio: "ignore" });
52
+ }
53
+ return git(root, "rev-parse", "HEAD");
54
+ }
55
+
56
+ /** A checkout on main with a bare origin, declaring `ci.green` from runs and listing BOT as the runner `ci-bot`. */
57
+ function workspace(green: Record<string, unknown> = GREEN): { root: string; origin: string } {
58
+ const origin = scratchDir("chant-ci-runs-origin-");
59
+ git(origin, "init", "-q", "--bare");
60
+ const root = scratchDir("chant-ci-runs-");
61
+ git(root, "init", "-q");
62
+ git(root, "symbolic-ref", "HEAD", "refs/heads/main");
63
+ git(root, "config", "user.name", "t");
64
+ git(root, "config", "user.email", "t@example.com");
65
+ writeFiles(root, {
66
+ "chant.workspace.json": declaration([{ name: "app", dir: "app", kind: "other", because: "the app" }], { ci: { green } }),
67
+ ".chant/trust.json": JSON.stringify({ schema: 1, runners: [{ principal: "ci-bot", class: "runner", key: BOT.ssh }], roles: { runner: ["ci-bot"] } }),
68
+ "app/a.txt": "a\n",
69
+ });
70
+ git(root, "remote", "add", "origin", origin);
71
+ commitAt(root, 23, "app/init.txt");
72
+ return { root, origin };
73
+ }
74
+
75
+ const op = (name: string): OpConfig => ({ name, overview: `run ${name}`, labels: { Env: "ci" }, phases: [{ name: "Run", steps: [{ kind: "activity", fn: "step", args: {} }] }] });
76
+ const PASS = new Map<string, ActivityFn>([["step", async () => ({})]]);
77
+ const FAIL = new Map<string, ActivityFn>([
78
+ [
79
+ "step",
80
+ async () => {
81
+ throw new Error("red");
82
+ },
83
+ ],
84
+ ]);
85
+
86
+ /** Run Op `name` at the checkout's HEAD, recording on its ledger, signed with `pem` when given. */
87
+ async function runOp(root: string, name: string, opts: { pem?: string; fail?: boolean } = {}): Promise<OpRunRecord> {
88
+ const options = { cwd: root, ledger: { cwd: root }, ...(opts.pem ? { signRecord: opRunSigner(root, opts.pem) } : {}) };
89
+ try {
90
+ return (await runOpLocally(op(name), opts.fail ? FAIL : PASS, {}, undefined, options)).record;
91
+ } catch (err) {
92
+ if (err instanceof OpRunFailure) return err.result.record;
93
+ throw err;
94
+ }
95
+ }
96
+
97
+ /** Push main and the run ledger, as a CI job does after its runs. */
98
+ function push(root: string): void {
99
+ git(root, "push", "-q", "origin", "main");
100
+ git(root, "push", "-q", "origin", "chant/lifecycle");
101
+ }
102
+
103
+ const recordOf = (root: string, tag: string) => {
104
+ const note = git(root, "tag", "-l", "--format=%(contents)", tag);
105
+ return JSON.parse(note.slice(note.indexOf("{"))) as Record<string, unknown>;
106
+ };
107
+
108
+ describe("chant runs as the ci tick's status source (#4044)", () => {
109
+ const readList = contract(listSchema);
110
+
111
+ test("a commit is tagged green from signed local Op runs, and the tag names and carries them", async () => {
112
+ const { root } = workspace();
113
+ const a = commitAt(root, 3, "app/a.txt");
114
+ const lint = await runOp(root, "lint", { pem: BOT.pem });
115
+ const test1 = await runOp(root, "test", { pem: BOT.pem });
116
+ expect(lint.gitSha).toBe(a);
117
+ expect(lint.statement).toBeDefined();
118
+ // The ledger holds the signed record, and it verifies there.
119
+ const stored = (await readRunLedger("ci", "test", { cwd: root })).records[0]!;
120
+ expect(stored.statement).toEqual(test1.statement);
121
+ expect(verifyOpRunStatement(stored, RUNNERS, { runner: ["ci-bot"] })).toMatchObject({ status: "signed", signer: "ci-bot" });
122
+ push(root);
123
+
124
+ // No forge is given: a tick from runs needs none.
125
+ const tick = await ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem });
126
+ expect(tick.source).toBe("runs");
127
+ expect(tick.made).toEqual([`ci/green/${a}`]);
128
+ expect(tick.signer).toBe("ci-bot");
129
+ expect(formatTick(tick)).toContain("judged from chant runs");
130
+
131
+ const record = recordOf(root, `ci/green/${a}`);
132
+ expect(record.source).toBe("runs");
133
+ const phases = record.phases as Record<string, { name: string; id: null; opRun: { id: string; env: string; signer: string; statement: unknown } }[]>;
134
+ expect(phases.lint![0]).toMatchObject({ name: "lint", id: null, conclusion: "success", opRun: { id: lint.id, env: "ci", signer: "ci-bot" } });
135
+ expect(phases.test![0]!.opRun.id).toBe(test1.id);
136
+ expect(phases.test![0]!.opRun.statement).toEqual(test1.statement);
137
+ expect(verifyCiTagStatement(record, { sha: a, kind: "green", branch: "main" }, RUNNERS, { runner: ["ci-bot"] })).toMatchObject({ status: "signed" });
138
+
139
+ // The tag is pushed, and ci list reads it.
140
+ expect(git(scratchDir("chant-ci-runs-peek-"), "ls-remote", "--tags", git(root, "remote", "get-url", "origin"))).toContain(`refs/tags/ci/green/${a}`);
141
+ const list = await ciList({ cwd: root, now: NOW });
142
+ readList.expectValid(list);
143
+ const at = (list as Extract<typeof list, { commits: unknown }>).commits.find((c) => c.sha === a)!;
144
+ expect(at.state).toBe("green");
145
+ expect(at.phases[0]).toMatchObject({ phase: "lint", result: "pass", runs: [{ name: "lint", id: null, opRun: { id: lint.id, env: "ci", signer: "ci-bot" } }] });
146
+ expect(at.ignoredRuns).toEqual([]);
147
+
148
+ // A second tick over the same runs changes nothing.
149
+ expect((await ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem })).made).toEqual([]);
150
+ });
151
+
152
+ test("an unsigned run is refused: it neither greens nor revokes a commit, and is reported", async () => {
153
+ const { root } = workspace();
154
+ const a = commitAt(root, 3, "app/a.txt");
155
+ await runOp(root, "lint", { pem: BOT.pem });
156
+ const unsigned = await runOp(root, "test");
157
+ expect(unsigned.statement).toBeUndefined();
158
+ push(root);
159
+
160
+ const tick = await ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem });
161
+ expect(tick.made).toEqual([]);
162
+ const at = tick.commits.find((c) => c.sha === a)!;
163
+ expect(at.verdict?.verdict).toBe("pending");
164
+ expect(at.ignoredRuns).toEqual([{ run: unsigned.id, op: "test", env: "ci", status: "unsigned", reason: expect.stringContaining("carries no statement signed by a runner key") }]);
165
+ expect(formatTick(tick)).toContain(`ignored run ${unsigned.id} of test in ci (unsigned)`);
166
+ expect(git(root, "tag", "-l")).toBe("");
167
+
168
+ const list = (await ciList({ cwd: root, now: NOW })) as Extract<Awaited<ReturnType<typeof ciList>>, { commits: unknown }>;
169
+ readList.expectValid(list);
170
+ const listed = list.commits.find((c) => c.sha === a)!;
171
+ expect(listed.phases.map((p) => [p.phase, p.result])).toEqual([
172
+ ["lint", "pass"],
173
+ ["test", "pending"],
174
+ ]);
175
+ expect(listed.ignoredRuns?.map((r) => r.status)).toEqual(["unsigned"]);
176
+ expect(formatList(list)).toContain(`ignored run ${unsigned.id} of test in ci (unsigned)`);
177
+
178
+ // Signed now, the commit goes green; a later unsigned failure does not revoke it.
179
+ await runOp(root, "test", { pem: BOT.pem });
180
+ push(root);
181
+ expect((await ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem })).made).toEqual([`ci/green/${a}`]);
182
+ await runOp(root, "test", { fail: true });
183
+ push(root);
184
+ const after = await ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem });
185
+ expect(after.made).toEqual([]);
186
+ expect(after.commits.find((c) => c.sha === a)!.ignoredRuns?.map((r) => r.status)).toEqual(["unsigned", "unsigned"]);
187
+ });
188
+
189
+ test("a run signed by an unlisted key, or whose record changed after signing, does not count", async () => {
190
+ const { root } = workspace();
191
+ const a = commitAt(root, 3, "app/a.txt");
192
+ // A key trust.json does not list can't sign a run here at all.
193
+ const mallory = runnerKey();
194
+ expect(() => opRunSigner(root, mallory.pem)).toThrow(/lists no runner with this key/);
195
+ // A signed record whose outcome is edited before it reaches the ledger.
196
+ const signed = await runOp(root, "lint", { pem: BOT.pem });
197
+ const { version: _v, ...input } = { ...signed, status: "ok" as const, outcomes: { edited: true } };
198
+ await appendRunRecord({ ...input, id: "tampered" }, { cwd: root });
199
+ const source = await readOpRunSource(root, root, git(root, "rev-parse", "HEAD"));
200
+ expect(source.checkRuns(a).map((r) => r.opRun!.id)).toEqual([signed.id]);
201
+ expect(source.ignored.get(a)).toEqual([{ run: "tampered", op: "lint", env: "ci", status: "mismatch", reason: expect.stringContaining("does not match its record") }]);
202
+ });
203
+
204
+ test("a signed failing run revokes a green commit, and the revoked tag names the run", async () => {
205
+ const { root } = workspace();
206
+ const a = commitAt(root, 3, "app/a.txt");
207
+ await runOp(root, "lint", { pem: BOT.pem });
208
+ await runOp(root, "test", { pem: BOT.pem });
209
+ push(root);
210
+ expect((await ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem })).made).toEqual([`ci/green/${a}`]);
211
+
212
+ const red = await runOp(root, "test", { pem: BOT.pem, fail: true });
213
+ expect(red.status).toBe("fail");
214
+ push(root);
215
+ const tick = await ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem });
216
+ expect(tick.made).toEqual([`ci/revoked/${a}`]);
217
+ expect(formatTick(tick)).toContain("phase test, test failure");
218
+ const record = recordOf(root, `ci/revoked/${a}`);
219
+ expect(record).toMatchObject({ source: "runs", phase: "test", run: { name: "test", id: null, conclusion: "failure", opRun: { id: red.id, signer: "ci-bot" } } });
220
+ });
221
+
222
+ test("a tick from runs needs a runner key unless it is a dry run, --source overrides the declaration, and ci list refuses --forge", async () => {
223
+ const { root } = workspace({ ...GREEN, source: "checks" });
224
+ const a = commitAt(root, 3, "app/a.txt");
225
+ await runOp(root, "lint", { pem: BOT.pem });
226
+ await runOp(root, "test", { pem: BOT.pem });
227
+ push(root);
228
+ await expect(ciTick({ cwd: root, now: NOW, source: "runs" })).rejects.toThrow(/every tag it makes from them is signed by a runner key, and none was given/);
229
+ expect((await ciTick({ cwd: root, now: NOW, source: "runs", dryRun: true })).made).toEqual([`ci/green/${a}`]);
230
+ expect(git(root, "tag", "-l")).toBe("");
231
+ // The declaration says checks, so without --source the tick wants a forge.
232
+ await expect(ciTick({ cwd: root, now: NOW, runnerKey: BOT.pem })).rejects.toThrow(/needs a forge/);
233
+ await expect(ciList({ cwd: root, now: NOW, source: "runs", forge: { kind: "github", checkRuns: async () => [] } })).rejects.toThrow(/--forge reads check runs, and the source is chant runs/);
234
+ const list = (await ciList({ cwd: root, now: NOW, source: "runs" })) as Extract<Awaited<ReturnType<typeof ciList>>, { commits: unknown }>;
235
+ expect(list.commits.find((c) => c.sha === a)!.phases.map((p) => p.result)).toEqual(["pass", "pass"]);
236
+ });
237
+
238
+ test("GitLab and Forgejo get no check-run reader, and the refusal points at chant runs", () => {
239
+ expect(() => ciForgeFromEnv("gitlab", {})).toThrow(/sets ci.green.source to "runs"/);
240
+ expect(() => ciForgeFromEnv("forgejo", {})).toThrow(/--source runs/);
241
+ });
242
+ });