@intentius/chant 0.124.46 → 0.124.49

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 (726) hide show
  1. package/dist/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  2. package/dist/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  3. package/dist/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  4. package/dist/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  6. package/dist/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  7. package/dist/__golden__/wave-loop/op-cost-member.json +13 -0
  8. package/dist/__golden__/wave-loop/op-denied-source.json +16 -0
  9. package/dist/__golden__/wave-loop/op-denied.json +16 -0
  10. package/dist/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  11. package/dist/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  12. package/dist/__golden__/wave-loop/op-discover-rest.json +26 -0
  13. package/dist/__golden__/wave-loop/op-discover-wide.json +26 -0
  14. package/dist/__golden__/wave-loop/op-held-on-again.json +16 -0
  15. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  16. package/dist/__golden__/wave-loop/op-held-on.json +16 -0
  17. package/dist/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  18. package/dist/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  19. package/dist/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  20. package/dist/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  21. package/dist/__golden__/wave-loop/op-hooks-held.json +13 -0
  22. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  23. package/dist/__golden__/wave-loop/op-hooks-pass.json +13 -0
  24. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  25. package/dist/__golden__/wave-loop/op-hooks-share.json +33 -0
  26. package/dist/__golden__/wave-loop/op-outcome-held.json +10 -0
  27. package/dist/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  28. package/dist/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  29. package/dist/__golden__/wave-loop/op-outcome-replan.json +10 -0
  30. package/dist/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  31. package/dist/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  32. package/dist/__golden__/wave-loop/op-overridden.json +16 -0
  33. package/dist/__golden__/wave-loop/op-per-run.json +20 -0
  34. package/dist/__golden__/wave-loop/op-point.json +13 -0
  35. package/dist/__golden__/wave-loop/op-pr-review.json +10 -0
  36. package/dist/__golden__/wave-loop/op-sealed.json +10 -0
  37. package/dist/__golden__/wave-loop/op-spend-change-only.json +10 -0
  38. package/dist/__golden__/wave-loop/op-spend.json +10 -0
  39. package/dist/__golden__/wave-loop/op-wave-commands.json +55 -0
  40. package/dist/__golden__/wave-loop/op-wide-refused.json +26 -0
  41. package/dist/__golden__/wave-loop/op-wide-waiting.json +29 -0
  42. package/dist/__golden__/wave-loop/op-wide.json +39 -0
  43. package/dist/action-pins.d.ts +61 -0
  44. package/dist/action-pins.d.ts.map +1 -0
  45. package/dist/action-pins.js +77 -0
  46. package/dist/cli/commands/carve.d.ts +3 -0
  47. package/dist/cli/commands/carve.d.ts.map +1 -1
  48. package/dist/cli/commands/carve.js +36 -7
  49. package/dist/cli/commands/import-report.d.ts +26 -0
  50. package/dist/cli/commands/import-report.d.ts.map +1 -0
  51. package/dist/cli/commands/import-report.js +65 -0
  52. package/dist/cli/commands/import.d.ts +11 -1
  53. package/dist/cli/commands/import.d.ts.map +1 -1
  54. package/dist/cli/commands/import.js +22 -3
  55. package/dist/cli/commands/migrate.d.ts +13 -1
  56. package/dist/cli/commands/migrate.d.ts.map +1 -1
  57. package/dist/cli/commands/migrate.js +134 -10
  58. package/dist/cli/handlers/approve.d.ts.map +1 -1
  59. package/dist/cli/handlers/approve.js +41 -14
  60. package/dist/cli/handlers/build.d.ts.map +1 -1
  61. package/dist/cli/handlers/build.js +6 -1
  62. package/dist/cli/handlers/graph-reader.d.ts +26 -0
  63. package/dist/cli/handlers/graph-reader.d.ts.map +1 -0
  64. package/dist/cli/handlers/graph-reader.js +41 -0
  65. package/dist/cli/handlers/graph.d.ts.map +1 -1
  66. package/dist/cli/handlers/graph.js +14 -0
  67. package/dist/cli/handlers/ledger.d.ts +3 -0
  68. package/dist/cli/handlers/ledger.d.ts.map +1 -0
  69. package/dist/cli/handlers/ledger.js +55 -0
  70. package/dist/cli/handlers/migrate.d.ts.map +1 -1
  71. package/dist/cli/handlers/migrate.js +18 -11
  72. package/dist/cli/handlers/misc.d.ts.map +1 -1
  73. package/dist/cli/handlers/misc.js +12 -1
  74. package/dist/cli/handlers/pr-env.js +2 -2
  75. package/dist/cli/handlers/pr.d.ts +15 -1
  76. package/dist/cli/handlers/pr.d.ts.map +1 -1
  77. package/dist/cli/handlers/pr.js +183 -40
  78. package/dist/cli/handlers/query.d.ts +7 -2
  79. package/dist/cli/handlers/query.d.ts.map +1 -1
  80. package/dist/cli/handlers/query.js +16 -4
  81. package/dist/cli/handlers/release.d.ts.map +1 -1
  82. package/dist/cli/handlers/release.js +6 -3
  83. package/dist/cli/handlers/search.d.ts +2 -4
  84. package/dist/cli/handlers/search.d.ts.map +1 -1
  85. package/dist/cli/handlers/search.js +13 -39
  86. package/dist/cli/main.d.ts.map +1 -1
  87. package/dist/cli/main.js +87 -14
  88. package/dist/cli/parse-args.d.ts.map +1 -1
  89. package/dist/cli/parse-args.js +47 -2
  90. package/dist/cli/registry.d.ts +22 -4
  91. package/dist/cli/registry.d.ts.map +1 -1
  92. package/dist/components/builders.d.ts +5 -1
  93. package/dist/components/builders.d.ts.map +1 -1
  94. package/dist/components/builders.js +5 -0
  95. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  96. package/dist/components/capability-plugin-loader.js +16 -1
  97. package/dist/components/capability-plugin.d.ts +14 -0
  98. package/dist/components/capability-plugin.d.ts.map +1 -1
  99. package/dist/components/pr-hooks.d.ts +21 -1
  100. package/dist/components/pr-hooks.d.ts.map +1 -1
  101. package/dist/components/pr-hooks.js +72 -4
  102. package/dist/components/pr-pipeline.d.ts +45 -10
  103. package/dist/components/pr-pipeline.d.ts.map +1 -1
  104. package/dist/components/pr-pipeline.js +47 -14
  105. package/dist/components/pr-policy.d.ts +36 -0
  106. package/dist/components/pr-policy.d.ts.map +1 -1
  107. package/dist/components/pr-policy.js +36 -0
  108. package/dist/components/pr-run.d.ts +3 -0
  109. package/dist/components/pr-run.d.ts.map +1 -1
  110. package/dist/components/pr-run.js +1 -0
  111. package/dist/components/starter-plugin.d.ts +1 -0
  112. package/dist/components/starter-plugin.d.ts.map +1 -1
  113. package/dist/components/starter-plugin.js +5 -1
  114. package/dist/components/verbs/index.d.ts +2 -0
  115. package/dist/components/verbs/index.d.ts.map +1 -1
  116. package/dist/components/verbs/index.js +2 -0
  117. package/dist/components/verbs/publish-artifact.d.ts +65 -0
  118. package/dist/components/verbs/publish-artifact.d.ts.map +1 -0
  119. package/dist/components/verbs/publish-artifact.js +74 -0
  120. package/dist/components/verbs/publish-image.d.ts +129 -0
  121. package/dist/components/verbs/publish-image.d.ts.map +1 -0
  122. package/dist/components/verbs/publish-image.js +145 -0
  123. package/dist/config-environments.d.ts +20 -0
  124. package/dist/config-environments.d.ts.map +1 -1
  125. package/dist/config-environments.js +12 -0
  126. package/dist/config.d.ts +25 -1
  127. package/dist/config.d.ts.map +1 -1
  128. package/dist/config.js +10 -2
  129. package/dist/gate-decision.d.ts +20 -2
  130. package/dist/gate-decision.d.ts.map +1 -1
  131. package/dist/gate-decision.js +30 -3
  132. package/dist/graph-ir.d.ts +25 -0
  133. package/dist/graph-ir.d.ts.map +1 -1
  134. package/dist/graph-walk.d.ts +66 -0
  135. package/dist/graph-walk.d.ts.map +1 -0
  136. package/dist/graph-walk.js +70 -0
  137. package/dist/import/parser.d.ts +19 -0
  138. package/dist/import/parser.d.ts.map +1 -1
  139. package/dist/lexicon.d.ts +94 -6
  140. package/dist/lexicon.d.ts.map +1 -1
  141. package/dist/lifecycle/gate-ledger.d.ts +11 -0
  142. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  143. package/dist/lifecycle/git.d.ts +2 -5
  144. package/dist/lifecycle/git.d.ts.map +1 -1
  145. package/dist/lifecycle/ledger-read.d.ts +65 -0
  146. package/dist/lifecycle/ledger-read.d.ts.map +1 -0
  147. package/dist/lifecycle/ledger-read.js +160 -0
  148. package/dist/lifecycle/release-workstation.d.ts +7 -0
  149. package/dist/lifecycle/release-workstation.d.ts.map +1 -1
  150. package/dist/lifecycle/release-workstation.js +25 -0
  151. package/dist/lifecycle/run-ledger.d.ts +4 -3
  152. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  153. package/dist/lifecycle/run-ledger.js +1 -0
  154. package/dist/lint/discover.js +3 -1
  155. package/dist/lint/post-synth.d.ts +127 -1
  156. package/dist/lint/post-synth.d.ts.map +1 -1
  157. package/dist/lint/post-synth.js +84 -2
  158. package/dist/lint/rules/op/index.d.ts +1 -0
  159. package/dist/lint/rules/op/index.d.ts.map +1 -1
  160. package/dist/lint/rules/op/index.js +2 -0
  161. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts +32 -0
  162. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts.map +1 -0
  163. package/dist/lint/rules/op/ops019-workspace-op-target.js +63 -0
  164. package/dist/op/activities/activity-contracts.d.ts +9 -6
  165. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  166. package/dist/op/activities/activity-contracts.js +5 -2
  167. package/dist/op/activities/index.d.ts +2 -2
  168. package/dist/op/activities/index.d.ts.map +1 -1
  169. package/dist/op/activities/index.js +1 -1
  170. package/dist/op/activities/review-agent.d.ts +90 -4
  171. package/dist/op/activities/review-agent.d.ts.map +1 -1
  172. package/dist/op/activities/review-agent.js +217 -19
  173. package/dist/op/activities/work-evidence.d.ts +8 -1
  174. package/dist/op/activities/work-evidence.d.ts.map +1 -1
  175. package/dist/op/activities/work-evidence.js +1 -0
  176. package/dist/op/builders.d.ts +9 -5
  177. package/dist/op/builders.d.ts.map +1 -1
  178. package/dist/op/builders.js +9 -5
  179. package/dist/op/composites/apply-op.d.ts +29 -2
  180. package/dist/op/composites/apply-op.d.ts.map +1 -1
  181. package/dist/op/composites/apply-op.js +20 -1
  182. package/dist/op/gate-approval-base.d.ts +25 -0
  183. package/dist/op/gate-approval-base.d.ts.map +1 -0
  184. package/dist/op/gate-approval-base.js +71 -0
  185. package/dist/op/gate-approval.d.ts +18 -9
  186. package/dist/op/gate-approval.d.ts.map +1 -1
  187. package/dist/op/gate-approval.js +13 -31
  188. package/dist/op/gate-name.d.ts +13 -0
  189. package/dist/op/gate-name.d.ts.map +1 -1
  190. package/dist/op/gate-name.js +14 -1
  191. package/dist/op/gate-quorum.d.ts +100 -3
  192. package/dist/op/gate-quorum.d.ts.map +1 -1
  193. package/dist/op/gate-quorum.js +108 -6
  194. package/dist/op/gate-review.d.ts +9 -0
  195. package/dist/op/gate-review.d.ts.map +1 -1
  196. package/dist/op/gate-review.js +13 -0
  197. package/dist/op/gate.d.ts +47 -5
  198. package/dist/op/gate.d.ts.map +1 -1
  199. package/dist/op/gate.js +25 -4
  200. package/dist/op/index.d.ts +3 -3
  201. package/dist/op/index.d.ts.map +1 -1
  202. package/dist/op/index.js +1 -1
  203. package/dist/op/local-executor.d.ts +45 -3
  204. package/dist/op/local-executor.d.ts.map +1 -1
  205. package/dist/op/local-executor.js +68 -7
  206. package/dist/op/op-waves-run.d.ts +26 -1
  207. package/dist/op/op-waves-run.d.ts.map +1 -1
  208. package/dist/op/op-waves-run.js +259 -13
  209. package/dist/op/op-waves.d.ts +72 -5
  210. package/dist/op/op-waves.d.ts.map +1 -1
  211. package/dist/op/op-waves.js +110 -15
  212. package/dist/op/runtime.d.ts +11 -0
  213. package/dist/op/runtime.d.ts.map +1 -1
  214. package/dist/op/schedule-overdue.d.ts +16 -0
  215. package/dist/op/schedule-overdue.d.ts.map +1 -1
  216. package/dist/op/schedule-overdue.js +36 -0
  217. package/dist/op/stage-hooks.d.ts +9 -2
  218. package/dist/op/stage-hooks.d.ts.map +1 -1
  219. package/dist/op/stage-hooks.js +7 -5
  220. package/dist/op/workspace-run-env.d.ts +22 -0
  221. package/dist/op/workspace-run-env.d.ts.map +1 -0
  222. package/dist/op/workspace-run-env.js +21 -0
  223. package/dist/pr-forge.d.ts +24 -0
  224. package/dist/pr-forge.d.ts.map +1 -1
  225. package/dist/pr-forge.js +47 -0
  226. package/dist/pr-loop.d.ts +10 -0
  227. package/dist/pr-loop.d.ts.map +1 -1
  228. package/dist/pr-loop.js +4 -0
  229. package/dist/pr-migrations.d.ts +62 -0
  230. package/dist/pr-migrations.d.ts.map +1 -0
  231. package/dist/pr-migrations.js +50 -0
  232. package/dist/pr-post.d.ts +42 -0
  233. package/dist/pr-post.d.ts.map +1 -1
  234. package/dist/pr-post.js +68 -2
  235. package/dist/read-filter.d.ts +65 -0
  236. package/dist/read-filter.d.ts.map +1 -0
  237. package/dist/read-filter.js +179 -0
  238. package/dist/run-event.d.ts +1 -1
  239. package/dist/run-event.d.ts.map +1 -1
  240. package/dist/terraform/carve.d.ts +24 -3
  241. package/dist/terraform/carve.d.ts.map +1 -1
  242. package/dist/terraform/carve.js +32 -14
  243. package/dist/terraform/cuts.d.ts +97 -0
  244. package/dist/terraform/cuts.d.ts.map +1 -0
  245. package/dist/terraform/cuts.js +138 -0
  246. package/dist/terraform/folds.d.ts +26 -0
  247. package/dist/terraform/folds.d.ts.map +1 -0
  248. package/dist/terraform/folds.js +38 -0
  249. package/dist/terraform/graph.d.ts.map +1 -1
  250. package/dist/terraform/graph.js +10 -0
  251. package/dist/terraform/reach.d.ts +140 -0
  252. package/dist/terraform/reach.d.ts.map +1 -0
  253. package/dist/terraform/reach.js +170 -0
  254. package/dist/terraform/score.d.ts +14 -0
  255. package/dist/terraform/score.d.ts.map +1 -1
  256. package/dist/terraform/score.js +14 -31
  257. package/dist/terraform/state.d.ts.map +1 -1
  258. package/dist/terraform/state.js +4 -2
  259. package/dist/terraform/types.d.ts +8 -0
  260. package/dist/terraform/types.d.ts.map +1 -1
  261. package/dist/wave-blocked.d.ts +10 -1
  262. package/dist/wave-blocked.d.ts.map +1 -1
  263. package/dist/wave-blocked.js +31 -3
  264. package/dist/wave-runtime.mjs +264 -254
  265. package/dist/workspace/check.schema.json +13 -2
  266. package/dist/workspace/checks/ci-branch.d.ts +44 -0
  267. package/dist/workspace/checks/ci-branch.d.ts.map +1 -0
  268. package/dist/workspace/checks/ci-branch.js +133 -0
  269. package/dist/workspace/checks/gate-approval.d.ts +58 -0
  270. package/dist/workspace/checks/gate-approval.d.ts.map +1 -0
  271. package/dist/workspace/checks/gate-approval.js +103 -0
  272. package/dist/workspace/checks/links.d.ts +3 -0
  273. package/dist/workspace/checks/links.d.ts.map +1 -1
  274. package/dist/workspace/checks/links.js +12 -1
  275. package/dist/workspace/checks/org.d.ts +48 -0
  276. package/dist/workspace/checks/org.d.ts.map +1 -0
  277. package/dist/workspace/checks/org.js +156 -0
  278. package/dist/workspace/checks.d.ts +12 -1
  279. package/dist/workspace/checks.d.ts.map +1 -1
  280. package/dist/workspace/checks.js +63 -7
  281. package/dist/workspace/ci-branches.d.ts +16 -0
  282. package/dist/workspace/ci-branches.d.ts.map +1 -1
  283. package/dist/workspace/ci-branches.js +27 -0
  284. package/dist/workspace/ci-green-cli.js +7 -7
  285. package/dist/workspace/ci-green-workflow.d.ts +1 -1
  286. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  287. package/dist/workspace/ci-green-workflow.js +13 -8
  288. package/dist/workspace/ci-green.d.ts +14 -0
  289. package/dist/workspace/ci-green.d.ts.map +1 -1
  290. package/dist/workspace/ci-green.js +44 -11
  291. package/dist/workspace/ci-run.d.ts +9 -1
  292. package/dist/workspace/ci-run.d.ts.map +1 -1
  293. package/dist/workspace/ci-run.js +67 -6
  294. package/dist/workspace/ci-serve.d.ts +9 -0
  295. package/dist/workspace/ci-serve.d.ts.map +1 -1
  296. package/dist/workspace/ci-serve.js +114 -76
  297. package/dist/workspace/compose-graph.d.ts +14 -0
  298. package/dist/workspace/compose-graph.d.ts.map +1 -1
  299. package/dist/workspace/composites.d.ts +4 -1
  300. package/dist/workspace/composites.d.ts.map +1 -1
  301. package/dist/workspace/composites.js +5 -2
  302. package/dist/workspace/composites.schema.json +22 -0
  303. package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  304. package/dist/workspace/cross-root-lint.d.ts +56 -0
  305. package/dist/workspace/cross-root-lint.d.ts.map +1 -0
  306. package/dist/workspace/cross-root-lint.js +111 -0
  307. package/dist/workspace/dbt-models-reader.d.ts +44 -0
  308. package/dist/workspace/dbt-models-reader.d.ts.map +1 -0
  309. package/dist/workspace/dbt-models-reader.js +356 -0
  310. package/dist/workspace/dbt-project-common.d.ts +40 -0
  311. package/dist/workspace/dbt-project-common.d.ts.map +1 -0
  312. package/dist/workspace/dbt-project-common.js +106 -0
  313. package/dist/workspace/dbt-project-reader.d.ts +4 -3
  314. package/dist/workspace/dbt-project-reader.d.ts.map +1 -1
  315. package/dist/workspace/dbt-project-reader.js +14 -108
  316. package/dist/workspace/decision-rank.d.ts +26 -0
  317. package/dist/workspace/decision-rank.d.ts.map +1 -0
  318. package/dist/workspace/decision-rank.js +16 -0
  319. package/dist/workspace/declaration.d.ts +50 -2
  320. package/dist/workspace/declaration.d.ts.map +1 -1
  321. package/dist/workspace/declaration.js +57 -11
  322. package/dist/workspace/declaration.schema.json +166 -24
  323. package/dist/workspace/environments.d.ts +26 -2
  324. package/dist/workspace/environments.d.ts.map +1 -1
  325. package/dist/workspace/environments.js +26 -2
  326. package/dist/workspace/exit-cli.d.ts +44 -0
  327. package/dist/workspace/exit-cli.d.ts.map +1 -0
  328. package/dist/workspace/exit-cli.js +198 -0
  329. package/dist/workspace/exit.d.ts +159 -0
  330. package/dist/workspace/exit.d.ts.map +1 -0
  331. package/dist/workspace/exit.js +480 -0
  332. package/dist/workspace/exit.schema.json +257 -0
  333. package/dist/workspace/flyway-dir.d.ts +39 -0
  334. package/dist/workspace/flyway-dir.d.ts.map +1 -0
  335. package/dist/workspace/flyway-dir.js +104 -0
  336. package/dist/workspace/flyway-layout.d.ts +96 -0
  337. package/dist/workspace/flyway-layout.d.ts.map +1 -0
  338. package/dist/workspace/flyway-layout.js +308 -0
  339. package/dist/workspace/graph-cache.d.ts +3 -1
  340. package/dist/workspace/graph-cache.d.ts.map +1 -1
  341. package/dist/workspace/graph-cache.js +12 -1
  342. package/dist/workspace/graph-cli.d.ts.map +1 -1
  343. package/dist/workspace/graph-cli.js +26 -8
  344. package/dist/workspace/graph.schema.json +34 -0
  345. package/dist/workspace/import-fetch.d.ts +38 -0
  346. package/dist/workspace/import-fetch.d.ts.map +1 -0
  347. package/dist/workspace/import-fetch.js +99 -0
  348. package/dist/workspace/import-init.d.ts +26 -0
  349. package/dist/workspace/import-init.d.ts.map +1 -0
  350. package/dist/workspace/import-init.js +130 -0
  351. package/dist/workspace/import-source.d.ts +147 -0
  352. package/dist/workspace/import-source.d.ts.map +1 -0
  353. package/dist/workspace/import-source.js +370 -0
  354. package/dist/workspace/init.d.ts +4 -0
  355. package/dist/workspace/init.d.ts.map +1 -1
  356. package/dist/workspace/init.js +70 -3
  357. package/dist/workspace/intent.d.ts +2 -3
  358. package/dist/workspace/intent.d.ts.map +1 -1
  359. package/dist/workspace/intent.js +5 -9
  360. package/dist/workspace/kind-link-readers.d.ts +59 -2
  361. package/dist/workspace/kind-link-readers.d.ts.map +1 -1
  362. package/dist/workspace/kind-link-readers.js +17 -1
  363. package/dist/workspace/kind-readers.d.ts +6 -0
  364. package/dist/workspace/kind-readers.d.ts.map +1 -1
  365. package/dist/workspace/kind-readers.js +18 -0
  366. package/dist/workspace/kinds.d.ts +90 -3
  367. package/dist/workspace/kinds.d.ts.map +1 -1
  368. package/dist/workspace/kinds.js +161 -7
  369. package/dist/workspace/ledger.schema.json +158 -0
  370. package/dist/workspace/lexicon-outputs.d.ts +20 -1
  371. package/dist/workspace/lexicon-outputs.d.ts.map +1 -1
  372. package/dist/workspace/lexicon-outputs.js +102 -10
  373. package/dist/workspace/lineage-check.d.ts +3 -0
  374. package/dist/workspace/lineage-check.d.ts.map +1 -1
  375. package/dist/workspace/lineage-check.js +23 -4
  376. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  377. package/dist/workspace/lineage-upgrade.js +13 -4
  378. package/dist/workspace/links.d.ts +8 -1
  379. package/dist/workspace/links.d.ts.map +1 -1
  380. package/dist/workspace/links.js +25 -1
  381. package/dist/workspace/ls.d.ts +8 -0
  382. package/dist/workspace/ls.d.ts.map +1 -1
  383. package/dist/workspace/ls.js +7 -1
  384. package/dist/workspace/ls.schema.json +7 -0
  385. package/dist/workspace/member-commands.d.ts.map +1 -1
  386. package/dist/workspace/member-commands.js +54 -5
  387. package/dist/workspace/migrate-links.d.ts +45 -0
  388. package/dist/workspace/migrate-links.d.ts.map +1 -0
  389. package/dist/workspace/migrate-links.js +100 -0
  390. package/dist/workspace/migration-dirs.d.ts +74 -0
  391. package/dist/workspace/migration-dirs.d.ts.map +1 -0
  392. package/dist/workspace/migration-dirs.js +254 -0
  393. package/dist/workspace/new-migrations.d.ts +47 -0
  394. package/dist/workspace/new-migrations.d.ts.map +1 -0
  395. package/dist/workspace/new-migrations.js +91 -0
  396. package/dist/workspace/pr-report.schema.json +46 -0
  397. package/dist/workspace/promote.d.ts +96 -0
  398. package/dist/workspace/promote.d.ts.map +1 -0
  399. package/dist/workspace/promote.js +263 -0
  400. package/dist/workspace/reason-codes.d.ts +21 -0
  401. package/dist/workspace/reason-codes.d.ts.map +1 -1
  402. package/dist/workspace/reason-codes.js +25 -0
  403. package/dist/workspace/records-cli.d.ts +11 -0
  404. package/dist/workspace/records-cli.d.ts.map +1 -1
  405. package/dist/workspace/records-cli.js +57 -5
  406. package/dist/workspace/records.schema.json +5 -0
  407. package/dist/workspace/release-candidates.d.ts +2 -0
  408. package/dist/workspace/release-candidates.d.ts.map +1 -1
  409. package/dist/workspace/release-candidates.js +5 -2
  410. package/dist/workspace/release-train.d.ts +53 -0
  411. package/dist/workspace/release-train.d.ts.map +1 -0
  412. package/dist/workspace/release-train.js +134 -0
  413. package/dist/workspace/release-workflow.d.ts.map +1 -1
  414. package/dist/workspace/release-workflow.js +4 -3
  415. package/dist/workspace/repo-ref.d.ts +45 -0
  416. package/dist/workspace/repo-ref.d.ts.map +1 -0
  417. package/dist/workspace/repo-ref.js +114 -0
  418. package/dist/workspace/run-event.schema.json +2 -2
  419. package/dist/workspace/runs-cli.d.ts +9 -0
  420. package/dist/workspace/runs-cli.d.ts.map +1 -1
  421. package/dist/workspace/runs-cli.js +48 -3
  422. package/dist/workspace/runs.schema.json +7 -0
  423. package/dist/workspace/search-contract.d.ts +1 -1
  424. package/dist/workspace/search-contract.d.ts.map +1 -1
  425. package/dist/workspace/status-decisions.d.ts +37 -0
  426. package/dist/workspace/status-decisions.d.ts.map +1 -0
  427. package/dist/workspace/status-decisions.js +147 -0
  428. package/dist/workspace/status-gates.d.ts +9 -0
  429. package/dist/workspace/status-gates.d.ts.map +1 -1
  430. package/dist/workspace/status.d.ts +4 -0
  431. package/dist/workspace/status.d.ts.map +1 -1
  432. package/dist/workspace/status.js +14 -2
  433. package/dist/workspace/status.schema.json +6 -0
  434. package/dist/workspace/work-cli.d.ts.map +1 -1
  435. package/dist/workspace/work-cli.js +5 -3
  436. package/dist/workspace/work-evidence-ops.d.ts +83 -0
  437. package/dist/workspace/work-evidence-ops.d.ts.map +1 -0
  438. package/dist/workspace/work-evidence-ops.js +169 -0
  439. package/dist/workspace/work-evidence.d.ts +12 -2
  440. package/dist/workspace/work-evidence.d.ts.map +1 -1
  441. package/dist/workspace/work-evidence.js +21 -3
  442. package/dist/workspace/work-evidence.schema.json +49 -2
  443. package/dist/workspace/workspace-gates.d.ts +110 -0
  444. package/dist/workspace/workspace-gates.d.ts.map +1 -0
  445. package/dist/workspace/workspace-gates.js +140 -0
  446. package/dist/workspace/workspace-kinds.schema.json +108 -10
  447. package/dist/workspace/workspace-ledger.d.ts +76 -0
  448. package/dist/workspace/workspace-ledger.d.ts.map +1 -0
  449. package/dist/workspace/workspace-ledger.js +91 -0
  450. package/dist/workspace/workspace-run.d.ts +112 -0
  451. package/dist/workspace/workspace-run.d.ts.map +1 -0
  452. package/dist/workspace/workspace-run.js +432 -0
  453. package/package.json +1 -1
  454. package/src/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  455. package/src/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  456. package/src/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  457. package/src/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  458. package/src/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  459. package/src/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  460. package/src/__golden__/wave-loop/op-cost-member.json +13 -0
  461. package/src/__golden__/wave-loop/op-denied-source.json +16 -0
  462. package/src/__golden__/wave-loop/op-denied.json +16 -0
  463. package/src/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  464. package/src/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  465. package/src/__golden__/wave-loop/op-discover-rest.json +26 -0
  466. package/src/__golden__/wave-loop/op-discover-wide.json +26 -0
  467. package/src/__golden__/wave-loop/op-held-on-again.json +16 -0
  468. package/src/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  469. package/src/__golden__/wave-loop/op-held-on.json +16 -0
  470. package/src/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  471. package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  472. package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  473. package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  474. package/src/__golden__/wave-loop/op-hooks-held.json +13 -0
  475. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  476. package/src/__golden__/wave-loop/op-hooks-pass.json +13 -0
  477. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  478. package/src/__golden__/wave-loop/op-hooks-share.json +33 -0
  479. package/src/__golden__/wave-loop/op-outcome-held.json +10 -0
  480. package/src/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  481. package/src/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  482. package/src/__golden__/wave-loop/op-outcome-replan.json +10 -0
  483. package/src/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  484. package/src/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  485. package/src/__golden__/wave-loop/op-overridden.json +16 -0
  486. package/src/__golden__/wave-loop/op-per-run.json +20 -0
  487. package/src/__golden__/wave-loop/op-point.json +13 -0
  488. package/src/__golden__/wave-loop/op-pr-review.json +10 -0
  489. package/src/__golden__/wave-loop/op-sealed.json +10 -0
  490. package/src/__golden__/wave-loop/op-spend-change-only.json +10 -0
  491. package/src/__golden__/wave-loop/op-spend.json +10 -0
  492. package/src/__golden__/wave-loop/op-wave-commands.json +55 -0
  493. package/src/__golden__/wave-loop/op-wide-refused.json +26 -0
  494. package/src/__golden__/wave-loop/op-wide-waiting.json +29 -0
  495. package/src/__golden__/wave-loop/op-wide.json +39 -0
  496. package/src/action-pins.test.ts +78 -0
  497. package/src/action-pins.ts +92 -0
  498. package/src/cli/commands/carve-score-once.test.ts +99 -0
  499. package/src/cli/commands/carve.ts +37 -6
  500. package/src/cli/commands/check-lexicon.test.ts +2 -2
  501. package/src/cli/commands/import-report.ts +71 -0
  502. package/src/cli/commands/import.ts +33 -3
  503. package/src/cli/commands/migrate-dialect.test.ts +145 -0
  504. package/src/cli/commands/migrate.ts +132 -11
  505. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +4 -4
  506. package/src/cli/handlers/approve.ts +33 -4
  507. package/src/cli/handlers/build.ts +7 -1
  508. package/src/cli/handlers/graph-reader.test.ts +31 -0
  509. package/src/cli/handlers/graph-reader.ts +51 -0
  510. package/src/cli/handlers/graph.ts +14 -0
  511. package/src/cli/handlers/ledger.ts +54 -0
  512. package/src/cli/handlers/migrate.ts +21 -12
  513. package/src/cli/handlers/misc.ts +10 -1
  514. package/src/cli/handlers/pr-env.ts +2 -2
  515. package/src/cli/handlers/pr.ts +176 -37
  516. package/src/cli/handlers/query.ts +16 -4
  517. package/src/cli/handlers/release.ts +6 -3
  518. package/src/cli/handlers/search.ts +13 -34
  519. package/src/cli/main.test.ts +17 -0
  520. package/src/cli/main.ts +87 -14
  521. package/src/cli/parse-args.ts +35 -2
  522. package/src/cli/registry.ts +22 -4
  523. package/src/components/builders.ts +8 -0
  524. package/src/components/capability-plugin-loader.ts +13 -1
  525. package/src/components/capability-plugin.ts +16 -0
  526. package/src/components/pr-env-run-bundle.test.ts +1 -0
  527. package/src/components/pr-hooks.test.ts +73 -5
  528. package/src/components/pr-hooks.ts +95 -5
  529. package/src/components/pr-pipeline.ts +64 -16
  530. package/src/components/pr-policy.test.ts +64 -1
  531. package/src/components/pr-policy.ts +53 -0
  532. package/src/components/pr-run.ts +4 -0
  533. package/src/components/registry.test.ts +1 -1
  534. package/src/components/starter-plugin.ts +6 -0
  535. package/src/components/verbs/index.ts +2 -0
  536. package/src/components/verbs/publish-artifact.test.ts +60 -0
  537. package/src/components/verbs/publish-artifact.ts +122 -0
  538. package/src/components/verbs/publish-image.test.ts +101 -0
  539. package/src/components/verbs/publish-image.ts +265 -0
  540. package/src/config-environments.ts +28 -1
  541. package/src/config.ts +24 -1
  542. package/src/gate-decision-bundle.test.ts +5 -2
  543. package/src/gate-decision-identity.test.ts +20 -0
  544. package/src/gate-decision.ts +46 -3
  545. package/src/graph-ir.ts +27 -0
  546. package/src/graph-walk.test.ts +44 -0
  547. package/src/graph-walk.ts +102 -0
  548. package/src/import/parser.ts +20 -0
  549. package/src/lexicon.ts +98 -6
  550. package/src/lifecycle/gate-ledger.ts +8 -0
  551. package/src/lifecycle/git.ts +2 -2
  552. package/src/lifecycle/ledger-read.test.ts +107 -0
  553. package/src/lifecycle/ledger-read.ts +195 -0
  554. package/src/lifecycle/release-workstation.ts +24 -0
  555. package/src/lifecycle/run-ledger.ts +5 -2
  556. package/src/lint/cross-root.test.ts +122 -0
  557. package/src/lint/discover.ts +3 -1
  558. package/src/lint/post-synth.ts +212 -2
  559. package/src/lint/rules/op/index.ts +2 -0
  560. package/src/lint/rules/op/ops019-workspace-op-target.ts +89 -0
  561. package/src/op/activities/activity-contracts.ts +5 -2
  562. package/src/op/activities/index.ts +2 -2
  563. package/src/op/activities/review-agent.test.ts +221 -4
  564. package/src/op/activities/review-agent.ts +265 -22
  565. package/src/op/activities/work-evidence.ts +9 -1
  566. package/src/op/builders.ts +9 -5
  567. package/src/op/composites/apply-op.test.ts +108 -0
  568. package/src/op/composites/apply-op.ts +37 -2
  569. package/src/op/gate-approval-at-base.test.ts +97 -0
  570. package/src/op/gate-approval-base.ts +72 -0
  571. package/src/op/gate-approval.test.ts +6 -1
  572. package/src/op/gate-approval.ts +22 -27
  573. package/src/op/gate-name.ts +16 -1
  574. package/src/op/gate-quorum-at-base.test.ts +211 -0
  575. package/src/op/gate-quorum.ts +156 -8
  576. package/src/op/gate-review.ts +14 -0
  577. package/src/op/gate.ts +69 -9
  578. package/src/op/index.ts +3 -2
  579. package/src/op/local-executor.ts +91 -11
  580. package/src/op/op-waves-depends-on.test.ts +81 -0
  581. package/src/op/op-waves-hook-targets.test.ts +4 -2
  582. package/src/op/op-waves-quorum-product.test.ts +158 -0
  583. package/src/op/op-waves-run.ts +288 -14
  584. package/src/op/op-waves.ts +143 -14
  585. package/src/op/runtime.ts +8 -0
  586. package/src/op/schedule-overdue.ts +33 -0
  587. package/src/op/stage-hooks.ts +13 -5
  588. package/src/op/workspace-run-env.ts +29 -0
  589. package/src/pr-forge-bundle.test.ts +2 -2
  590. package/src/pr-forge.test.ts +47 -0
  591. package/src/pr-forge.ts +68 -0
  592. package/src/pr-loop-bundle.test.ts +3 -2
  593. package/src/pr-loop.test.ts +52 -0
  594. package/src/pr-loop.ts +12 -0
  595. package/src/pr-migrations.ts +97 -0
  596. package/src/pr-post-open.e2e.test.ts +36 -0
  597. package/src/pr-post.test.ts +102 -3
  598. package/src/pr-post.ts +90 -2
  599. package/src/read-filter.test.ts +54 -0
  600. package/src/read-filter.ts +193 -0
  601. package/src/run-event.ts +1 -1
  602. package/src/stage-hooks-bundle.test.ts +1 -0
  603. package/src/terraform/carve.ts +52 -15
  604. package/src/terraform/cuts.test.ts +173 -0
  605. package/src/terraform/cuts.ts +188 -0
  606. package/src/terraform/folds.ts +44 -0
  607. package/src/terraform/graph.ts +9 -0
  608. package/src/terraform/radius.test.ts +90 -0
  609. package/src/terraform/reach.test.ts +146 -0
  610. package/src/terraform/reach.ts +247 -0
  611. package/src/terraform/score.ts +27 -29
  612. package/src/terraform/state.ts +3 -1
  613. package/src/terraform/types.ts +8 -0
  614. package/src/wave-blocked.ts +28 -3
  615. package/src/wave-loop-golden.test.ts +4 -3
  616. package/src/workspace/__fixtures__/import/spacelift.json +9 -0
  617. package/src/workspace/__fixtures__/import/tfc.json +13 -0
  618. package/src/workspace/behold-kinds.test.ts +2 -1
  619. package/src/workspace/check-contract.test.ts +8 -6
  620. package/src/workspace/check.schema.json +13 -2
  621. package/src/workspace/checks/ci-branch.test.ts +128 -0
  622. package/src/workspace/checks/ci-branch.ts +141 -0
  623. package/src/workspace/checks/gate-approval.test.ts +77 -0
  624. package/src/workspace/checks/gate-approval.ts +121 -0
  625. package/src/workspace/checks/links.ts +12 -1
  626. package/src/workspace/checks/org.ts +169 -0
  627. package/src/workspace/checks.ts +68 -7
  628. package/src/workspace/ci-branches.ts +31 -0
  629. package/src/workspace/ci-green-branches.test.ts +153 -0
  630. package/src/workspace/ci-green-cli.ts +7 -7
  631. package/src/workspace/ci-green-workflow.test.ts +4 -3
  632. package/src/workspace/ci-green-workflow.ts +13 -9
  633. package/src/workspace/ci-green.ts +49 -12
  634. package/src/workspace/ci-run.test.ts +72 -1
  635. package/src/workspace/ci-run.ts +68 -8
  636. package/src/workspace/ci-serve.test.ts +95 -0
  637. package/src/workspace/ci-serve.ts +106 -65
  638. package/src/workspace/component-environments.test.ts +25 -2
  639. package/src/workspace/compose-graph.ts +9 -2
  640. package/src/workspace/composites.schema.json +22 -0
  641. package/src/workspace/composites.test.ts +39 -8
  642. package/src/workspace/composites.ts +6 -2
  643. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  644. package/src/workspace/cross-root-lint.test.ts +90 -0
  645. package/src/workspace/cross-root-lint.ts +126 -0
  646. package/src/workspace/dbt-kind.test.ts +3 -2
  647. package/src/workspace/dbt-models-reader.ts +362 -0
  648. package/src/workspace/dbt-models.test.ts +248 -0
  649. package/src/workspace/dbt-project-common.ts +113 -0
  650. package/src/workspace/dbt-project-reader.ts +29 -112
  651. package/src/workspace/decision-rank.ts +40 -0
  652. package/src/workspace/declaration.schema.json +166 -24
  653. package/src/workspace/declaration.ts +101 -15
  654. package/src/workspace/environments.ts +40 -4
  655. package/src/workspace/exit-cli.ts +219 -0
  656. package/src/workspace/exit.schema.json +257 -0
  657. package/src/workspace/exit.test.ts +185 -0
  658. package/src/workspace/exit.ts +560 -0
  659. package/src/workspace/flyway-dir.ts +113 -0
  660. package/src/workspace/flyway-layout.ts +337 -0
  661. package/src/workspace/graph-cache.ts +12 -2
  662. package/src/workspace/graph-cli.ts +25 -10
  663. package/src/workspace/graph-contract.test.ts +4 -2
  664. package/src/workspace/graph.schema.json +34 -0
  665. package/src/workspace/identity.test.ts +29 -2
  666. package/src/workspace/import-fetch.ts +151 -0
  667. package/src/workspace/import-init.ts +147 -0
  668. package/src/workspace/import-source.test.ts +183 -0
  669. package/src/workspace/import-source.ts +462 -0
  670. package/src/workspace/init.test.ts +55 -1
  671. package/src/workspace/init.ts +66 -4
  672. package/src/workspace/intent.ts +6 -14
  673. package/src/workspace/kind-link-readers.ts +68 -3
  674. package/src/workspace/kind-readers.test.ts +36 -0
  675. package/src/workspace/kind-readers.ts +18 -1
  676. package/src/workspace/kinds.test.ts +35 -7
  677. package/src/workspace/kinds.ts +212 -11
  678. package/src/workspace/ledger.schema.json +158 -0
  679. package/src/workspace/lexicon-outputs.ts +132 -13
  680. package/src/workspace/lineage-check.ts +30 -7
  681. package/src/workspace/lineage-upgrade.test.ts +20 -0
  682. package/src/workspace/lineage-upgrade.ts +16 -7
  683. package/src/workspace/links.ts +24 -1
  684. package/src/workspace/ls-contract.test.ts +1 -1
  685. package/src/workspace/ls.schema.json +7 -0
  686. package/src/workspace/ls.ts +10 -1
  687. package/src/workspace/member-commands.ts +60 -2
  688. package/src/workspace/member-file-schema.test.ts +124 -0
  689. package/src/workspace/migrate-links.test.ts +99 -0
  690. package/src/workspace/migrate-links.ts +108 -0
  691. package/src/workspace/migration-dirs.test.ts +272 -0
  692. package/src/workspace/migration-dirs.ts +267 -0
  693. package/src/workspace/new-migrations.test.ts +98 -0
  694. package/src/workspace/new-migrations.ts +109 -0
  695. package/src/workspace/org-scope.test.ts +238 -0
  696. package/src/workspace/pr-report.schema.json +46 -0
  697. package/src/workspace/promote.test.ts +114 -0
  698. package/src/workspace/promote.ts +302 -0
  699. package/src/workspace/reason-codes.test.ts +10 -1
  700. package/src/workspace/reason-codes.ts +25 -0
  701. package/src/workspace/records-cli.ts +58 -5
  702. package/src/workspace/records.schema.json +5 -0
  703. package/src/workspace/release-candidates.ts +7 -2
  704. package/src/workspace/release-train.ts +157 -0
  705. package/src/workspace/release-workflow.ts +4 -3
  706. package/src/workspace/repo-ref.ts +113 -0
  707. package/src/workspace/run-event.schema.json +2 -2
  708. package/src/workspace/runs-cli.ts +48 -4
  709. package/src/workspace/runs.schema.json +7 -0
  710. package/src/workspace/status-contract.test.ts +46 -1
  711. package/src/workspace/status-decisions.test.ts +100 -0
  712. package/src/workspace/status-decisions.ts +151 -0
  713. package/src/workspace/status-gates.ts +9 -0
  714. package/src/workspace/status.schema.json +6 -0
  715. package/src/workspace/status.ts +17 -3
  716. package/src/workspace/where-filters.test.ts +39 -0
  717. package/src/workspace/work-cli.ts +5 -3
  718. package/src/workspace/work-evidence-ops.test.ts +176 -0
  719. package/src/workspace/work-evidence-ops.ts +189 -0
  720. package/src/workspace/work-evidence.schema.json +49 -2
  721. package/src/workspace/work-evidence.ts +22 -3
  722. package/src/workspace/workspace-gates.ts +200 -0
  723. package/src/workspace/workspace-kinds.schema.json +108 -10
  724. package/src/workspace/workspace-ledger.ts +130 -0
  725. package/src/workspace/workspace-run.test.ts +322 -0
  726. package/src/workspace/workspace-run.ts +461 -0
@@ -48,8 +48,9 @@
48
48
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
49
49
  import { dirname, join, relative, resolve, sep } from "node:path";
50
50
  import { GENERATED_MARKER } from "../discovery/files";
51
+ import { ACTION_PINS, tagComments, usesRef } from "../action-pins";
51
52
  import { emitYAMLEntry, parseYAMLDocument } from "../yaml";
52
- import type { CiBuild, CiGreen } from "./declaration";
53
+ import { greenBranches, type CiBuild, type CiGreen } from "./declaration";
53
54
  import { matchesCheckRun } from "./ci-green";
54
55
  import { githubSecretOrJobToken } from "../ci-jobs";
55
56
  import { COMPOSE_BRANCH_PREFIX } from "../lifecycle/compose-refs";
@@ -228,7 +229,7 @@ export function renderCiGreenWorkflow(input: WorkflowInput): string {
228
229
  ? [
229
230
  " - name: Mint a GitHub App token that may push tags to commits that change workflows",
230
231
  " id: app-token",
231
- " uses: actions/create-github-app-token@v2",
232
+ ` uses: ${usesRef(ACTION_PINS.createGithubAppToken2)}`,
232
233
  " with:",
233
234
  ` app-id: \${{ vars.${push.appIdVar} }}`,
234
235
  ` private-key: \${{ secrets.${push.appKeySecret} }}`,
@@ -247,7 +248,7 @@ export function renderCiGreenWorkflow(input: WorkflowInput): string {
247
248
  " types:",
248
249
  " - completed",
249
250
  " branches:",
250
- ` - ${q(green.branch)}`,
251
+ ...greenBranches(green).map((b) => ` - ${q(b)}`),
251
252
  // #4169: a build the tick asked for runs at a ci/build/<sha> tag, and its completion ticks too.
252
253
  ...(newestPhases(green) ? [` - ${q("ci/build/**")}`] : []),
253
254
  // #4220: a composed commit's phases run on its chant/compose/<name> branch, and their completion ticks too.
@@ -273,12 +274,12 @@ export function renderCiGreenWorkflow(input: WorkflowInput): string {
273
274
  " timeout-minutes: 15",
274
275
  " steps:",
275
276
  ...mint,
276
- " - uses: actions/checkout@v6",
277
+ ` - uses: ${usesRef(ACTION_PINS.checkout6)}`,
277
278
  " with:",
278
279
  ` ref: ${q(green.branch)}`,
279
280
  " fetch-depth: 0",
280
281
  ...token,
281
- " - uses: actions/setup-node@v6",
282
+ ` - uses: ${usesRef(ACTION_PINS.setupNode6)}`,
282
283
  " with:",
283
284
  " node-version: \"24\"",
284
285
  ...cache,
@@ -292,7 +293,7 @@ export function renderCiGreenWorkflow(input: WorkflowInput): string {
292
293
  ...(input.runnerKeySecret ? [` CHANT_RUNNER_KEY: \${{ secrets.${input.runnerKeySecret} }}`] : []),
293
294
  ` run: ${tick}`,
294
295
  ];
295
- return lines.join("\n") + "\n";
296
+ return tagComments(lines.join("\n") + "\n");
296
297
  }
297
298
 
298
299
  /** Whether any phase sets `build` (#4169): then the tick may re-run jobs and start workflows. */
@@ -370,7 +371,7 @@ export function buildTriggerEdits(green: CiGreen, sources: readonly WorkflowSour
370
371
  delete on.schedule;
371
372
  const push = isObject(on.push) ? { ...on.push } : {};
372
373
  const branches = Array.isArray(push.branches) ? push.branches.filter((b): b is string => typeof b === "string") : [];
373
- const withBranch = branches.includes(green.branch) ? branches : [...branches, green.branch];
374
+ const withBranch = [...branches, ...greenBranches(green).filter((b) => !branches.includes(b))];
374
375
  push.branches = compose && !withBranch.includes(COMPOSE_BRANCHES) ? [...withBranch, COMPOSE_BRANCHES] : withBranch;
375
376
  const { push: _push, workflow_dispatch: dispatch, ...rest } = on;
376
377
  text = writeTopLevel(text, "on", { push, workflow_dispatch: dispatch ?? {}, ...rest });
@@ -388,8 +389,11 @@ export function buildTriggerEdits(green: CiGreen, sources: readonly WorkflowSour
388
389
  if (!("workflow_dispatch" in next)) next = { ...next, workflow_dispatch: {} };
389
390
  if (next !== on) text = writeTopLevel(text, "on", next);
390
391
  // #4220: a compose branch's commits each get a verdict, so its runs are keyed by commit, as the trunk branch's are.
391
- const keyed = compose ? `(github.ref == '${main}' || startsWith(github.ref, 'refs/heads/${COMPOSE_BRANCH_PREFIX}'))` : `github.ref == '${main}'`;
392
- const unkeyed = compose ? `!${keyed}` : `github.ref != '${main}'`;
392
+ // #4325: each branch ci.green judges is keyed by commit.
393
+ const heads = greenBranches(green).map((b) => `github.ref == 'refs/heads/${b}'`);
394
+ const several = heads.length > 1;
395
+ const keyed = compose ? `(${[...heads, `startsWith(github.ref, 'refs/heads/${COMPOSE_BRANCH_PREFIX}')`].join(" || ")})` : several ? `(${heads.join(" || ")})` : `github.ref == '${main}'`;
396
+ const unkeyed = compose || several ? `!${keyed}` : `github.ref != '${main}'`;
393
397
  // Written as lines: the emitter would quote the expression, and GitHub reads it the same either way.
394
398
  text = writeTopLevel(text, "concurrency", [
395
399
  "concurrency:",
@@ -55,7 +55,7 @@
55
55
 
56
56
  import { judgeCommits, mergeCommits, readWorkIndex, type CommitLink } from "./commit-links";
57
57
  import { execFileSync } from "node:child_process";
58
- import { readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type CiGreen, type CiPhase, type CiSource } from "./declaration";
58
+ import { greenBranches, readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type CiGreen, type CiPhase, type CiSource } from "./declaration";
59
59
  import type { CheckRun, CiForge, OpRunRef } from "./ci-green-forge";
60
60
  import type { OpRunRecord } from "../op/runtime";
61
61
  import { readRunLedger } from "../lifecycle/run-ledger";
@@ -514,8 +514,25 @@ export function readGreen(cwd: string): { green: CiGreen; top: string; workspace
514
514
  return { green, top, workspace: { name: declaration.name, root: located.root, file: declaration.file } };
515
515
  }
516
516
 
517
+ /**
518
+ * `green` judging `branch` (#4325): `ci.green` names several branches when
519
+ * `ci.branches` routes members to branches of their own, and each is judged
520
+ * as the single branch always was. Without `branch` the first is judged.
521
+ * Refuses a branch `ci.green` does not name.
522
+ */
523
+ export function greenOn(green: CiGreen, branch: string | undefined): CiGreen {
524
+ const { branches: _all, ...rest } = green;
525
+ if (branch === undefined || branch === green.branch) return rest;
526
+ if (!greenBranches(green).includes(branch)) {
527
+ throw new CiGreenError("ci-branch-unknown", `ci.green judges ${greenBranches(green).join(", ")}, not ${branch}; add it to ci.green.branch to give it a green verdict`);
528
+ }
529
+ return { ...rest, branch };
530
+ }
531
+
517
532
  export interface TickRequest {
518
533
  cwd: string;
534
+ /** Judge this one branch of `ci.green.branch` (#4325). Without it a tick judges every branch the declaration names. */
535
+ branch?: string;
519
536
  /**
520
537
  * Where check runs come from. A function is called only when the source
521
538
  * is `checks`, so a tick from runs needs no forge token. Required for
@@ -547,6 +564,8 @@ export interface ComposedBranchTick {
547
564
 
548
565
  export interface TickResult {
549
566
  branch: string;
567
+ /** The other branches `ci.green` names (#4325), each judged as this one is. Their tags are in `made`. Absent for a one-branch declaration. */
568
+ routed?: TickResult[];
550
569
  window: string;
551
570
  /** The ref the commits were read from. */
552
571
  ref: string;
@@ -601,7 +620,16 @@ export function ciTagSigner(top: string, base: string, pem: string, what = "the
601
620
  export async function ciTick(req: TickRequest): Promise<TickResult> {
602
621
  const read = readGreen(req.cwd);
603
622
  const top = read.top;
604
- const green: CiGreen = { ...read.green, source: req.source ?? read.green.source };
623
+ const names = greenBranches(read.green);
624
+ if (req.branch === undefined && names.length > 1) {
625
+ const first = await ciTick({ ...req, branch: names[0]! });
626
+ const routed: TickResult[] = [];
627
+ for (const b of names.slice(1)) routed.push(await ciTick({ ...req, branch: b }));
628
+ return { ...first, routed, made: [...first.made, ...routed.flatMap((r) => r.made)] };
629
+ }
630
+ const green: CiGreen = { ...greenOn(read.green, req.branch), source: req.source ?? read.green.source };
631
+ // Compose branches are walked from trunk, so only the first branch's tick walks them.
632
+ const walksCompose = green.branch === read.green.branch;
605
633
  const forge = green.source === "checks" ? (typeof req.forge === "function" ? req.forge() : req.forge) : undefined;
606
634
  if (green.source === "checks" && !forge) throw new Error("ci.green.source is checks, so the tick reads check runs and needs a forge");
607
635
  const remote = req.remote ?? "origin";
@@ -671,7 +699,7 @@ export async function ciTick(req: TickRequest): Promise<TickResult> {
671
699
  if (links) for (const c of planned) c.work = links.get(c.sha);
672
700
  // Compose branches (#4168 4.3): each composed commit within the window is judged as trunk's are, and its tags name its branch.
673
701
  const composed: ComposedBranchTick[] = [];
674
- for (const b of remoteComposeBranches(top, remote)) {
702
+ for (const b of walksCompose ? remoteComposeBranches(top, remote) : []) {
675
703
  const own = git(top, ["log", "--first-parent", `--max-age=${cutoff}`, "--format=%H %ct", b.ref, `^${tracking}`])
676
704
  .split("\n")
677
705
  .filter(Boolean)
@@ -904,18 +932,20 @@ export interface RequestBuildResult {
904
932
  */
905
933
  export async function requestBuild(req: { cwd: string; at: string; remote?: string; forge?: CiForge; now?: Date }): Promise<RequestBuildResult> {
906
934
  const read = readGreen(req.cwd);
907
- const { green, top } = read;
935
+ const { top } = read;
936
+ const green = greenOn(read.green, undefined);
937
+ const names = greenBranches(read.green);
908
938
  const remote = req.remote ?? "origin";
909
- const tracking = `refs/remotes/${remote}/${green.branch}`;
939
+ const trackingOf = (b: string): string => `refs/remotes/${remote}/${b}`;
910
940
  try {
911
- git(top, ["fetch", "--quiet", "--no-tags", remote, `+refs/heads/${green.branch}:${tracking}`, `+refs/tags/${CI_BUILD_TAG_PREFIX}*:refs/tags/${CI_BUILD_TAG_PREFIX}*`]);
941
+ git(top, ["fetch", "--quiet", "--no-tags", remote, ...names.map((b) => `+refs/heads/${b}:${trackingOf(b)}`), `+refs/tags/${CI_BUILD_TAG_PREFIX}*:refs/tags/${CI_BUILD_TAG_PREFIX}*`]);
912
942
  } catch (err) {
913
- throw new Error(`could not fetch ${green.branch} and the build requests from ${remote}: ${stderrOf(err)}`);
943
+ throw new Error(`could not fetch ${names.join(", ")} and the build requests from ${remote}: ${stderrOf(err)}`);
914
944
  }
915
945
  if (req.at.startsWith("-") || tryGit(top, ["rev-parse", "--verify", "--quiet", `${req.at}^{commit}`]) === null) throw new WorkspaceReadError("revision-unknown", `--at ${req.at} names no commit in this repository`);
916
946
  const sha = git(top, ["rev-parse", `${req.at}^{commit}`]);
917
- const onBranch = git(top, ["rev-list", "--first-parent", tracking]).split("\n").includes(sha);
918
- if (!onBranch) throw new Error(`${sha.slice(0, 12)} is not a first-parent commit of ${remote}/${green.branch}, and only those are judged green`);
947
+ const onBranch = names.some((b) => tryGit(top, ["rev-list", "--first-parent", trackingOf(b)])?.split("\n").includes(sha));
948
+ if (!onBranch) throw new Error(`${sha.slice(0, 12)} is not a first-parent commit of ${names.map((b) => `${remote}/${b}`).join(" or ")}, and only those are judged green`);
919
949
  const existing = readBuildRequests(top).get(sha);
920
950
  const request = existing ?? requestedBuild(green, sha, green.require, req.now ?? new Date());
921
951
  const warnings: string[] = [];
@@ -1101,6 +1131,7 @@ export function whyWorkUnknown(work: CommitLink | null | undefined): string | un
1101
1131
 
1102
1132
  /** The text `chant ci tick` prints. */
1103
1133
  export function formatTick(r: TickResult, dryRun = false): string {
1134
+ if (r.routed) return [formatTick({ ...r, routed: undefined }, dryRun), ...r.routed.map((b) => formatTick(b, dryRun))].join("\n");
1104
1135
  const lines = [`${r.branch}, commits within ${r.window}${r.source === "runs" ? ", judged from chant runs" : ""}: ${r.commits.length}`];
1105
1136
  for (const c of r.commits) {
1106
1137
  const short = c.sha.slice(0, 12);
@@ -1187,10 +1218,12 @@ export const LAST_GREEN_NEWER = 50;
1187
1218
  * green tag and no revoked tag. It reads local refs and tags and never
1188
1219
  * fetches, so a reader fetches first (`git fetch origin --tags`).
1189
1220
  */
1190
- export function lastGreen(req: { cwd: string; remote?: string; from?: string }): LastGreenDocument {
1221
+ export function lastGreen(req: { cwd: string; remote?: string; from?: string; branch?: string }): LastGreenDocument {
1191
1222
  const head = { $schema: CI_LAST_GREEN_SCHEMA_ID, contract: CI_LAST_GREEN_CONTRACT_VERSION, chant: readerVersion() };
1192
1223
  try {
1193
- const { green, top, workspace } = readGreen(req.cwd);
1224
+ const read = readGreen(req.cwd);
1225
+ const { top, workspace } = read;
1226
+ const green = greenOn(read.green, req.branch);
1194
1227
  const branchRef = branchRefOf(top, green, req.remote);
1195
1228
  let ref = branchRef;
1196
1229
  if (req.from !== undefined) {
@@ -1355,6 +1388,8 @@ function recordedPhase(green: CountedCiTag | undefined, phase: string): CiListPh
1355
1388
 
1356
1389
  export interface ListRequest {
1357
1390
  cwd: string;
1391
+ /** List this branch of `ci.green.branch` (#4325). The first when absent. */
1392
+ branch?: string;
1358
1393
  remote?: string;
1359
1394
  /** Keep only the newest n commits. */
1360
1395
  limit?: number;
@@ -1380,7 +1415,9 @@ export interface ListRequest {
1380
1415
  export async function ciList(req: ListRequest): Promise<CiListDocument> {
1381
1416
  const head = { $schema: CI_LIST_SCHEMA_ID, contract: CI_LIST_CONTRACT_VERSION, chant: readerVersion() };
1382
1417
  try {
1383
- const { green, top, workspace } = readGreen(req.cwd);
1418
+ const read = readGreen(req.cwd);
1419
+ const { top, workspace } = read;
1420
+ const green = greenOn(read.green, req.branch);
1384
1421
  const ref = branchRefOf(top, green, req.remote);
1385
1422
 
1386
1423
  const logArgs = ["log", "--first-parent", "--format=%H %ct"];
@@ -17,7 +17,7 @@ import { join } from "node:path";
17
17
  import { afterAll, beforeAll, describe, expect, test } from "vitest";
18
18
  import { cleanScratch, declaration, git, repoFixture, scratchDir, writeFiles } from "./__fixtures__/contract-repo";
19
19
  import { checkRunEventLines } from "./__fixtures__/run-event-check";
20
- import { ciJobCommand, ciJobEnv, ciJobFailure, ciJobOp, ciLoopOptions, ciRun, CiRunError, ciStatusContext, ciStatusFromEnv, ciStatusUrl, formatCiRun } from "./ci-run";
20
+ import { chantNodeModules, ciJobCommand, ciJobEnv, ciJobFailure, ciJobOp, ciLoopOptions, ciRun, CiRunError, ciStatusContext, ciStatusFromEnv, ciStatusUrl, formatCiRun } from "./ci-run";
21
21
  import type { CommitStatus, ForgeStatus, PrForge } from "../pr-forge";
22
22
  import { FORGE_TOKEN_VARS } from "../ci-handover";
23
23
  import { ciList, ciTick, evaluateCommit, readOpRunSource, revokedAnnotation } from "./ci-green";
@@ -1040,6 +1040,77 @@ describe("the plan's steps hold no forge token (#4295)", () => {
1040
1040
  });
1041
1041
  });
1042
1042
 
1043
+ /** A scheduled watch Op's file, shaped as discovery reads one (entity.props = OpConfig), importing nothing. */
1044
+ const WATCH_OP = (name: string): string =>
1045
+ `export default { props: { name: ${JSON.stringify(name)}, overview: "t", schedule: { cron: "0 * * * *" }, labels: { Watch: "true" }, phases: [{ name: "Run", steps: [] }] } };\n`;
1046
+
1047
+ describe("chant ci run plan checks the scheduled Ops at --base for the note (#4328)", () => {
1048
+ /**
1049
+ * The base declares watch Op `drift`, with its generated workflow. The
1050
+ * change deletes it and adds `sneaky`. The note names the Op the base has,
1051
+ * since the check reads the Ops at --base, and never the change's own.
1052
+ */
1053
+ function stale(): { app: string; base: string; sha: string } {
1054
+ const { root, app } = workspace();
1055
+ writeFiles(root, {
1056
+ "app/chant.config.json": "{}",
1057
+ "app/ops/drift.op.ts": WATCH_OP("drift"),
1058
+ ".forgejo/workflows/app-drift.yml": "on: schedule\n",
1059
+ });
1060
+ const base = commitAt(root, 1, "app/b.txt");
1061
+ execFileSync("git", ["rm", "-q", "app/ops/drift.op.ts"], { cwd: root });
1062
+ writeFiles(root, { "app/ops/sneaky.op.ts": WATCH_OP("sneaky"), ".forgejo/workflows/app-sneaky.yml": "on: schedule\n" });
1063
+ const sha = commitAt(root, 0.5, "app/c.txt");
1064
+ return { app, base, sha };
1065
+ }
1066
+
1067
+ function overdueForge(): { forge: PrForge; notes: string[]; asked: string[] } {
1068
+ const notes: string[] = [];
1069
+ const asked: string[] = [];
1070
+ const forge = {
1071
+ kind: "forgejo",
1072
+ host: "forgejo.localhost",
1073
+ async setStatus() {},
1074
+ async upsertNote(_pr: number, _marker: string, body: string) {
1075
+ notes.push(body);
1076
+ return "1";
1077
+ },
1078
+ // Every Op last ran a year ago: overdue whichever is asked about.
1079
+ async scheduledRuns(q: { op: string }) {
1080
+ asked.push(q.op);
1081
+ return { last: "2025-10-01T00:00:00Z" };
1082
+ },
1083
+ } as unknown as PrForge;
1084
+ return { forge, notes, asked };
1085
+ }
1086
+
1087
+ test("on the runner, the note warns of the base's overdue Op and not of one the change added", async () => {
1088
+ const { app, base, sha } = stale();
1089
+ const forge = overdueForge();
1090
+ const r = await ciRun({ image: JOB_IMAGE, cwd: app, job: "plan", env: "prod", forge: "forgejo", base, pr: 7, chant: fakeChant(), hostEnv: { ...HOST, FAKE_REPORT: planReport(sha) }, passEnv: ["FAKE_REPORT"], planForge: forge.forge, push: false, output: join(scratchDir("chant-ci-overdue-"), "pr") });
1091
+ expect(r.status).toBe("ok");
1092
+ expect(forge.asked).toEqual(["drift"]);
1093
+ expect(forge.notes).toHaveLength(1);
1094
+ expect(forge.notes[0]).toContain("Op `drift` is overdue");
1095
+ expect(forge.notes[0]).not.toContain("sneaky");
1096
+ expect(r.warnings).toEqual([expect.stringMatching(/Op `drift` is overdue/)]);
1097
+ });
1098
+
1099
+ test("in place, the check still reads the base, not the checkout the change runs in", async () => {
1100
+ const { app, base, sha } = stale();
1101
+ const forge = overdueForge();
1102
+ const r = await ciRun({ cwd: app, job: "plan", env: "prod", forge: "forgejo", base, pr: 7, chant: fakeChant(), hostEnv: { ...HOST, GITHUB_JOB: "plan", FAKE_REPORT: planReport(sha) }, inPlace: true, push: false, planForge: forge.forge });
1103
+ expect(r.status).toBe("ok");
1104
+ expect(forge.asked).toEqual(["drift"]);
1105
+ expect(forge.notes[0]).toContain("Op `drift` is overdue");
1106
+ });
1107
+
1108
+ test("the base checkout resolves packages from the node_modules the running chant is installed in", () => {
1109
+ expect(chantNodeModules("/opt/ci/node_modules/@intentius/chant/dist/workspace/ci-run.js")).toBe("/opt/ci/node_modules");
1110
+ expect(chantNodeModules("/src/chant/packages/core/src/workspace/ci-run.ts")).toBeUndefined();
1111
+ });
1112
+ });
1113
+
1043
1114
  /**
1044
1115
  * #4330: a pull request environment's jobs run through `ci run` too. The
1045
1116
  * stand-in chant keeps each action's arguments, environment, directory and
@@ -53,7 +53,9 @@
53
53
  * report with its own token (`postPlanReport`, ../pr-post.ts). In place, a
54
54
  * generated job holds no token and its `plan-note` job posts; a job that
55
55
  * still holds one was generated before #4295, and the runner posts for it
56
- * with a warning to regenerate.
56
+ * with a warning to regenerate. Either way the note's overdue check (#4289)
57
+ * reads the scheduled Ops from a fresh checkout of `--base`, as the
58
+ * plan-note job reads them at the target branch's commit (#4328).
57
59
  *
58
60
  * Signing is opt-in (#4156). With no runner key the record is unsigned, and
59
61
  * a reader counts it with no signer. A key `.chant/trust.json` does not list
@@ -81,14 +83,16 @@
81
83
  */
82
84
 
83
85
  import { execFileSync } from "node:child_process";
84
- import { copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
86
+ import { copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
85
87
  import { tmpdir } from "node:os";
86
- import { dirname, join, relative, resolve, sep } from "node:path";
88
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
89
+ import { fileURLToPath } from "node:url";
87
90
  import { PR_APPLY_RECORD, PR_ENV_POST_JOB, PR_ENV_REPORT_DIR, PR_LOOP_REPORT_DIR, prEnvJobs, prLoopJobs, prLoopSetup, PR_LOOP_IMAGE, type PrEnvJob, type PrLoopJob, type PrLoopPipelineOptions } from "../components/pr-pipeline";
88
91
  import { detectForge, ForgeEnvironmentError, forgeFromEnv, FORGE_KINDS, type CommitStatus, type ForgeKind, type PrForge } from "../pr-forge";
89
92
  import { holdsForgeToken, withoutForgeTokens } from "../ci-handover";
90
93
  import { postPlanReport } from "../pr-post";
91
- import { PR_APPLY_GATE } from "../pr-loop";
94
+ import { overdueOpsAt } from "../op/schedule-overdue";
95
+ import { PR_APPLY_GATE, type PrOverdue } from "../pr-loop";
92
96
  import type { CommandContext } from "../cli/registry";
93
97
  import { formatError, formatWarning } from "../cli/format";
94
98
  import { CHANT_CI_RUN_FORGE_ENV, CHANT_CI_RUN_ID_ENV, FORGE_RUN_ENV, resolveCiJob, type RunOrigin } from "../lifecycle/release-ledger";
@@ -738,7 +742,9 @@ export async function ciRun(req: CiRunRequest): Promise<CiRunResult> {
738
742
  // A generated wave job's command, as prLoopWaveJobs writes it. #4161: a local job's command posts no status or note.
739
743
  const forgeCommand = req.noForge ? found.command.replace(` --forge ${req.forge}`, "") : found.command;
740
744
  const noStatus = req.local && !/ --no-status(?: |$)/.test(forgeCommand);
741
- const command = `${forgeCommand}${req.wave !== undefined ? ` --wave ${req.wave}` : ""}${noStatus ? " --no-status" : ""}`;
745
+ // #4325: a record or apply on a routed branch is told the branch, as the forge's ref would tell it in a pipeline. `ci serve` has no such ref.
746
+ const branch = req.job !== "plan" && req.loop?.branch !== undefined && !/ --branch(?: |=)/.test(forgeCommand) ? ` --branch ${/^[A-Za-z0-9._\/=:@%+,-]+$/.test(req.loop.branch) ? req.loop.branch : `'${req.loop.branch.replace(/'/g, `'\\''`)}'`}` : "";
747
+ const command = `${forgeCommand}${branch}${req.wave !== undefined ? ` --wave ${req.wave}` : ""}${noStatus ? " --no-status" : ""}`;
742
748
  const job = { ...found, command, ...(req.wave !== undefined ? { wave: req.wave } : {}) };
743
749
  const own = ciJobEnv(job, {
744
750
  forge: req.forge,
@@ -759,7 +765,7 @@ export async function ciRun(req: CiRunRequest): Promise<CiRunResult> {
759
765
  const redact = ciRedactor(req.inPlace ? { ...longValues(jobEnv), ...own } : jobEnv, req.secrets);
760
766
  try {
761
767
  const ran = await runCiJob(req, { cwd, top, commit, place, job, jobEnv, ...(redact ? { redact } : {}) });
762
- const posted = poster ? await postFromReport(req, poster, ran, { commit, ...(place.member ? { member: place.member } : {}) }) : [];
768
+ const posted = poster ? await postFromReport(req, poster, ran, { top, dir: place.dir, commit, ...(place.member ? { member: place.member } : {}) }) : [];
763
769
  const envPost = req.job === "env-up" ? await envPostAfterUp(req, { cwd, top, head: commit, place, jobs, hostEnv, ran }) : undefined;
764
770
  const withPost = envPost ? ("result" in envPost ? { ...ran, post: envPost.result } : { ...ran, warnings: [...ran.warnings, envPost.warning] }) : ran;
765
771
  const withPosts = posted.length > 0 ? { ...withPost, warnings: [...withPost.warnings, ...posted] } : withPost;
@@ -831,10 +837,11 @@ async function postFromReport(
831
837
  req: CiRunRequest,
832
838
  poster: NonNullable<ReturnType<typeof planPoster>>,
833
839
  ran: CiRunResult,
834
- at: { commit: string; member?: string },
840
+ at: { top: string; dir: string; commit: string; member?: string },
835
841
  ): Promise<string[]> {
836
842
  if ("error" in poster) return [poster.error];
837
843
  const warnings = poster.warning ? [poster.warning] : [];
844
+ const checked: string[] = [];
838
845
  const dir = ran.report ?? resolve(req.cwd, req.output ?? PR_LOOP_REPORT_DIR);
839
846
  const posted = await postPlanReport({
840
847
  forge: poster.forge,
@@ -844,9 +851,62 @@ async function postFromReport(
844
851
  env: req.env,
845
852
  ...(at.member ? { member: at.member } : {}),
846
853
  result: ran.status === "ok" ? "success" : "failure",
854
+ // #4328: as pr-post does (#4289), with the Ops read at --base, never from the change's checkout.
855
+ overdue: () => overdueAtBase(req, { forge: poster.forge, top: at.top, dir: at.dir, ...(at.member ? { member: at.member } : {}), log: (line) => checked.push(line) }),
847
856
  });
848
857
  if (posted.refused) warnings.push(`the plan report was not posted: ${posted.refused}`);
849
- return [...warnings, ...posted.warnings];
858
+ return [...warnings, ...checked, ...posted.warnings];
859
+ }
860
+
861
+ /**
862
+ * The overdue check for the plan's note (#4289, #4328), at `--base`, as the
863
+ * plan-note job's `pr-post` runs it at the target branch's commit. The Ops
864
+ * are read from a fresh checkout of the base outside the repository, never
865
+ * from the checkout the change's code ran in, in place or not. That checkout
866
+ * resolves packages from the `node_modules` the running chant is installed
867
+ * in ({@link chantNodeModules}), so an Op file's `@intentius/chant` is the
868
+ * chant running the job, not one the change installed. Without `--base`
869
+ * nothing is checked, and `log` says so.
870
+ */
871
+ async function overdueAtBase(
872
+ req: CiRunRequest,
873
+ at: { forge: PrForge; top: string; dir: string; member?: string; log: (line: string) => void },
874
+ ): Promise<PrOverdue[]> {
875
+ if (!at.forge.scheduledRuns) return [];
876
+ if (!req.base) {
877
+ at.log("the scheduled Ops were not checked: the check reads them at the target branch's commit, so pass --base <sha>");
878
+ return [];
879
+ }
880
+ let checkout: { dir: string; remove(): void };
881
+ try {
882
+ checkout = freshCheckout(at.top, resolveJobCommit(at.top, req.base));
883
+ } catch (err) {
884
+ at.log(`the scheduled Ops were not checked: ${message(err)}`);
885
+ return [];
886
+ }
887
+ try {
888
+ const modules = chantNodeModules();
889
+ const link = join(checkout.dir, "node_modules");
890
+ if (modules && !existsSync(link)) symlinkSync(modules, link, "dir");
891
+ return await overdueOpsAt({ forge: at.forge, cwd: at.dir === "." ? checkout.dir : join(checkout.dir, at.dir), ...(at.member ? { member: at.member } : {}), log: at.log });
892
+ } finally {
893
+ checkout.remove();
894
+ }
895
+ }
896
+
897
+ /**
898
+ * The `node_modules` directory the running chant is installed in, or
899
+ * undefined when it runs from a source checkout, where chant's own packages
900
+ * resolve without one (../ts-loader.ts).
901
+ */
902
+ export function chantNodeModules(from: string = fileURLToPath(import.meta.url)): string | undefined {
903
+ let dir = dirname(from);
904
+ for (;;) {
905
+ if (basename(dir) === "node_modules") return dir;
906
+ const parent = dirname(dir);
907
+ if (parent === dir) return undefined;
908
+ dir = parent;
909
+ }
850
910
  }
851
911
 
852
912
  /**
@@ -602,3 +602,98 @@ describe("ci serve runs fast-forwards only, and honours [skip ci] (#4122)", () =
602
602
  expect(ofType(events, "not-run")).toHaveLength(1);
603
603
  });
604
604
  });
605
+
606
+ describe("ci serve follows ci.branches (#4325)", () => {
607
+ /** A forge with a head per branch, and the branches it was asked about. */
608
+ function branchForge(heads: Record<string, string>): CiServeForge & { heads: Record<string, string>; polled: string[] } {
609
+ const f = {
610
+ heads,
611
+ polled: [] as string[],
612
+ async pullRequestFor() {
613
+ return null;
614
+ },
615
+ async openPullRequests(base: string) {
616
+ f.polled.push(base);
617
+ return [];
618
+ },
619
+ async branchHead(branch: string) {
620
+ return f.heads[branch];
621
+ },
622
+ };
623
+ return f;
624
+ }
625
+
626
+ /** The workspace with `ci.branches` routing `app` to `release`, and a `release` branch on the forge. */
627
+ function routed(globs: string[] = ["app"]): ReturnType<typeof workspace> & { release: string } {
628
+ const w = workspace();
629
+ writeFiles(w.root, { "chant.workspace.json": declaration([{ name: "app", dir: "app", kind: "other", because: "the app" }], { ci: { branches: { release: globs } } }) });
630
+ commit(w.root, "app/route.txt", "route app to release");
631
+ git(w.root, "push", "-q", "origin", "main");
632
+ git(w.root, "checkout", "-q", "-b", "release");
633
+ const release = commit(w.root, "app/rel.txt");
634
+ git(w.root, "push", "-q", "origin", "release");
635
+ git(w.root, "checkout", "-q", "main");
636
+ return { ...w, main: git(w.root, "rev-parse", "main"), release };
637
+ }
638
+
639
+ test("a routed member polls its route alone, and its record job gets --branch", async () => {
640
+ const { root, app, main, release } = routed();
641
+ const forge = branchForge({ main, release });
642
+ const { opts, events } = serve(app, forge);
643
+ await ciServePass(opts);
644
+ expect(forge.polled).toEqual(["release"]);
645
+ expect(ofType(events, "baseline")).toEqual([{ type: "baseline", commit: release }]);
646
+
647
+ git(root, "checkout", "-q", "release");
648
+ const next = commit(root, "app/rel2.txt");
649
+ git(root, "push", "-q", "origin", "release");
650
+ git(root, "checkout", "-q", "main");
651
+ forge.heads.release = next;
652
+ const pass = await ciServePass(opts);
653
+ expect(pass.jobs).toEqual([expect.objectContaining({ job: "record", commit: next, base: release, status: "ok" })]);
654
+ const argv = argsOf(pass.jobs[0]!.report!, "pr-record");
655
+ expect(argv[argv.indexOf("--branch") + 1]).toBe("release");
656
+ // A push to main is not this member's.
657
+ forge.heads.main = "0".repeat(40);
658
+ expect((await ciServePass(opts)).jobs).toEqual([]);
659
+ });
660
+
661
+ test("a member no glob matches polls main and passes no --branch", async () => {
662
+ const { root, app, main } = routed(["other-*"]);
663
+ const forge = branchForge({ main });
664
+ const { opts } = serve(app, forge);
665
+ await ciServePass(opts);
666
+ expect(forge.polled).toEqual(["main"]);
667
+ const merged = commit(root, "app/merged.txt");
668
+ git(root, "push", "-q", "origin", "main");
669
+ forge.heads.main = merged;
670
+ const pass = await ciServePass(opts);
671
+ expect(pass.jobs).toEqual([expect.objectContaining({ job: "record", commit: merged, base: main })]);
672
+ expect(argsOf(pass.jobs[0]!.report!, "pr-record")).not.toContain("--branch");
673
+ });
674
+
675
+ test("the workspace root polls main and each routed branch, each from its own head", async () => {
676
+ const { root, main, release } = routed();
677
+ const forge = branchForge({ main, release });
678
+ const { opts } = serve(root, forge);
679
+ await ciServePass(opts);
680
+ expect(forge.polled).toEqual(["main", "release"]);
681
+ git(root, "checkout", "-q", "release");
682
+ const next = commit(root, "app/rel2.txt");
683
+ git(root, "push", "-q", "origin", "release");
684
+ git(root, "checkout", "-q", "main");
685
+ forge.heads.release = next;
686
+ const pass = await ciServePass(opts);
687
+ expect(pass.jobs).toEqual([expect.objectContaining({ job: "record", commit: next, base: release })]);
688
+ const state = readCiServeState(join(opts.stateDir!, `${prApplyGroup("prod")}.json`), { forge: "forgejo", repo: "demo/estate", env: "prod", branch: "main" });
689
+ expect(state.target).toBe(main);
690
+ expect(state.targets).toEqual({ release: next });
691
+ expect((await ciServePass(opts)).jobs).toEqual([]);
692
+ });
693
+
694
+ test("a --branch that disagrees with the route is refused", async () => {
695
+ const { app, main, release } = routed();
696
+ const { opts } = serve(app, branchForge({ main, release }), { job: { loop: { branch: "main" } } });
697
+ await expect(ciServePass(opts)).rejects.toThrow(/routes member app to release/);
698
+ });
699
+ });