@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
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Org scope (#3168): members of kind `repo`, which name another repository's
3
+ * workspace by URL and pinned revision, and the org-level workspace that
4
+ * holds only such members.
5
+ *
6
+ * | Id | Check |
7
+ * |---|---|
8
+ * | WSP013 | an org workspace holds only repo members |
9
+ * | WSP014 | every repository the forge lists is declared (with `--forge-repos`) |
10
+ * | WSP015 | every declared repository is one the forge lists (with `--forge-repos`) |
11
+ * | WSP016 | no repo member's URL carries a password or token |
12
+ *
13
+ * The forge's list is input, never fetched here: a warden enumerates the
14
+ * org's repositories from its own API and hands the list to
15
+ * `chant workspace check --forge-repos <file>`, so the check stays offline.
16
+ */
17
+
18
+ import { isAbsolute, resolve } from "node:path";
19
+ import { REPO_KIND } from "../kinds";
20
+ import type { WorkspaceCheck, WorkspaceDiagnostic } from "../checks";
21
+
22
+ /** One repository the forge lists. */
23
+ export interface ForgeRepo {
24
+ url: string;
25
+ /** An archived repository need not be declared, and declaring one is not a drop. */
26
+ archived: boolean;
27
+ }
28
+
29
+ /** The forge's repository list for `--forge-repos` (#3168), as a warden writes it. */
30
+ export interface ForgeRepoList {
31
+ /** The file it was read from, for messages. */
32
+ source: string;
33
+ repos: ForgeRepo[];
34
+ }
35
+
36
+ /**
37
+ * Parse a forge repository list. Three shapes are read: `{ "repos": [...] }`
38
+ * (a warden may add `forge` and `org` beside it), a bare array, and, for each
39
+ * repository, a URL string or `{ "url", "archived"? }`. Throws an Error that
40
+ * says what is wrong.
41
+ */
42
+ export function parseForgeRepos(text: string, source: string): ForgeRepoList {
43
+ let raw: unknown;
44
+ try {
45
+ raw = JSON.parse(text);
46
+ } catch (err) {
47
+ throw new Error(`${source} is not JSON (${(err as Error).message})`);
48
+ }
49
+ const list = Array.isArray(raw) ? raw : raw !== null && typeof raw === "object" && Array.isArray((raw as { repos?: unknown }).repos) ? (raw as { repos: unknown[] }).repos : undefined;
50
+ if (!list) throw new Error(`${source} is neither an array of repositories nor an object with a repos array`);
51
+ const repos = list.map((r, i): ForgeRepo => {
52
+ if (typeof r === "string" && r.length > 0) return { url: r, archived: false };
53
+ if (r !== null && typeof r === "object" && typeof (r as { url?: unknown }).url === "string") {
54
+ return { url: (r as { url: string }).url, archived: (r as { archived?: unknown }).archived === true };
55
+ }
56
+ throw new Error(`${source}: repos[${i}] is neither a URL nor an object with a url`);
57
+ });
58
+ return { source, repos };
59
+ }
60
+
61
+ /**
62
+ * A repository URL reduced to what names the repository, so the forge's
63
+ * https URL and a declaration's ssh or scp-style URL compare equal:
64
+ * `host/owner/repo`, lowercased, with no scheme, user, port, `.git` or
65
+ * trailing slash. A local path or file URL becomes its absolute path,
66
+ * resolved from `base`.
67
+ */
68
+ export function repoKey(url: string, base: string = process.cwd()): string {
69
+ const trimmed = url.trim();
70
+ const scp = /^(?:[^@/\s]+@)?([^:/\s]+):(?!\/\/)(.+)$/.exec(trimmed);
71
+ let hostPath: string | undefined;
72
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
73
+ let u: URL;
74
+ try {
75
+ u = new URL(trimmed);
76
+ } catch {
77
+ return trimmed.toLowerCase();
78
+ }
79
+ if (u.protocol === "file:") return stripGit(decodeURIComponent(u.pathname));
80
+ hostPath = `${u.hostname}${u.pathname}`;
81
+ } else if (scp && !isAbsolute(trimmed) && !/^[a-z]:[\\/]/i.test(trimmed)) {
82
+ hostPath = `${scp[1]}/${scp[2]}`;
83
+ } else {
84
+ return stripGit(resolve(base, trimmed));
85
+ }
86
+ return stripGit(hostPath.replace(/\/+/g, "/")).toLowerCase();
87
+ }
88
+
89
+ function stripGit(path: string): string {
90
+ return path.replace(/\/+$/, "").replace(/\.git$/, "");
91
+ }
92
+
93
+ /** Whether a URL carries a password or token, which a declaration must never hold. */
94
+ export function urlCredential(url: string): boolean {
95
+ if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(url)) return false;
96
+ try {
97
+ const u = new URL(url);
98
+ // ssh://git@host is a user name, not a secret; a password, or any user on http(s), is one.
99
+ return u.password !== "" || (u.username !== "" && /^https?:$/.test(u.protocol));
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
104
+
105
+ const repoMembers = (ctx: Parameters<WorkspaceCheck["check"]>[0]) => ctx.declaration.members.filter((m) => m.kind === REPO_KIND && m.repo !== null);
106
+
107
+ function finding(check: WorkspaceCheck, message: string, pointer: string, entity?: string): WorkspaceDiagnostic {
108
+ return { checkId: check.id, severity: check.severity, message, pointer, ...(entity !== undefined ? { entity } : {}) };
109
+ }
110
+
111
+ export const ORG_CHECKS: readonly WorkspaceCheck[] = [
112
+ {
113
+ id: "WSP013",
114
+ name: "org-member-local",
115
+ description: "An org workspace (scope org) holds only members of kind repo, each a reference to another repository's workspace (#3168).",
116
+ severity: "error",
117
+ configurable: false,
118
+ check(ctx) {
119
+ if (ctx.declaration.scope !== "org") return [];
120
+ return ctx.declaration.entries
121
+ .filter((e) => e.kind !== REPO_KIND)
122
+ .map((e) =>
123
+ finding(this, `${e.type === "group" ? "example group" : "member"} ${e.name} is kind ${e.kind}, and an org workspace holds only repo members; move it into a repository's own workspace and reference that`, `${e.pointer}/kind`, e.name),
124
+ );
125
+ },
126
+ },
127
+ {
128
+ id: "WSP014",
129
+ name: "org-repo-undeclared",
130
+ description: "With --forge-repos, every repository the forge lists, archived ones aside, is named by a repo member (#3168).",
131
+ severity: "error",
132
+ configurable: true,
133
+ check(ctx) {
134
+ const forge = ctx.facts?.forgeRepos;
135
+ if (!forge) return [];
136
+ const declared = new Set(repoMembers(ctx).map((m) => repoKey(m.repo!.url)));
137
+ return forge.repos
138
+ .filter((r) => !r.archived && !declared.has(repoKey(r.url)))
139
+ .map((r) => finding(this, `the forge lists ${r.url} (${forge.source}), and no repo member names it; add a member of kind repo with its url and pinned rev`, "/members"));
140
+ },
141
+ },
142
+ {
143
+ id: "WSP015",
144
+ name: "org-repo-dropped",
145
+ description: "With --forge-repos, every repository a repo member names is one the forge still lists (#3168).",
146
+ severity: "warning",
147
+ configurable: true,
148
+ check(ctx) {
149
+ const forge = ctx.facts?.forgeRepos;
150
+ if (!forge) return [];
151
+ const listed = new Set(forge.repos.map((r) => repoKey(r.url)));
152
+ return repoMembers(ctx)
153
+ .filter((m) => !listed.has(repoKey(m.repo!.url)))
154
+ .map((m) => finding(this, `member ${m.name} names ${m.repo!.url}, which the forge no longer lists (${forge.source}); remove the member or say why it stays`, `${m.pointer}/repo/url`, m.name));
155
+ },
156
+ },
157
+ {
158
+ id: "WSP016",
159
+ name: "repo-url-credential",
160
+ description: "No repo member's URL carries a password or token: the declaration is committed, and git's own credential helpers supply access (#3168).",
161
+ severity: "error",
162
+ configurable: false,
163
+ check(ctx) {
164
+ return repoMembers(ctx)
165
+ .filter((m) => urlCredential(m.repo!.url))
166
+ .map((m) => finding(this, `member ${m.name}'s repo url carries a credential; name the repository without one and let git's credential helper supply access`, `${m.pointer}/repo/url`, m.name));
167
+ },
168
+ },
169
+ ];
@@ -21,7 +21,8 @@
21
21
  *
22
22
  * | Ids | Checks |
23
23
  * |---|---|
24
- * | WSP001 to WSP011 | the declaration and member kinds (this file) |
24
+ * | WSP001 to WSP012 | the declaration and member kinds (this file), and a member's file against its kind's schema (#3672) |
25
+ * | WSP013 to WSP016 | org scope: repo members and the org workspace, checked against the forge's list with `--forge-repos` (#3168) |
25
26
  * | WSP071, WSP072 | member ledgers |
26
27
  * | WSP081 to WSP083 | recorded pipelines |
27
28
  * | WSP091 to WSP099 | member links (#2539, #2558), and those a kind's reader finds in a member's files (#3932) |
@@ -38,6 +39,8 @@
38
39
  * | WSP141, WSP142 | member links resolved against a live read, with `--live` (#2549) |
39
40
  * | WSP131 to WSP133 | diagram artifacts: source and render exist, a recorded source hash still matches (#2764) |
40
41
  * | WSP151 | what a member under `ci.workflow` still lacks (#4168 step 6) |
42
+ * | WSP152 | a member's generated pipeline targets the branch `ci.branches` routes it to (#4325) |
43
+ * | WSP153 | an Op gate's approval block that `identity.gates` does not set at base (#4404) |
41
44
  * | WSP161 to WSP165 | the security groups the declaration opts into: what each still lacks (#4168 step 7) |
42
45
  */
43
46
 
@@ -48,7 +51,7 @@ import type { PostSynthDiagnostic } from "../lint/post-synth";
48
51
  import { readDeclaration, resolveGroups, WorkspaceReadError, type Declaration, type Entry, type ErrorLocation, type ResolvedGroup } from "./declaration";
49
52
  import type { ReasonCode } from "./reason-codes";
50
53
  import { parseJsonText, pointerToken, type TextLocation } from "./jsonc";
51
- import { loadKindRegistry, probeKind, resolveKind, resolveMemberFields, type KindLoadProblem, type KindRegistry } from "./kinds";
54
+ import { checkMemberFile, loadKindRegistry, probeKind, REPO_KIND, resolveKind, resolveMemberFields, type KindLoadProblem, type KindRegistry } from "./kinds";
52
55
  import { coreClassRegistry, loadClassRegistry, unknownScopeClasses, type ClassRegistry } from "./principal-classes";
53
56
  import { gitTop, workingTree, type WorkspaceTree } from "./tree";
54
57
  import { LINK_CHECKS, linkTable } from "./checks/links";
@@ -62,9 +65,13 @@ import { BOX_CHECKS } from "./checks/boxes";
62
65
  import { DIAGRAM_CHECKS } from "./checks/diagrams";
63
66
  import { SECURITY_CHECKS } from "./checks/security";
64
67
  import { CI_WORKFLOW_CHECKS } from "./checks/ci-workflow";
68
+ import { CI_BRANCH_CHECKS } from "./checks/ci-branch";
69
+ import { GATE_APPROVAL_CHECKS } from "./checks/gate-approval";
70
+ import { ORG_CHECKS, type ForgeRepoList } from "./checks/org";
65
71
  import { loadDeclaredKinds, type DeclaredKind } from "./declared-kinds";
66
72
  import { resolveBoxIntents, type ResolvedBoxIntent } from "./box-intent";
67
- import { gatherLexiconOutputs, type MemberLexiconOutputs } from "./lexicon-outputs";
73
+ import { gatherLexiconLinks, gatherLexiconOutputs, type MemberLexiconOutputs } from "./lexicon-outputs";
74
+ import type { KindLinkRead } from "./kind-link-readers";
68
75
 
69
76
  /**
70
77
  * What the checks beyond the declaration read, gathered from the checkout
@@ -88,6 +95,10 @@ export interface WorkspaceFacts {
88
95
  boxIntents?: readonly ResolvedBoxIntent[];
89
96
  /** The outputs linked members' lexicons report about their declarations, read by folding their source (#3931). */
90
97
  lexiconOutputs?: ReadonlyMap<string, MemberLexiconOutputs>;
98
+ /** The links members' lexicons read from their declarations, such as an otel clickhouse exporter's tables (#4397). */
99
+ lexiconLinks?: ReadonlyMap<string, KindLinkRead>;
100
+ /** The forge's repository list, from `--forge-repos` (#3168). */
101
+ forgeRepos?: ForgeRepoList;
91
102
  }
92
103
 
93
104
  /** What every declaration check reads. */
@@ -174,9 +185,9 @@ function entryFinding(check: WorkspaceCheck, entry: Entry, field: string | null,
174
185
  };
175
186
  }
176
187
 
177
- /** The dir exists and the kind is known: the members every probe check reads. */
188
+ /** The dir exists and the kind is known: the members every probe check reads. A repo member has no directory here (#3168). */
178
189
  function readableMembers(ctx: WorkspaceCheckContext) {
179
- return ctx.declaration.members.filter((m) => ctx.tree.stat(memberDir(m.dir)) === "dir" && ctx.kinds.get(m.kind) !== undefined);
190
+ return ctx.declaration.members.filter((m) => m.kind !== REPO_KIND && ctx.tree.stat(memberDir(m.dir)) === "dir" && ctx.kinds.get(m.kind) !== undefined);
180
191
  }
181
192
 
182
193
  /** The first id of the read failure, used by {@link runDeclarationChecks} when the declaration can't be read. */
@@ -240,7 +251,7 @@ export const WORKSPACE_CHECKS: readonly WorkspaceCheck[] = [
240
251
  configurable: true,
241
252
  check(ctx) {
242
253
  return ctx.declaration.members
243
- .filter((m) => ctx.tree.stat(memberDir(m.dir)) !== "dir")
254
+ .filter((m) => m.kind !== REPO_KIND && ctx.tree.stat(memberDir(m.dir)) !== "dir")
244
255
  .map((m) => entryFinding(this, m, "dir", `member ${m.name}'s directory ${m.dir} does not exist${ctx.tree.label}`));
245
256
  },
246
257
  },
@@ -359,6 +370,43 @@ export const WORKSPACE_CHECKS: readonly WorkspaceCheck[] = [
359
370
  return out;
360
371
  },
361
372
  },
373
+ {
374
+ id: "WSP012",
375
+ name: "member-file-invalid",
376
+ description: "Every member of a kind that names a member file has that file, and it passes the kind's JSON Schema (#3672).",
377
+ severity: "error",
378
+ configurable: true,
379
+ check(ctx) {
380
+ const out: WorkspaceDiagnostic[] = [];
381
+ for (const m of readableMembers(ctx)) {
382
+ const kind = ctx.kinds.get(m.kind)!;
383
+ const mf = kind.memberFile;
384
+ if (!mf) continue;
385
+ const file = memberDir(m.dir) === "" ? mf.path : `${memberDir(m.dir)}/${mf.path}`;
386
+ if (!mf.validate) {
387
+ out.push(entryFinding(this, m, "kind", `member ${m.name}'s ${mf.path} can't be checked: kind ${m.kind}'s schema ${mf.schema} did not load (see WSP002)`));
388
+ continue;
389
+ }
390
+ if (ctx.tree.stat(file) !== "file") {
391
+ out.push(entryFinding(this, m, "dir", `member ${m.name} (${m.dir}) has no ${mf.path}, which kind ${m.kind} requires${ctx.tree.label}`));
392
+ continue;
393
+ }
394
+ let text: string;
395
+ try {
396
+ text = ctx.tree.read(file);
397
+ } catch (err) {
398
+ out.push(entryFinding(this, m, "dir", `member ${m.name}'s ${mf.path} can't be read: ${(err as Error).message}`));
399
+ continue;
400
+ }
401
+ for (const p of checkMemberFile(mf, mf.path, text)) {
402
+ out.push({ ...entryFinding(this, m, null, `member ${m.name}'s ${mf.path} fails kind ${m.kind}'s schema ${mf.schema}: ${p.message}`), treeFile: file, line: 1, column: 1 });
403
+ }
404
+ }
405
+ return out;
406
+ },
407
+ },
408
+ // Org scope (#3168), WSP013 to WSP016.
409
+ ...ORG_CHECKS,
362
410
  // Member ledgers (#2538).
363
411
  ...LEDGER_CHECKS,
364
412
  // Recorded pipelines (#2542).
@@ -377,6 +425,10 @@ export const WORKSPACE_CHECKS: readonly WorkspaceCheck[] = [
377
425
  ...LIVE_CHECKS,
378
426
  // What a member under ci.workflow still lacks (#4168 step 6), WSP151.
379
427
  ...CI_WORKFLOW_CHECKS,
428
+ // A member's pipeline targets the branch ci.branches routes it to (#4325), WSP152.
429
+ ...CI_BRANCH_CHECKS,
430
+ // An Op gate's approval block that identity.gates does not set at base (#4404), WSP153.
431
+ ...GATE_APPROVAL_CHECKS,
380
432
  // The security groups the declaration opts into (#4168 step 7), WSP161 to WSP165.
381
433
  ...SECURITY_CHECKS,
382
434
  ];
@@ -463,6 +515,8 @@ export interface DeclarationCheckOptions {
463
515
  * account now. `onStderr` gets each member's stderr.
464
516
  */
465
517
  live?: { env: string; onStderr?: (text: string) => void };
518
+ /** The forge's repository list, from `--forge-repos` (#3168), for WSP014 and WSP015. Read under `--at` too: it is input, not the tree. */
519
+ forgeRepos?: ForgeRepoList;
466
520
  }
467
521
 
468
522
  /**
@@ -534,12 +588,19 @@ export async function runDeclarationChecks(
534
588
  // A box's intent is read from the working tree's records, so not under --at (#2850).
535
589
  const boxIntents = declaredKinds && !options.tree ? await resolveBoxIntents(declaration, root, declaredKinds) : undefined;
536
590
  // A linked member's lexicons name its tables and columns as outputs (#3931); reading them folds its source, never runs it.
591
+ // A member's lexicons may also read other members' outputs from its declarations (#4397); their targets are read too.
592
+ const outputsTree = options.tree ? workingTree(root) : tree;
593
+ const lexiconLinks = options.gather === false ? undefined : await gatherLexiconLinks(root, declaration, outputsTree, groups, registry, { atRevision: options.tree !== undefined });
537
594
  const lexiconOutputs =
538
- options.gather === false ? undefined : await gatherLexiconOutputs(root, declaration, options.tree ? workingTree(root) : tree, groups, registry, { atRevision: options.tree !== undefined });
595
+ options.gather === false
596
+ ? undefined
597
+ : await gatherLexiconOutputs(root, declaration, outputsTree, groups, registry, { atRevision: options.tree !== undefined, ...(lexiconLinks ? { lexiconLinks } : {}) });
539
598
  const facts: WorkspaceFacts = {
540
599
  ...(lexiconOutputs && lexiconOutputs.size > 0 ? { lexiconOutputs } : {}),
600
+ ...(lexiconLinks && lexiconLinks.size > 0 ? { lexiconLinks } : {}),
541
601
  ...gathered,
542
602
  ...(options.records ? { records: options.records } : {}),
603
+ ...(options.forgeRepos ? { forgeRepos: options.forgeRepos } : {}),
543
604
  ...(declaredKinds ? { declaredKinds } : {}),
544
605
  ...(boxIntents ? { boxIntents } : {}),
545
606
  ...(options.live && !options.tree ? { live: await gatherLiveLinkFacts(root, declaration, registry, options.live.env, options.live.onStderr) } : {}),
@@ -40,6 +40,37 @@ export function routeConflicts(branches: MemberGlobs | undefined, members: reado
40
40
  return members.map((member) => ({ member, branches: keysMatching(branches, member) })).filter((c) => c.branches.length > 1);
41
41
  }
42
42
 
43
+ /**
44
+ * The branches `chant ci serve` polls (#4325), and the ones among them a job
45
+ * is told with `--branch`.
46
+ *
47
+ * - A routed member polls its route alone. A `named` branch that differs
48
+ * from the route is refused, as `chant build --pr-loop` refuses it.
49
+ * - An unrouted member polls `fallback`, which may not be a routed branch.
50
+ * - Where no member is in play (the workspace root), `fallback` and then each
51
+ * routed branch, so every branch a member applies from is watched.
52
+ *
53
+ * `fallback` is the branch `--branch` names, else the default branch.
54
+ */
55
+ export function servedBranches(
56
+ branches: MemberGlobs | undefined,
57
+ member: string | undefined,
58
+ fallback: string,
59
+ named: boolean,
60
+ ): { served: string[]; routed: string[] } {
61
+ const keys = Object.keys(branches ?? {});
62
+ if (member === undefined) return { served: [fallback, ...keys.filter((k) => k !== fallback)], routed: keys };
63
+ const own = routedBranch(branches, member);
64
+ if (own !== null) {
65
+ if (named && fallback !== own) throw new Error(`--branch ${fallback}: ci.branches routes member ${member} to ${own}, so ci serve polls ${own}`);
66
+ return { served: [own], routed: [own] };
67
+ }
68
+ if (keys.includes(fallback)) {
69
+ throw new Error(`--branch ${fallback}: ci.branches applies only the members its globs match from ${fallback}, and member ${member} is not one`);
70
+ }
71
+ return { served: [fallback], routed: [] };
72
+ }
73
+
43
74
  /**
44
75
  * Whether a push to `pushed` applies `member`, with a line that says why.
45
76
  * A routed member applies only from its branch. Any other member applies
@@ -0,0 +1,153 @@
1
+ /**
2
+ * #4325: `ci.green` names several branches when `ci.branches` routes members
3
+ * to branches of their own. Each is judged as the single branch always was, a
4
+ * one-branch declaration reads as before, and `release cut` takes a commit of
5
+ * any of them.
6
+ */
7
+
8
+ import { afterAll, describe, expect, test } from "vitest";
9
+ import { greenBranches, parseDeclaration } from "./declaration";
10
+ import { cleanScratch, declaration, git, scratchDir, writeFiles } from "./__fixtures__/contract-repo";
11
+ import { ciList, ciTick, formatTick, greenOn, lastGreen } from "./ci-green";
12
+ import type { CheckRun, CiForge } from "./ci-green-forge";
13
+ import { buildTriggerEdits, renderCiGreenWorkflow } from "./ci-green-workflow";
14
+ import { checkOnGreenBranches, checkOnTrunk, resolveTrunk, WorkstationCheckError } from "../lifecycle/release-workstation";
15
+
16
+ afterAll(cleanScratch);
17
+
18
+ const PHASES = { test: ["test"] };
19
+ const decl = (green: unknown) => JSON.stringify({ name: "w", schema: 1, members: [{ name: "app", dir: "app", kind: "other", because: "x" }], ci: { green } });
20
+ const parse = (branch: unknown) => parseDeclaration(decl({ branch, phases: PHASES, require: ["test"] }), "chant.workspace.json").ci!.green!;
21
+
22
+ describe("ci.green.branch as one branch or several (#4325)", () => {
23
+ test("a string reads exactly as it did: no branches field", () => {
24
+ const g = parse("main");
25
+ expect(g.branch).toBe("main");
26
+ expect("branches" in g).toBe(false);
27
+ expect(greenBranches(g)).toEqual(["main"]);
28
+ // A list of one is the same declaration.
29
+ expect(parse(["main"])).toEqual(g);
30
+ });
31
+
32
+ test("a list judges each branch, and the first is the trunk readers use", () => {
33
+ const g = parse(["main", "release"]);
34
+ expect(g.branch).toBe("main");
35
+ expect(g.branches).toEqual(["main", "release"]);
36
+ expect(greenBranches(g)).toEqual(["main", "release"]);
37
+ });
38
+
39
+ test("an empty list, a repeated branch and a malformed name are refused", () => {
40
+ for (const bad of [[], ["main", "main"], ["main", "has space"], [3]]) {
41
+ expect(() => parse(bad), JSON.stringify(bad)).toThrow();
42
+ }
43
+ });
44
+
45
+ test("greenOn picks a branch ci.green names and refuses one it doesn't", () => {
46
+ const g = parse(["main", "release"]);
47
+ expect(greenOn(g, undefined).branch).toBe("main");
48
+ expect(greenOn(g, "release").branch).toBe("release");
49
+ expect("branches" in greenOn(g, "release")).toBe(false);
50
+ expect(() => greenOn(g, "staging")).toThrow(/judges main, release, not staging/);
51
+ });
52
+ });
53
+
54
+ // ── The tick, against a real remote ──────────────────────────────────────────
55
+
56
+ const NOW = new Date();
57
+ const run = (id: number, name: string, conclusion: string): CheckRun => ({ id, name, status: "completed", conclusion, completedAt: null, url: `https://example.com/runs/${id}` });
58
+
59
+ function commit(root: string, file: string): string {
60
+ writeFiles(root, { [file]: `${file}\n` });
61
+ git(root, "add", "-A");
62
+ git(root, "commit", "-q", "-m", file);
63
+ return git(root, "rev-parse", "HEAD");
64
+ }
65
+
66
+ /** A checkout with `main` and `release` pushed to a bare origin, one commit each past a shared base. */
67
+ function twoBranches(green: unknown): { root: string; origin: string; main: string; release: string } {
68
+ const origin = scratchDir("chant-ci-branches-origin-");
69
+ git(origin, "init", "-q", "--bare");
70
+ const root = scratchDir("chant-ci-branches-work-");
71
+ git(root, "init", "-q");
72
+ git(root, "symbolic-ref", "HEAD", "refs/heads/main");
73
+ writeFiles(root, { "chant.workspace.json": declaration([{ name: "app", dir: "app", kind: "other", because: "the app" }], { ci: { green } }) });
74
+ git(root, "remote", "add", "origin", origin);
75
+ commit(root, "app/base.txt");
76
+ git(root, "checkout", "-q", "-b", "release");
77
+ const release = commit(root, "app/release.txt");
78
+ git(root, "checkout", "-q", "main");
79
+ const main = commit(root, "app/main.txt");
80
+ git(root, "push", "-q", "origin", "main", "release");
81
+ return { root, origin, main, release };
82
+ }
83
+
84
+ const remoteTags = (origin: string) => git(origin, "for-each-ref", "--format=%(refname:lstrip=2)", "refs/tags/").split("\n").filter(Boolean).sort();
85
+ const forge = (shas: string[]): CiForge => ({ kind: "github", checkRuns: async (sha) => (shas.includes(sha) ? [run(1, "test", "success")] : []) });
86
+
87
+ describe("the tick judges each branch ci.green names (#4325)", () => {
88
+ test("a commit of a routed branch gets a green tag, and last-green reads each branch", async () => {
89
+ const { root, origin, main, release } = twoBranches({ branch: ["main", "release"], phases: PHASES, require: ["test"] });
90
+ const result = await ciTick({ cwd: root, forge: forge([main, release]), now: NOW });
91
+ expect(result.branch).toBe("main");
92
+ expect(result.routed?.map((r) => r.branch)).toEqual(["release"]);
93
+ expect(result.made.sort()).toEqual([`ci/green/${main}`, `ci/green/${release}`].sort());
94
+ expect(remoteTags(origin)).toEqual([`ci/green/${main}`, `ci/green/${release}`].sort());
95
+ // The tag says which branch it judged.
96
+ const note = git(origin, "tag", "-l", "--format=%(contents)", `ci/green/${release}`);
97
+ expect(JSON.parse(note.slice(note.indexOf("{")))).toMatchObject({ tag: "green", commit: release, branch: "release" });
98
+
99
+ expect((lastGreen({ cwd: root }) as { commit: { sha: string } }).commit.sha).toBe(main);
100
+ const onRelease = lastGreen({ cwd: root, branch: "release" }) as { branch: string; commit: { sha: string } };
101
+ expect([onRelease.branch, onRelease.commit.sha]).toEqual(["release", release]);
102
+ expect(lastGreen({ cwd: root, branch: "staging" })).toMatchObject({ error: { code: "ci-branch-unknown" } });
103
+
104
+ const listed = await ciList({ cwd: root, branch: "release", now: NOW });
105
+ expect("commits" in listed && listed.commits.filter((c) => c.state === "green").map((c) => c.sha)).toEqual([release]);
106
+
107
+ const text = formatTick(result);
108
+ expect(text).toContain("main, commits within 24h");
109
+ expect(text).toContain("release, commits within 24h");
110
+ });
111
+
112
+ test("a one-branch declaration ticks one branch, with no routed results", async () => {
113
+ const { root, main, release } = twoBranches({ branch: "main", phases: PHASES, require: ["test"] });
114
+ const result = await ciTick({ cwd: root, forge: forge([main, release]), now: NOW });
115
+ expect(result.routed).toBeUndefined();
116
+ expect(result.made).toEqual([`ci/green/${main}`]);
117
+ expect(lastGreen({ cwd: root, branch: "release" })).toMatchObject({ error: { code: "ci-branch-unknown" } });
118
+ });
119
+ });
120
+
121
+ describe("the generated tick workflow follows the branches (#4325)", () => {
122
+ const green = (branch: string | string[]) => parse(branch);
123
+
124
+ test("workflow_run lists every branch, and one branch renders as before", () => {
125
+ const render = (g: ReturnType<typeof green>) => renderCiGreenWorkflow({ green: g, workflows: ["ci"], chantVersion: "0.0.0", workspaceRoot: ".", install: undefined, chant: undefined, npmLock: undefined, push: undefined });
126
+ const many = render(green(["main", "release"]));
127
+ expect(many).toMatch(/branches:\n\s+- "?main"?\n\s+- "?release"?\n/);
128
+ // The checkout stays on the first branch, which holds the declaration the tick reads.
129
+ expect(many).toMatch(/ref: "?main"?/);
130
+ expect(render(green("main"))).not.toContain("release");
131
+ });
132
+
133
+ test("a push-triggered build lists every branch", () => {
134
+ const sources = [{ path: ".github/workflows/ci.yml", text: "name: ci\non:\n push:\n branches: [main]\njobs:\n test:\n runs-on: ubuntu-latest\n steps: []\n" }];
135
+ const g = parseDeclaration(decl({ branch: ["main", "release"], phases: { test: { runs: ["test"], build: "newest" } }, require: ["test"] }), "chant.workspace.json").ci!.green!;
136
+ const [edit] = buildTriggerEdits(g, sources as never, { compose: false });
137
+ expect(edit!.text).toMatch(/branches:\n\s+- main\n\s+- release/);
138
+ });
139
+ });
140
+
141
+ describe("release cut takes a commit of any branch ci.green names (#4325)", () => {
142
+ test("a routed branch's commit is on trunk for the cut, and an unrelated one is not", () => {
143
+ const { root, main, release } = twoBranches({ branch: ["main", "release"], phases: PHASES, require: ["test"] });
144
+ git(root, "fetch", "-q", "origin");
145
+ const trunk = resolveTrunk(root, "main");
146
+ expect(() => checkOnTrunk(root, main, trunk)).not.toThrow();
147
+ expect(() => checkOnTrunk(root, release, trunk)).toThrow(WorkstationCheckError);
148
+ expect(checkOnGreenBranches(root, main, trunk, ["release"]).branch).toBe("main");
149
+ expect(checkOnGreenBranches(root, release, trunk, ["release"]).branch).toBe("release");
150
+ // Without the routed branch listed, the first branch's refusal stands.
151
+ expect(() => checkOnGreenBranches(root, release, trunk, [])).toThrow(WorkstationCheckError);
152
+ });
153
+ });
@@ -32,12 +32,12 @@ import { findWorkspaceRoot } from "../project-root";
32
32
  import { ciList, ciTick, CiGreenError, formatList, formatTick, lastGreen, RUNNER_KEY_ENV } from "./ci-green";
33
33
  import { ciForgeFromEnv } from "./ci-green-forge";
34
34
  import { ACTIONS_NAME, buildTriggerEdits, CI_GREEN_WORKFLOW_FILE, ciGreenHeader, type PushToken, readWorkflowSources, renderCiGreenWorkflow, workflowsHolding, writeWorkflowFile } from "./ci-green-workflow";
35
- import { readDeclaration, readerVersion, WorkspaceReadError, type CiSource } from "./declaration";
35
+ import { greenBranches, readDeclaration, readerVersion, WorkspaceReadError, type CiSource } from "./declaration";
36
36
  import { recordGeneratedFiles, resolveMemberContext, shellArg } from "./member-pipeline";
37
37
  import { gitTop, workingTree } from "./tree";
38
38
 
39
39
  const USAGE =
40
- "chant ci last-green [--json] [--from <rev>] | ci list [--json] [--limit <n>] [--since <rev>] [--forge github] [--source checks|runs] | ci tick [--dry-run] [--forge github] [--source checks|runs] [--key <runner key.pem>] | " +
40
+ "chant ci last-green [--json] [--from <rev>] [--branch <branch>] | ci list [--json] [--branch <branch>] [--limit <n>] [--since <rev>] [--forge github] [--source checks|runs] | ci tick [--dry-run] [--forge github] [--source checks|runs] [--key <runner key.pem>] | " +
41
41
  "ci workflow [--workflow <name>]... [--output <file>] [--chant <command>] [--install <command>] [--token-secret <NAME> | --app-id-var <VAR> --app-key-secret <NAME>] [--runner-key-secret <NAME>]";
42
42
 
43
43
  export { RUNNER_KEY_ENV };
@@ -56,7 +56,7 @@ function warnIgnored(ignored: readonly { tag: string; status: string; reason: st
56
56
  }
57
57
 
58
58
  export async function runCiLastGreen(ctx: CommandContext): Promise<number> {
59
- const doc = lastGreen({ cwd: process.cwd(), ...(ctx.args.migrateFrom !== undefined ? { from: ctx.args.migrateFrom } : {}) });
59
+ const doc = lastGreen({ cwd: process.cwd(), ...(ctx.args.branch !== undefined ? { branch: ctx.args.branch } : {}), ...(ctx.args.migrateFrom !== undefined ? { from: ctx.args.migrateFrom } : {}) });
60
60
  if (ctx.args.json) {
61
61
  console.log(JSON.stringify(doc, null, 2));
62
62
  return "error" in doc ? 1 : 0;
@@ -81,7 +81,7 @@ export async function runCiList(ctx: CommandContext): Promise<number> {
81
81
  // Only an explicit --forge reaches the network; without it the list reads local refs and tags.
82
82
  const forge = ctx.args.forge !== undefined ? ciForgeFromEnv(ctx.args.forge) : undefined;
83
83
  const source = sourceOf(ctx.args);
84
- const doc = await ciList({ cwd: process.cwd(), limit, since: ctx.args.since, forge, ...(source ? { source } : {}) });
84
+ const doc = await ciList({ cwd: process.cwd(), ...(ctx.args.branch !== undefined ? { branch: ctx.args.branch } : {}), limit, since: ctx.args.since, forge, ...(source ? { source } : {}) });
85
85
  if (ctx.args.json) {
86
86
  console.log(JSON.stringify(doc, null, 2));
87
87
  return "error" in doc ? 1 : 0;
@@ -185,14 +185,14 @@ export async function runCiWorkflow(ctx: CommandContext): Promise<number> {
185
185
  const yaml = renderCiGreenWorkflow({ green, workflows, chantVersion: readerVersion(), workspaceRoot, chant: ctx.args.ciChant, install: ctx.args.ciInstall, npmLock, push, ...(runnerKeySecret ? { runnerKeySecret } : {}), ...(compose ? { compose } : {}) });
186
186
  const written = writeWorkflowFile(target, ciGreenHeader(command, member ? "member" : "root") + yaml);
187
187
  if (member) recordGeneratedFiles(member.memberRoot, [{ path: fromRepo, command }]);
188
- console.log(`Wrote ${written}: ticks when ${workflows.join(", ")} complete${workflows.length === 1 ? "s" : ""} on ${green.branch}${compose ? " or a chant/compose/ branch" : ""}, and every 15 minutes.`);
188
+ console.log(`Wrote ${written}: ticks when ${workflows.join(", ")} complete${workflows.length === 1 ? "s" : ""} on ${greenBranches(green).join(", ")}${compose ? " or a chant/compose/ branch" : ""}, and every 15 minutes.`);
189
189
  for (const e of edits) {
190
190
  if (!e.changed) continue;
191
191
  const path = writeWorkflowFile(join(repoRoot, e.path), e.text);
192
192
  console.log(
193
193
  e.build === "newest"
194
- ? `Wrote ${path}'s triggers and concurrency: ${e.phases.join(", ")} build${e.phases.length === 1 ? "s" : ""} the newest commit on ${green.branch} when a run ends, and a ci/build/<sha> tag by hand.`
195
- : `Wrote ${path}'s triggers and concurrency: ${e.phases.join(", ")} build${e.phases.length === 1 ? "s" : ""} every commit on ${green.branch}, none cancelled, and a ci/build/<sha> tag by hand when the forge ran nothing.`,
194
+ ? `Wrote ${path}'s triggers and concurrency: ${e.phases.join(", ")} build${e.phases.length === 1 ? "s" : ""} the newest commit on ${greenBranches(green).join(", ")} when a run ends, and a ci/build/<sha> tag by hand.`
195
+ : `Wrote ${path}'s triggers and concurrency: ${e.phases.join(", ")} build${e.phases.length === 1 ? "s" : ""} every commit on ${greenBranches(green).join(", ")}, none cancelled, and a ci/build/<sha> tag by hand when the forge ran nothing.`,
196
196
  );
197
197
  }
198
198
  return 0;
@@ -14,6 +14,7 @@ import { runCiWorkflow } from "./ci-green-cli";
14
14
  import { BUILD_BLOCK_MARKER, buildTriggerEdits, CI_GREEN_WORKFLOW_FILE, dispatchableWorkflows, jobCheckRuns, renderCiGreenWorkflow, workflowsHolding, writeTopLevel } from "./ci-green-workflow";
15
15
  import { parseDeclaration } from "./declaration";
16
16
  import { cleanScratch, declaration, git, scratchDir, writeFiles } from "./__fixtures__/contract-repo";
17
+ import { ACTION_PINS, usesRef } from "../action-pins";
17
18
 
18
19
  afterAll(cleanScratch);
19
20
 
@@ -82,7 +83,7 @@ describe("the workflows that hold required check runs (#3573)", () => {
82
83
  expect(doc.concurrency).toEqual({ group: "chant-ci-green", "cancel-in-progress": false });
83
84
  expect(doc.permissions).toEqual({ contents: "write", checks: "read" });
84
85
  const steps = doc.jobs.tick.steps;
85
- expect(steps[0]).toEqual({ uses: "actions/checkout@v6", with: { ref: "main", "fetch-depth": 0 } });
86
+ expect(steps[0]).toEqual({ uses: usesRef(ACTION_PINS.checkout6), with: { ref: "main", "fetch-depth": 0 } });
86
87
  expect(steps[2].run).toBe("npx --yes @intentius/chant@0.108.0 ci tick");
87
88
  expect(steps[2]["working-directory"]).toBeUndefined();
88
89
  expect(steps[2].env.GITHUB_TOKEN).toBe("${{ github.token }}");
@@ -128,7 +129,7 @@ describe("the token the tick pushes its tags with (#3573)", () => {
128
129
  const plain = renderCiGreenWorkflow(base);
129
130
  const text = renderCiGreenWorkflow({ ...base, push: { secret: "CI_GREEN_TOKEN" } });
130
131
  const steps = (parseYAMLDocument(text) as Yaml).jobs.tick.steps;
131
- expect(steps[0]).toEqual({ uses: "actions/checkout@v6", with: { ref: "main", "fetch-depth": 0, token: "${{ secrets.CI_GREEN_TOKEN || github.token }}" } });
132
+ expect(steps[0]).toEqual({ uses: usesRef(ACTION_PINS.checkout6), with: { ref: "main", "fetch-depth": 0, token: "${{ secrets.CI_GREEN_TOKEN || github.token }}" } });
132
133
  // Check runs are still read with the Actions token: a fine-grained token can't call the Checks API.
133
134
  expect(steps[2].env.GITHUB_TOKEN).toBe("${{ github.token }}");
134
135
  expect((parseYAMLDocument(text) as Yaml).permissions).toEqual({ contents: "write", checks: "read" });
@@ -147,7 +148,7 @@ describe("the token the tick pushes its tags with (#3573)", () => {
147
148
  const steps = (parseYAMLDocument(renderCiGreenWorkflow({ ...base, push: { appIdVar: "CI_GREEN_APP_ID", appKeySecret: "CI_GREEN_APP_KEY" } })) as Yaml).jobs.tick.steps;
148
149
  expect(steps[0]).toMatchObject({
149
150
  id: "app-token",
150
- uses: "actions/create-github-app-token@v2",
151
+ uses: usesRef(ACTION_PINS.createGithubAppToken2),
151
152
  with: { "app-id": "${{ vars.CI_GREEN_APP_ID }}", "private-key": "${{ secrets.CI_GREEN_APP_KEY }}" },
152
153
  });
153
154
  expect(steps[1].with.token).toBe("${{ steps.app-token.outputs.token }}");