@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
@@ -59,6 +59,11 @@
59
59
  * contains the other. Each is a warning. A local branch ahead of the
60
60
  * forge's keeps its facts, and the job's push sends them. Nothing here
61
61
  * pushes the ledger.
62
+ * - Routed branches (#4325). `ci.branches` decides the branches one serve
63
+ * polls: a routed member's route alone, an unrouted member's `--branch`
64
+ * (default `main`), and at the workspace root `main` and each routed
65
+ * branch. Jobs on a routed branch get `--branch`. The state file keeps a
66
+ * head per branch (`target`, then `targets`).
62
67
  *
63
68
  * - Land branches (#4168 4.4). With `land`, each `chant/land/<name>` branch
64
69
  * pushed to the remote is a change asking to land without a pull request.
@@ -104,6 +109,7 @@ import { CI_JOB_OPS, ciJobPlace, ciLoopOptions, ciSecretsOf, ciRun, ciStatusFrom
104
109
  import { CiGreenError, ciTick, judgePreMerge, lastGreen, RUNNER_KEY_ENV, signedRunsRequired, type CommitVerdict } from "./ci-green";
105
110
  import { gitTop } from "./tree";
106
111
  import { readDeclaration } from "./declaration";
112
+ import { servedBranches } from "./ci-branches";
107
113
  import { locateWorkspace } from "./which-chant";
108
114
  import { ciServeRunnerKey, formatRunnerKeySetup, setupRunnerKey } from "./ci-runner-key";
109
115
 
@@ -124,8 +130,8 @@ export interface CiServeForge {
124
130
  * apply of the newest green commit (#4168 step 9).
125
131
  */
126
132
  export type CiServeTrigger =
127
- | { job: "plan"; commit: string; base: string; pr: number; rerun?: boolean }
128
- | { job: "apply" | "record"; commit: string; base: string; rerun?: boolean };
133
+ | { job: "plan"; commit: string; base: string; pr: number; rerun?: boolean; branch?: string }
134
+ | { job: "apply" | "record"; commit: string; base: string; rerun?: boolean; branch?: string };
129
135
 
130
136
  /**
131
137
  * The `git` forge kind (#4016 gap 6): the branch head from `git ls-remote`
@@ -193,6 +199,8 @@ export interface CiServeState {
193
199
  member?: string;
194
200
  /** The target branch's head the last apply was measured to, or the last head seen and not applied. */
195
201
  target?: string;
202
+ /** The same for each further branch `ci.branches` routes (#4325), by branch. `target` is the first served branch's. */
203
+ targets?: Record<string, string>;
196
204
  jobs: Record<string, CiServeJobState>;
197
205
  /** Plans seen and not run, by state key, with why (#4122). */
198
206
  skipped?: Record<string, "skip-ci">;
@@ -381,7 +389,12 @@ export function defaultCiServeStateDir(cwd: string): string {
381
389
  interface ServePlace {
382
390
  top: string;
383
391
  member?: string;
392
+ /** The first branch served: the one the state file's `target` follows. */
384
393
  branch: string;
394
+ /** Every branch served (#4325), `branch` first: a routed member's route, or at the workspace root the default and each routed branch. */
395
+ branches: string[];
396
+ /** The served branches a job is told with `--branch`: the ones `ci.branches` names. */
397
+ routed: string[];
385
398
  group: string;
386
399
  stateDir: string;
387
400
  stateFile: string;
@@ -394,7 +407,21 @@ function servePlace(opts: CiServeOptions): ServePlace {
394
407
  const { member } = ciJobPlace(cwd, top);
395
408
  const group = prApplyGroup(opts.env, member);
396
409
  const stateDir = opts.stateDir ? resolve(cwd, opts.stateDir) : defaultCiServeStateDir(cwd);
397
- return { top, ...(member ? { member } : {}), branch: opts.job?.loop?.branch ?? "main", group, stateDir, stateFile: join(stateDir, `${group}.json`) };
410
+ // #4325: ci.branches decides which branches this serve polls. A workspace that can't be read routes nothing.
411
+ let routes: Record<string, string[]> | undefined;
412
+ try {
413
+ routes = readDeclaration(locateWorkspace(cwd).tree).ci?.branches;
414
+ } catch {
415
+ routes = undefined;
416
+ }
417
+ const named = opts.job?.loop?.branch;
418
+ let served: ReturnType<typeof servedBranches>;
419
+ try {
420
+ served = servedBranches(routes, member, named ?? "main", named !== undefined);
421
+ } catch (err) {
422
+ throw new CiRunError(message(err));
423
+ }
424
+ return { top, ...(member ? { member } : {}), branch: served.served[0]!, branches: served.served, routed: served.routed, group, stateDir, stateFile: join(stateDir, `${group}.json`) };
398
425
  }
399
426
 
400
427
  /** The state file, or a new one. */
@@ -439,73 +466,81 @@ export async function ciServeObserve(opts: CiServeOptions, rt: CiServeRuntime =
439
466
  };
440
467
  const triggers: CiServeTrigger[] = [];
441
468
  const events: CiServeEvent[] = [];
442
- const [head, prs] = await Promise.all([opts.client.branchHead(place.branch), opts.client.openPullRequests(place.branch)]);
443
469
  // #4168: a new target commit is recorded. With applyOnMerge the newest green commit is applied, below.
444
470
  const onMerge = "record" as const;
471
+ const authors = opts.authors?.length ? new Set(opts.authors.map((a) => a.toLowerCase())) : undefined;
445
472
 
446
- if (head === undefined) {
447
- events.push({ type: "no-branch", branch: place.branch });
448
- } else if (state.target === undefined) {
449
- // What the branch held before this runner watched it was applied by whatever ran then.
450
- update((s) => {
451
- s.target = head;
452
- });
453
- events.push({ type: "baseline", commit: head });
454
- } else if (head !== state.target) {
455
- const from = state.target;
456
- const move = targetMove(place.top, place.branch, from, head);
457
- const entry = state.jobs[ciServeKey({ job: onMerge, commit: head })];
458
- if (move === "unknown") {
459
- events.push({ type: "error", message: `${place.branch} is at ${head.slice(0, 12)} on the forge, and that commit could not be fetched; the next poll tries again` });
460
- } else if (move === "other" || (!entry && skipsCi(place.top, head))) {
461
- // #4122: a move that is not a fast-forward, or a head that asks for no run, is seen and not applied.
462
- update((s) => {
463
- s.target = head;
464
- });
465
- events.push({ type: "not-run", job: onMerge, commit: head, reason: move === "other" ? "not-fast-forward" : "skip-ci", ...(move === "other" ? { branch: place.branch, from } : {}) });
466
- } else if (!entry) triggers.push({ job: onMerge, commit: head, base: from });
467
- else if (opts.rerunFailed && failedEntry(entry, ciServeKey(entry), rt)) triggers.push({ job: onMerge, commit: head, base: entry.base, rerun: true });
468
- } else {
469
- const key = ciServeKey({ job: onMerge, commit: head });
470
- const entry = state.jobs[key];
471
- if (opts.rerunFailed && failedEntry(entry, key, rt)) triggers.push({ job: onMerge, commit: head, base: entry.base, rerun: true });
472
- }
473
+ // #4325: each served branch is polled on its own, with its own head to measure moves from.
474
+ for (const branch of place.branches) {
475
+ const [head, prs] = await Promise.all([opts.client.branchHead(branch), opts.client.openPullRequests(branch)]);
476
+ const first = branch === place.branch;
477
+ const target = first ? state.target : state.targets?.[branch];
478
+ const setTarget = (s: CiServeState, sha: string): void => {
479
+ if (first) s.target = sha;
480
+ else s.targets = { ...s.targets, [branch]: sha };
481
+ };
482
+ // A job on a routed branch is told which, so its apply routes members as the pushed branch does.
483
+ const routed = place.routed.includes(branch) ? { branch } : {};
473
484
 
474
- if (opts.applyOnMerge && head !== undefined) {
475
- const green = newestGreen(place.top, opts.cwd, place.branch);
476
- if (green) {
477
- const key = ciServeKey({ job: "apply", commit: green });
485
+ if (head === undefined) {
486
+ events.push({ type: "no-branch", branch });
487
+ } else if (target === undefined) {
488
+ // What the branch held before this runner watched it was applied by whatever ran then.
489
+ update((s) => setTarget(s, head));
490
+ events.push({ type: "baseline", commit: head });
491
+ } else if (head !== target) {
492
+ const from = target;
493
+ const move = targetMove(place.top, branch, from, head);
494
+ const entry = state.jobs[ciServeKey({ job: onMerge, commit: head })];
495
+ if (move === "unknown") {
496
+ events.push({ type: "error", message: `${branch} is at ${head.slice(0, 12)} on the forge, and that commit could not be fetched; the next poll tries again` });
497
+ } else if (move === "other" || (!entry && skipsCi(place.top, head))) {
498
+ // #4122: a move that is not a fast-forward, or a head that asks for no run, is seen and not applied.
499
+ update((s) => setTarget(s, head));
500
+ events.push({ type: "not-run", job: onMerge, commit: head, reason: move === "other" ? "not-fast-forward" : "skip-ci", ...(move === "other" ? { branch, from } : {}) });
501
+ } else if (!entry) triggers.push({ job: onMerge, commit: head, base: from, ...routed });
502
+ else if (opts.rerunFailed && failedEntry(entry, ciServeKey(entry), rt)) triggers.push({ job: onMerge, commit: head, base: entry.base, rerun: true, ...routed });
503
+ } else {
504
+ const key = ciServeKey({ job: onMerge, commit: head });
478
505
  const entry = state.jobs[key];
479
- if (!entry) triggers.push({ job: "apply", commit: green, base: green });
480
- else if (opts.rerunFailed && failedEntry(entry, key, rt)) triggers.push({ job: "apply", commit: green, base: entry.base, rerun: true });
506
+ if (opts.rerunFailed && failedEntry(entry, key, rt)) triggers.push({ job: onMerge, commit: head, base: entry.base, rerun: true, ...routed });
481
507
  }
482
- }
483
508
 
484
- const authors = opts.authors?.length ? new Set(opts.authors.map((a) => a.toLowerCase())) : undefined;
485
- for (const pr of prs) {
486
- if (pr.fork) {
487
- events.push({ type: "skipped", pr: pr.number, reason: "fork" });
488
- continue;
489
- }
490
- if (authors && !authors.has((pr.author ?? "").toLowerCase())) {
491
- events.push({ type: "skipped", pr: pr.number, reason: "author", ...(pr.author ? { author: pr.author } : {}) });
492
- continue;
509
+ if (opts.applyOnMerge && head !== undefined) {
510
+ const green = newestGreen(place.top, opts.cwd, branch, place.routed.includes(branch));
511
+ if (green) {
512
+ const key = ciServeKey({ job: "apply", commit: green });
513
+ const entry = state.jobs[key];
514
+ if (!entry) triggers.push({ job: "apply", commit: green, base: green, ...routed });
515
+ else if (opts.rerunFailed && failedEntry(entry, key, rt)) triggers.push({ job: "apply", commit: green, base: entry.base, rerun: true, ...routed });
516
+ }
493
517
  }
494
- const key = ciServeKey({ job: "plan", commit: pr.headSha, pr: pr.number });
495
- const entry = state.jobs[key];
496
- const base = pr.baseSha ?? head;
497
- if (!base || state.skipped?.[key]) continue;
498
- if (!entry) {
499
- // #4122: a head whose message asks for no run is seen and not planned.
500
- if (gitStatus(place.top, ["cat-file", "-e", `${pr.headSha}^{commit}`]).code !== 0) tryGit(place.top, ["fetch", "--quiet", "--no-tags", "origin", `refs/pull/${pr.number}/head`]);
501
- if (skipsCi(place.top, pr.headSha)) {
502
- update((s) => {
503
- s.skipped = { ...s.skipped, [key]: "skip-ci" };
504
- });
505
- events.push({ type: "not-run", job: "plan", commit: pr.headSha, pr: pr.number, reason: "skip-ci" });
506
- } else triggers.push({ job: "plan", commit: pr.headSha, base, pr: pr.number });
518
+
519
+ for (const pr of prs) {
520
+ if (pr.fork) {
521
+ events.push({ type: "skipped", pr: pr.number, reason: "fork" });
522
+ continue;
523
+ }
524
+ if (authors && !authors.has((pr.author ?? "").toLowerCase())) {
525
+ events.push({ type: "skipped", pr: pr.number, reason: "author", ...(pr.author ? { author: pr.author } : {}) });
526
+ continue;
527
+ }
528
+ const key = ciServeKey({ job: "plan", commit: pr.headSha, pr: pr.number });
529
+ const entry = state.jobs[key];
530
+ const base = pr.baseSha ?? head;
531
+ if (!base || state.skipped?.[key]) continue;
532
+ if (!entry) {
533
+ // #4122: a head whose message asks for no run is seen and not planned.
534
+ if (gitStatus(place.top, ["cat-file", "-e", `${pr.headSha}^{commit}`]).code !== 0) tryGit(place.top, ["fetch", "--quiet", "--no-tags", "origin", `refs/pull/${pr.number}/head`]);
535
+ if (skipsCi(place.top, pr.headSha)) {
536
+ update((s) => {
537
+ s.skipped = { ...s.skipped, [key]: "skip-ci" };
538
+ });
539
+ events.push({ type: "not-run", job: "plan", commit: pr.headSha, pr: pr.number, reason: "skip-ci" });
540
+ } else triggers.push({ job: "plan", commit: pr.headSha, base, pr: pr.number, ...routed });
541
+ }
542
+ else if (opts.rerunFailed && failedEntry(entry, key, rt)) triggers.push({ job: "plan", commit: pr.headSha, base: entry.base, pr: pr.number, rerun: true, ...routed });
507
543
  }
508
- else if (opts.rerunFailed && failedEntry(entry, key, rt)) triggers.push({ job: "plan", commit: pr.headSha, base: entry.base, pr: pr.number, rerun: true });
509
544
  }
510
545
  return { triggers, events };
511
546
  }
@@ -581,7 +616,10 @@ export async function ciServeSchedule(opts: CiServeOptions, triggers: readonly C
581
616
  const entry: CiServeJobState = { job: t.job, commit: t.commit, base: t.base, ...pr, status: "running", runId, attempts: attempt, startedAt: now().toISOString() };
582
617
  update((s) => {
583
618
  s.jobs[key] = entry;
584
- if (t.job === "record" && !t.rerun) s.target = t.commit;
619
+ if (t.job === "record" && !t.rerun) {
620
+ if (t.branch === undefined || t.branch === place.branch) s.target = t.commit;
621
+ else s.targets = { ...s.targets, [t.branch]: t.commit };
622
+ }
585
623
  });
586
624
  emit({ type: "started", job: t.job, commit: t.commit, ...pr, base: t.base, runId, attempt, ...(resume && existsSync(resume) ? { resume } : {}) });
587
625
 
@@ -621,13 +659,15 @@ async function runJob(
621
659
  let merged: number | undefined;
622
660
  try {
623
661
  // The pull request's head is on the forge's refs/pull/<n>/head, the target's on the branch.
624
- tryGit(place.top, ["fetch", "--quiet", "--no-tags", "origin", t.job === "plan" ? `refs/pull/${t.pr}/head` : place.branch]);
662
+ tryGit(place.top, ["fetch", "--quiet", "--no-tags", "origin", t.job === "plan" ? `refs/pull/${t.pr}/head` : (t.branch ?? place.branch)]);
625
663
  const hostEnv = { ...(opts.job?.hostEnv ?? process.env), GITHUB_REPOSITORY: opts.repo };
626
664
  // An apply is for a merge: pass the pull request whose merge commit it is, so the commit status link and the run index use `<pr>/<commit>`. None found, or a forge that errs, is today's behaviour.
627
665
  if (t.job === "record" && opts.client.pullRequestFor) merged = (await opts.client.pullRequestFor(t.commit).catch(() => null)) ?? undefined;
628
666
  const run = opts.run ?? ciRun;
629
667
  const r = await run({
630
668
  ...opts.job,
669
+ // #4325: a job on a routed branch is told the branch, so it routes members as a push to it does.
670
+ ...(t.branch !== undefined ? { loop: { ...opts.job?.loop, branch: t.branch } } : {}),
631
671
  hostEnv,
632
672
  cwd: opts.cwd,
633
673
  job: t.job,
@@ -656,10 +696,11 @@ async function runJob(
656
696
  * fetching the ci tags, or undefined when none is green or the workspace
657
697
  * declares no `ci.green`.
658
698
  */
659
- function newestGreen(top: string, cwd: string, branch: string): string | undefined {
699
+ function newestGreen(top: string, cwd: string, branch: string, routed = false): string | undefined {
660
700
  tryGit(top, ["fetch", "--quiet", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, "+refs/tags/ci/*:refs/tags/ci/*"]);
661
701
  try {
662
- const doc = lastGreen({ cwd });
702
+ // #4325: a routed branch has its own verdict, and none when ci.green does not name it.
703
+ const doc = lastGreen({ cwd, ...(routed ? { branch } : {}) });
663
704
  return "commit" in doc && doc.commit ? doc.commit.sha : undefined;
664
705
  } catch {
665
706
  return undefined;
@@ -14,9 +14,12 @@ import Ajv2020 from "ajv/dist/2020";
14
14
  import { afterEach, beforeEach, describe, expect, test } from "vitest";
15
15
  import { computeComponentGraph } from "../components/cli-support";
16
16
  import componentSchema from "../components/component.schema.json";
17
+ import { ChantConfigSchema, environmentExecutor } from "../config";
17
18
  import { componentEnvironments, memberEnvironments, type EnvironmentReason } from "./environments";
18
19
  import type { MemberRuntimes } from "./runtimes";
19
20
 
21
+ const NO_EXECUTOR = { forge: null, workflow: null, reason: { code: "executor-none-declared", message: expect.any(String) } };
22
+
20
23
  const runtimes = (environments: MemberRuntimes["environments"], lexicons: string[] = []): MemberRuntimes => ({ lexicons, default: "local", reasons: [], environments });
21
24
 
22
25
  const SITES = [
@@ -68,13 +71,14 @@ describe("graph --composites lists each environment with the component's site (#
68
71
  const member = memberEnvironments(runtimes(["prod", "fly"], ["fly"]), { envs: [], reason: null });
69
72
  const envs = componentEnvironments("app", member, "local", SITES, ["fly"]);
70
73
  expect(envs).toEqual([
71
- { name: "local", default: true, source: "builtin", site: null, command: "chant run --components app" },
72
- { name: "prod", default: false, source: "config", site: { runtime: "local", url: null, domain: null, lifecycle: null }, command: "chant run --components app --env prod" },
74
+ { name: "local", default: true, source: "builtin", site: null, executor: NO_EXECUTOR, command: "chant run --components app" },
75
+ { name: "prod", default: false, source: "config", site: { runtime: "local", url: null, domain: null, lifecycle: null }, executor: NO_EXECUTOR, command: "chant run --components app --env prod" },
73
76
  {
74
77
  name: "fly",
75
78
  default: false,
76
79
  source: "config",
77
80
  site: { runtime: "fly", url: "https://shop.fly.dev", domain: "shop.example.com", lifecycle: "origin" },
81
+ executor: NO_EXECUTOR,
78
82
  command: "chant run --components app --on fly --env fly",
79
83
  },
80
84
  ]);
@@ -103,3 +107,22 @@ describe("graph --composites lists each environment with the component's site (#
103
107
  expect(envs.find((e) => e.name === "pr-7")).toMatchObject({ source: "component", site: { url: "https://pr-7.example.com" } });
104
108
  });
105
109
  });
110
+
111
+ describe("an environment's executor in chant.config.ts (#4402)", () => {
112
+ const prod = { name: "prod", executor: { forge: "github" as const, workflow: "deploy-prod.yml" } };
113
+
114
+ test("is optional on an environment entry, and refuses an unknown forge, an empty workflow and a stray field", () => {
115
+ expect(ChantConfigSchema.safeParse({ environments: ["staging", prod] }).success).toBe(true);
116
+ expect(ChantConfigSchema.safeParse({ environments: [{ name: "prod", executor: { forge: "jenkins", workflow: "x.yml" } }] }).success).toBe(false);
117
+ expect(ChantConfigSchema.safeParse({ environments: [{ name: "prod", executor: { forge: "github", workflow: "" } }] }).success).toBe(false);
118
+ expect(ChantConfigSchema.safeParse({ environments: [{ name: "prod", executor: { forge: "github", workflow: "x.yml", ref: "main" } }] }).success).toBe(false);
119
+ });
120
+
121
+ test("resolves a literal entry before a pattern, and a literal without one doesn't take the pattern's", () => {
122
+ const envs = [prod, "pr-special", { name: "pr-*", executor: { forge: "gitlab" as const, workflow: ".gitlab-ci.yml" } }];
123
+ expect(environmentExecutor(envs, "prod")).toEqual(prod.executor);
124
+ expect(environmentExecutor(envs, "pr-7")).toEqual({ forge: "gitlab", workflow: ".gitlab-ci.yml" });
125
+ expect(environmentExecutor(envs, "pr-special")).toBeUndefined();
126
+ expect(environmentExecutor(envs, "staging")).toBeUndefined();
127
+ });
128
+ });
@@ -100,6 +100,12 @@ export interface ComposedMember {
100
100
  * the outer `nodes` with `outer/inner/id` ids.
101
101
  */
102
102
  nested?: NestedWorkspace;
103
+ /**
104
+ * For a member of kind `repo` (#3168): the repository and pinned revision it
105
+ * names, and `read`, the repo identity the other repository's own graph
106
+ * document gave, or null. Its nodes fold in as a nested workspace's do.
107
+ */
108
+ repo?: { url: string; rev: string; workspace: string; read: string | null };
103
109
  /**
104
110
  * The factory and listing the member's box block declares (#3146, ws-077),
105
111
  * read from the declaration at the revision read. Set by
@@ -181,7 +187,8 @@ export type ComposedDangling = IRDanglingRef & { member: string; nested?: string
181
187
 
182
188
  export interface WorkspaceGraph {
183
189
  version: number;
184
- workspace: { name: string; root: string };
190
+ /** `repo` (#3168): the repository the document came from, its origin URL redacted, or null; absent from a chant before it. */
191
+ workspace: { name: string; root: string; repo?: string | null };
185
192
  members: ComposedMember[];
186
193
  nodes: ComposedNode[];
187
194
  edges: ComposedEdge[];
@@ -335,7 +342,7 @@ function sorted(rec: Record<string, string[]>): Record<string, string[]> {
335
342
  * `read` holds the links each member's kind reader found in its files (#3932).
336
343
  */
337
344
  export function composeWorkspaceGraph(
338
- workspace: { name: string; root: string },
345
+ workspace: { name: string; root: string; repo?: string | null },
339
346
  inputs: ComposeInput[],
340
347
  links?: { declaration: Declaration; kinds?: KindRegistry; read?: ReadonlyMap<string, KindLinkRead> },
341
348
  ): WorkspaceGraph {
@@ -607,6 +607,28 @@
607
607
  }
608
608
  ]
609
609
  },
610
+ "executor": {
611
+ "description": "The forge and committed workflow that deploys this environment, as the member's chant.config.ts declares on the environment's entry (#4402): a literal entry before a pattern. forge and workflow are null when none is declared, and reason then says why. Never derived from a generated workflow's name. Added by chant 0.124.47; a document from an older chant has no executor.",
612
+ "type": "object",
613
+ "required": ["forge", "workflow", "reason"],
614
+ "properties": {
615
+ "forge": { "enum": ["github", "gitlab", "forgejo", null] },
616
+ "workflow": { "type": ["string", "null"], "description": "The workflow file the forge runs, such as deploy-prod.yml under .github/workflows/, or .gitlab-ci.yml." },
617
+ "reason": {
618
+ "oneOf": [
619
+ { "type": "null" },
620
+ {
621
+ "type": "object",
622
+ "required": ["code", "message"],
623
+ "properties": {
624
+ "code": { "enum": ["executor-none-declared", "executor-config-unreadable"] },
625
+ "message": { "type": "string" }
626
+ }
627
+ }
628
+ ]
629
+ }
630
+ }
631
+ },
610
632
  "command": {
611
633
  "type": "string",
612
634
  "description": "The command that deploys the component to this environment on its default runtime, run in the member's directory: chant run --components <name>, with --on <runtime> when the default runtime isn't local, and --env <env> for any environment but the default."
@@ -17,6 +17,7 @@ import { MEMBER_RUN_REASON_CODES } from "./compose-graph";
17
17
  import {
18
18
  COMPOSITES_CONTRACT_VERSION,
19
19
  COMPOSITES_ENVIRONMENT_REASON_CODES,
20
+ COMPOSITES_EXECUTOR_REASON_CODES,
20
21
  COMPOSITES_ERROR_CODES,
21
22
  COMPOSITES_OUTPUT_SCHEMA_ID,
22
23
  COMPOSITES_REASON_CODES,
@@ -30,6 +31,8 @@ import {
30
31
  import { GRAPH_ERROR_CODES } from "./graph-cli";
31
32
  import schema from "./composites.schema.json";
32
33
 
34
+ const NO_EXECUTOR = { forge: null, workflow: null, reason: { code: "executor-none-declared", message: expect.any(String) } };
35
+
33
36
  afterAll(cleanScratch);
34
37
 
35
38
  const { expectValid } = contract(schema);
@@ -61,7 +64,7 @@ const componentIr = (components: { name: string; composites?: string[]; archetyp
61
64
  const local = (name: string) => ({ name: "local", lexicon: null, default: true, command: `chant run --components ${name}` });
62
65
 
63
66
  /** The default environment, named by nothing but chant itself. */
64
- const localEnv = (name: string) => ({ name: "local", default: true, source: "builtin", site: null, command: `chant run --components ${name}` });
67
+ const localEnv = (name: string) => ({ name: "local", default: true, source: "builtin", site: null, executor: NO_EXECUTOR, command: `chant run --components ${name}` });
65
68
 
66
69
  /**
67
70
  * app declares three instances and exports ImageUri; delivery links to it and
@@ -117,6 +120,7 @@ describe("composites output schema", () => {
117
120
  expect(schema.$defs.member.properties.reason.oneOf[1].properties!.code.enum).toEqual([...MEMBER_RUN_REASON_CODES]);
118
121
  expect(schema.$defs.member.properties.runtimeReasons.items.properties.code.enum).toEqual([...COMPOSITES_RUNTIME_REASON_CODES]);
119
122
  expect(schema.$defs.member.properties.environmentReasons.items.properties.code.enum).toEqual([...COMPOSITES_ENVIRONMENT_REASON_CODES]);
123
+ expect(schema.$defs.environment.properties.executor.properties.reason.oneOf[1].properties!.code.enum).toEqual([...COMPOSITES_EXECUTOR_REASON_CODES]);
120
124
  });
121
125
  });
122
126
 
@@ -381,11 +385,11 @@ describe("the environments each component may deploy to (#2695)", () => {
381
385
  const g = result((await workspaceComposites({ cwd: declared() })).doc);
382
386
  expectValid(g);
383
387
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments).toEqual([
384
- { name: "local", default: true, source: "builtin", site: null, command: "chant run --components queue-runner" },
385
- { name: "staging", default: false, source: "config", site: null, command: "chant run --components queue-runner --env staging" },
386
- { name: "prod", default: false, source: "config", site: null, command: "chant run --components queue-runner --env prod" },
388
+ { name: "local", default: true, source: "builtin", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner" },
389
+ { name: "staging", default: false, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env staging" },
390
+ { name: "prod", default: false, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env prod" },
387
391
  // The pattern pr-* is no environment by itself, and it covers the ledger's pr-42.
388
- { name: "pr-42", default: false, source: "ledger", site: null, command: "chant run --components queue-runner --env pr-42" },
392
+ { name: "pr-42", default: false, source: "ledger", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env pr-42" },
389
393
  ]);
390
394
  const jobs = g.members.find((m) => m.name === "jobs")!;
391
395
  expect(jobs.environmentReasons).toEqual([{ code: "environments-ledger-undeclared", message: expect.stringContaining("retired") }]);
@@ -397,7 +401,7 @@ describe("the environments each component may deploy to (#2695)", () => {
397
401
  // delivery has no _members/delivery, so the flat staging ledger is its; its config declares nothing, so anything goes.
398
402
  expect(g.components.find((c) => c.id === "delivery/edge")!.environments).toEqual([
399
403
  localEnv("edge"),
400
- { name: "staging", default: false, source: "ledger", site: null, command: "chant run --components edge --env staging" },
404
+ { name: "staging", default: false, source: "ledger", site: null, executor: NO_EXECUTOR, command: "chant run --components edge --env staging" },
401
405
  ]);
402
406
  expect(g.members.find((m) => m.name === "delivery")!.environmentReasons.map((r) => r.code)).toEqual(["environments-none-declared"]);
403
407
  expect(g.members.find((m) => m.name === "docs")!.environmentReasons).toEqual([]);
@@ -409,8 +413,8 @@ describe("the environments each component may deploy to (#2695)", () => {
409
413
  const g = result((await workspaceComposites({ cwd: root })).doc);
410
414
  expectValid(g);
411
415
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments).toEqual([
412
- { name: "local", default: true, source: "config", site: null, command: "chant run --components queue-runner" },
413
- { name: "prod", default: false, source: "config", site: null, command: "chant run --components queue-runner --env prod" },
416
+ { name: "local", default: true, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner" },
417
+ { name: "prod", default: false, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env prod" },
414
418
  ]);
415
419
  expect(g.members.find((m) => m.name === "jobs")!.environmentReasons).toEqual([]);
416
420
  });
@@ -445,4 +449,31 @@ describe("the environments each component may deploy to (#2695)", () => {
445
449
  expect(g.members.find((m) => m.name === "jobs")!.environmentReasons).toEqual([{ code: "environments-ledger-unreadable", message: "boom" }]);
446
450
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments.map((e) => e.name)).toEqual(["local", "prod"]);
447
451
  });
452
+
453
+ test("each environment names the forge workflow the config declares for it, a literal before a pattern (#4402)", async () => {
454
+ const root = declared();
455
+ writeFileSync(
456
+ join(root, "jobs", "chant.config.ts"),
457
+ 'export default { environments: ["staging", { name: "prod", executor: { forge: "github", workflow: "deploy-prod.yml" } }, { name: "pr-*", executor: { forge: "forgejo", workflow: "preview.yml" } }] };\n',
458
+ );
459
+ const g = result((await workspaceComposites({ cwd: root })).doc);
460
+ expectValid(g);
461
+ const executors = Object.fromEntries(g.components.find((c) => c.id === "jobs/queue-runner")!.environments.map((e) => [e.name, e.executor]));
462
+ expect(executors).toEqual({
463
+ local: NO_EXECUTOR,
464
+ staging: NO_EXECUTOR,
465
+ prod: { forge: "github", workflow: "deploy-prod.yml", reason: null },
466
+ "pr-42": { forge: "forgejo", workflow: "preview.yml", reason: null },
467
+ });
468
+ });
469
+
470
+ test("a config that can't be read says no executor is known (#4402)", async () => {
471
+ const root = declared();
472
+ writeFileSync(join(root, "jobs", "chant.config.ts"), 'throw new Error("no config here");\nexport default {};\n');
473
+ const g = result((await workspaceComposites({ cwd: root })).doc);
474
+ expectValid(g);
475
+ for (const e of g.components.find((c) => c.id === "jobs/queue-runner")!.environments) {
476
+ expect(e.executor).toEqual({ forge: null, workflow: null, reason: { code: "executor-config-unreadable", message: expect.stringContaining(e.name) } });
477
+ }
478
+ });
448
479
  });
@@ -36,7 +36,8 @@
36
36
  * And each component lists the environments it may deploy to (#2695,
37
37
  * `environments.ts`): `local`, the environments its member's config declares,
38
38
  * and those with a release in the member's ledger on `chant/lifecycle`, with
39
- * the command line for each.
39
+ * the command line for each, and the forge workflow that deploys each one
40
+ * when the member's config names it (#4402).
40
41
  */
41
42
 
42
43
  import { formatError } from "../cli/format";
@@ -50,7 +51,7 @@ import { emitDocument, type UnitResult } from "./member-commands";
50
51
  import { readerVersion, WORKSPACE_ERROR_CODES, type ErrorLocation, type WorkspaceErrorCode } from "./declaration";
51
52
  import type { ReasonCode } from "./reason-codes";
52
53
  import type { ComponentEnvironmentDeclaration } from "../components/component";
53
- import { componentEnvironments, ENVIRONMENT_REASON_CODES, memberEnvironments, readLedgerEnvironments, type ComponentEnvironment, type EnvironmentReason, type LedgerEnvironmentReader, type MemberEnvironments } from "./environments";
54
+ import { componentEnvironments, ENVIRONMENT_REASON_CODES, EXECUTOR_REASON_CODES, memberEnvironments, readLedgerEnvironments, type ComponentEnvironment, type EnvironmentReason, type LedgerEnvironmentReader, type MemberEnvironments } from "./environments";
54
55
  import { componentRuntimes, readRuntimesIn, RUNTIME_REASON_CODES, type ComponentRuntime, type MemberRuntimes, type PluginLoader, type RuntimeReason } from "./runtimes";
55
56
 
56
57
  /** `$id` of the JSON Schema for the document, shipped beside this file. */
@@ -83,6 +84,9 @@ export const COMPOSITES_RUNTIME_REASON_CODES = RUNTIME_REASON_CODES;
83
84
  /** Why a member's environments are only `local`, or leave out one its ledger has (#2695). Closed. */
84
85
  export const COMPOSITES_ENVIRONMENT_REASON_CODES = ENVIRONMENT_REASON_CODES;
85
86
 
87
+ /** Why an environment row's executor names no forge workflow (#4402). Closed. */
88
+ export const COMPOSITES_EXECUTOR_REASON_CODES = EXECUTOR_REASON_CODES;
89
+
86
90
  export interface CompositesMember {
87
91
  name: string;
88
92
  dir: string;
@@ -79,7 +79,7 @@
79
79
  "uniqueItems": true
80
80
  },
81
81
  "evidence": {
82
- "description": "The proof of done: public links, or workspace files pinned by the hash of their bytes (chant workspace records pin <path>). May be empty while the work is open; a done item with none gets the warning work-done-unpinned. An entry that names a criterion from acceptance carries the verification it is and its result, and a manual verdict names who gave it in by (#2772).",
82
+ "description": "The proof of done: public links, workspace files pinned by the hash of their bytes (chant workspace records pin <path>), or ops results on the chant/lifecycle branch (#3171). May be empty while the work is open; a done item with none gets the warning work-done-unpinned. An entry that names a criterion from acceptance carries the verification it is and its result, and a manual verdict names who gave it in by (#2772).",
83
83
  "type": "array",
84
84
  "items": {
85
85
  "oneOf": [
@@ -88,6 +88,9 @@
88
88
  },
89
89
  {
90
90
  "$ref": "#/definitions/evidencePin"
91
+ },
92
+ {
93
+ "$ref": "#/definitions/evidenceOps"
91
94
  }
92
95
  ]
93
96
  }
@@ -305,6 +308,107 @@
305
308
  ]
306
309
  }
307
310
  },
311
+ "evidenceOps": {
312
+ "description": "An ops result on the chant/lifecycle branch (#3171): a run, a deploy, a gate answer or a release plan, named the way graph --intent's trace names a link. chant workspace work evidence and the workEvidence activity check it against the ledger before writing it, and a cited run's outcome agrees with result.",
313
+ "type": "object",
314
+ "additionalProperties": false,
315
+ "required": [
316
+ "title",
317
+ "ops"
318
+ ],
319
+ "properties": {
320
+ "title": {
321
+ "type": "string",
322
+ "minLength": 1
323
+ },
324
+ "ops": {
325
+ "type": "object",
326
+ "additionalProperties": false,
327
+ "required": [
328
+ "ledger",
329
+ "ref",
330
+ "field",
331
+ "value",
332
+ "sha256"
333
+ ],
334
+ "properties": {
335
+ "ledger": {
336
+ "description": "Which ledger: an Op run, a deploy record, a gate answer or a release plan.",
337
+ "enum": [
338
+ "run",
339
+ "deploy",
340
+ "gate",
341
+ "plan"
342
+ ]
343
+ },
344
+ "ref": {
345
+ "description": "The ledger file, as chant/lifecycle:<path>.",
346
+ "type": "string",
347
+ "pattern": "^chant/lifecycle:"
348
+ },
349
+ "field": {
350
+ "description": "The field of the record that picks it out of the file, such as id, runId, timestamp or digest.",
351
+ "type": "string",
352
+ "minLength": 1
353
+ },
354
+ "value": {
355
+ "description": "That field's value.",
356
+ "type": "string",
357
+ "minLength": 1
358
+ },
359
+ "sha256": {
360
+ "description": "Hex SHA-256 of the record as the ledger stores it: the line, or the plan file.",
361
+ "type": "string",
362
+ "pattern": "^[0-9a-f]{64}$"
363
+ },
364
+ "env": {
365
+ "description": "The environment, for a run or a deploy.",
366
+ "type": "string"
367
+ },
368
+ "outcome": {
369
+ "description": "The run's status, for a run.",
370
+ "enum": [
371
+ "ok",
372
+ "fail"
373
+ ]
374
+ }
375
+ }
376
+ },
377
+ "as_of": {
378
+ "description": "When the result was cited, as an ISO 8601 timestamp.",
379
+ "type": [
380
+ "string",
381
+ "null"
382
+ ]
383
+ },
384
+ "criterion": {
385
+ "description": "The acceptance criterion this evidence is for, by id (#2772).",
386
+ "type": "string",
387
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
388
+ },
389
+ "verification": {
390
+ "$ref": "#/definitions/verification"
391
+ },
392
+ "result": {
393
+ "description": "Whether the evidence shows the criterion met.",
394
+ "enum": [
395
+ "pass",
396
+ "fail"
397
+ ]
398
+ },
399
+ "by": {
400
+ "description": "Who produced the evidence: the lease holder for a steward's, the person for a manual verdict.",
401
+ "type": "string",
402
+ "minLength": 1
403
+ }
404
+ },
405
+ "dependencies": {
406
+ "criterion": [
407
+ "verification",
408
+ "result"
409
+ ]
410
+ }
411
+ },
308
412
  "evidencePin": {
309
413
  "description": "A file in the workspace, pinned by the hash of its bytes. chant workspace records reports a file that changed or went missing since.",
310
414
  "type": "object",