@intentius/chant 0.124.46 → 0.124.48

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 (633) 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 +21 -2
  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/ledger.d.ts +3 -0
  63. package/dist/cli/handlers/ledger.d.ts.map +1 -0
  64. package/dist/cli/handlers/ledger.js +55 -0
  65. package/dist/cli/handlers/migrate.d.ts.map +1 -1
  66. package/dist/cli/handlers/migrate.js +18 -11
  67. package/dist/cli/handlers/misc.d.ts.map +1 -1
  68. package/dist/cli/handlers/misc.js +12 -1
  69. package/dist/cli/handlers/pr-env.js +2 -2
  70. package/dist/cli/handlers/pr.d.ts +15 -1
  71. package/dist/cli/handlers/pr.d.ts.map +1 -1
  72. package/dist/cli/handlers/pr.js +143 -39
  73. package/dist/cli/handlers/query.d.ts +7 -2
  74. package/dist/cli/handlers/query.d.ts.map +1 -1
  75. package/dist/cli/handlers/query.js +16 -4
  76. package/dist/cli/handlers/release.d.ts.map +1 -1
  77. package/dist/cli/handlers/release.js +6 -3
  78. package/dist/cli/handlers/search.d.ts +2 -4
  79. package/dist/cli/handlers/search.d.ts.map +1 -1
  80. package/dist/cli/handlers/search.js +13 -39
  81. package/dist/cli/main.d.ts.map +1 -1
  82. package/dist/cli/main.js +65 -14
  83. package/dist/cli/parse-args.d.ts.map +1 -1
  84. package/dist/cli/parse-args.js +36 -2
  85. package/dist/cli/registry.d.ts +14 -4
  86. package/dist/cli/registry.d.ts.map +1 -1
  87. package/dist/components/builders.d.ts +5 -1
  88. package/dist/components/builders.d.ts.map +1 -1
  89. package/dist/components/builders.js +5 -0
  90. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  91. package/dist/components/capability-plugin-loader.js +16 -1
  92. package/dist/components/capability-plugin.d.ts +14 -0
  93. package/dist/components/capability-plugin.d.ts.map +1 -1
  94. package/dist/components/pr-hooks.d.ts +21 -1
  95. package/dist/components/pr-hooks.d.ts.map +1 -1
  96. package/dist/components/pr-hooks.js +72 -4
  97. package/dist/components/pr-pipeline.d.ts +45 -10
  98. package/dist/components/pr-pipeline.d.ts.map +1 -1
  99. package/dist/components/pr-pipeline.js +47 -14
  100. package/dist/components/pr-policy.d.ts +36 -0
  101. package/dist/components/pr-policy.d.ts.map +1 -1
  102. package/dist/components/pr-policy.js +36 -0
  103. package/dist/components/starter-plugin.d.ts +1 -0
  104. package/dist/components/starter-plugin.d.ts.map +1 -1
  105. package/dist/components/starter-plugin.js +5 -1
  106. package/dist/components/verbs/index.d.ts +2 -0
  107. package/dist/components/verbs/index.d.ts.map +1 -1
  108. package/dist/components/verbs/index.js +2 -0
  109. package/dist/components/verbs/publish-artifact.d.ts +65 -0
  110. package/dist/components/verbs/publish-artifact.d.ts.map +1 -0
  111. package/dist/components/verbs/publish-artifact.js +74 -0
  112. package/dist/components/verbs/publish-image.d.ts +129 -0
  113. package/dist/components/verbs/publish-image.d.ts.map +1 -0
  114. package/dist/components/verbs/publish-image.js +145 -0
  115. package/dist/config-environments.d.ts +20 -0
  116. package/dist/config-environments.d.ts.map +1 -1
  117. package/dist/config-environments.js +12 -0
  118. package/dist/config.d.ts +9 -1
  119. package/dist/config.d.ts.map +1 -1
  120. package/dist/config.js +6 -2
  121. package/dist/gate-decision.d.ts +11 -2
  122. package/dist/gate-decision.d.ts.map +1 -1
  123. package/dist/gate-decision.js +15 -3
  124. package/dist/graph-ir.d.ts +25 -0
  125. package/dist/graph-ir.d.ts.map +1 -1
  126. package/dist/graph-walk.d.ts +66 -0
  127. package/dist/graph-walk.d.ts.map +1 -0
  128. package/dist/graph-walk.js +70 -0
  129. package/dist/import/parser.d.ts +19 -0
  130. package/dist/import/parser.d.ts.map +1 -1
  131. package/dist/lexicon.d.ts +71 -6
  132. package/dist/lexicon.d.ts.map +1 -1
  133. package/dist/lifecycle/gate-ledger.d.ts +11 -0
  134. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  135. package/dist/lifecycle/git.d.ts +2 -5
  136. package/dist/lifecycle/git.d.ts.map +1 -1
  137. package/dist/lifecycle/ledger-read.d.ts +65 -0
  138. package/dist/lifecycle/ledger-read.d.ts.map +1 -0
  139. package/dist/lifecycle/ledger-read.js +160 -0
  140. package/dist/lifecycle/release-workstation.d.ts +7 -0
  141. package/dist/lifecycle/release-workstation.d.ts.map +1 -1
  142. package/dist/lifecycle/release-workstation.js +25 -0
  143. package/dist/lifecycle/run-ledger.d.ts +4 -3
  144. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  145. package/dist/lifecycle/run-ledger.js +1 -0
  146. package/dist/lint/discover.js +3 -1
  147. package/dist/lint/post-synth.d.ts +127 -1
  148. package/dist/lint/post-synth.d.ts.map +1 -1
  149. package/dist/lint/post-synth.js +84 -2
  150. package/dist/lint/rules/op/index.d.ts +1 -0
  151. package/dist/lint/rules/op/index.d.ts.map +1 -1
  152. package/dist/lint/rules/op/index.js +2 -0
  153. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts +32 -0
  154. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts.map +1 -0
  155. package/dist/lint/rules/op/ops019-workspace-op-target.js +63 -0
  156. package/dist/op/activities/activity-contracts.d.ts +5 -2
  157. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  158. package/dist/op/activities/activity-contracts.js +5 -2
  159. package/dist/op/activities/index.d.ts +2 -2
  160. package/dist/op/activities/index.d.ts.map +1 -1
  161. package/dist/op/activities/index.js +1 -1
  162. package/dist/op/activities/review-agent.d.ts +90 -4
  163. package/dist/op/activities/review-agent.d.ts.map +1 -1
  164. package/dist/op/activities/review-agent.js +217 -19
  165. package/dist/op/activities/work-evidence.d.ts +8 -1
  166. package/dist/op/activities/work-evidence.d.ts.map +1 -1
  167. package/dist/op/activities/work-evidence.js +1 -0
  168. package/dist/op/builders.d.ts +9 -5
  169. package/dist/op/builders.d.ts.map +1 -1
  170. package/dist/op/builders.js +9 -5
  171. package/dist/op/composites/apply-op.d.ts +29 -2
  172. package/dist/op/composites/apply-op.d.ts.map +1 -1
  173. package/dist/op/composites/apply-op.js +20 -1
  174. package/dist/op/gate-approval.d.ts +11 -5
  175. package/dist/op/gate-approval.d.ts.map +1 -1
  176. package/dist/op/gate-approval.js +11 -6
  177. package/dist/op/gate-quorum.d.ts +100 -3
  178. package/dist/op/gate-quorum.d.ts.map +1 -1
  179. package/dist/op/gate-quorum.js +108 -6
  180. package/dist/op/gate-review.d.ts +9 -0
  181. package/dist/op/gate-review.d.ts.map +1 -1
  182. package/dist/op/gate-review.js +13 -0
  183. package/dist/op/gate.d.ts +44 -3
  184. package/dist/op/gate.d.ts.map +1 -1
  185. package/dist/op/gate.js +25 -4
  186. package/dist/op/index.d.ts +3 -3
  187. package/dist/op/index.d.ts.map +1 -1
  188. package/dist/op/index.js +1 -1
  189. package/dist/op/local-executor.d.ts +38 -3
  190. package/dist/op/local-executor.d.ts.map +1 -1
  191. package/dist/op/local-executor.js +48 -6
  192. package/dist/op/op-waves-run.d.ts +26 -1
  193. package/dist/op/op-waves-run.d.ts.map +1 -1
  194. package/dist/op/op-waves-run.js +259 -13
  195. package/dist/op/op-waves.d.ts +72 -5
  196. package/dist/op/op-waves.d.ts.map +1 -1
  197. package/dist/op/op-waves.js +110 -15
  198. package/dist/op/runtime.d.ts +11 -0
  199. package/dist/op/runtime.d.ts.map +1 -1
  200. package/dist/op/schedule-overdue.d.ts +16 -0
  201. package/dist/op/schedule-overdue.d.ts.map +1 -1
  202. package/dist/op/schedule-overdue.js +36 -0
  203. package/dist/op/stage-hooks.d.ts +9 -2
  204. package/dist/op/stage-hooks.d.ts.map +1 -1
  205. package/dist/op/stage-hooks.js +7 -5
  206. package/dist/op/workspace-run-env.d.ts +22 -0
  207. package/dist/op/workspace-run-env.d.ts.map +1 -0
  208. package/dist/op/workspace-run-env.js +21 -0
  209. package/dist/pr-forge.d.ts +24 -0
  210. package/dist/pr-forge.d.ts.map +1 -1
  211. package/dist/pr-forge.js +47 -0
  212. package/dist/pr-post.d.ts +42 -0
  213. package/dist/pr-post.d.ts.map +1 -1
  214. package/dist/pr-post.js +67 -1
  215. package/dist/read-filter.d.ts +65 -0
  216. package/dist/read-filter.d.ts.map +1 -0
  217. package/dist/read-filter.js +179 -0
  218. package/dist/run-event.d.ts +1 -1
  219. package/dist/run-event.d.ts.map +1 -1
  220. package/dist/terraform/carve.d.ts +24 -3
  221. package/dist/terraform/carve.d.ts.map +1 -1
  222. package/dist/terraform/carve.js +32 -14
  223. package/dist/terraform/cuts.d.ts +97 -0
  224. package/dist/terraform/cuts.d.ts.map +1 -0
  225. package/dist/terraform/cuts.js +138 -0
  226. package/dist/terraform/folds.d.ts +26 -0
  227. package/dist/terraform/folds.d.ts.map +1 -0
  228. package/dist/terraform/folds.js +38 -0
  229. package/dist/terraform/graph.d.ts.map +1 -1
  230. package/dist/terraform/graph.js +10 -0
  231. package/dist/terraform/reach.d.ts +140 -0
  232. package/dist/terraform/reach.d.ts.map +1 -0
  233. package/dist/terraform/reach.js +170 -0
  234. package/dist/terraform/score.d.ts +14 -0
  235. package/dist/terraform/score.d.ts.map +1 -1
  236. package/dist/terraform/score.js +14 -31
  237. package/dist/terraform/state.d.ts.map +1 -1
  238. package/dist/terraform/state.js +4 -2
  239. package/dist/terraform/types.d.ts +8 -0
  240. package/dist/terraform/types.d.ts.map +1 -1
  241. package/dist/wave-blocked.d.ts +10 -1
  242. package/dist/wave-blocked.d.ts.map +1 -1
  243. package/dist/wave-blocked.js +31 -3
  244. package/dist/wave-runtime.mjs +263 -254
  245. package/dist/workspace/check.schema.json +11 -2
  246. package/dist/workspace/checks/ci-branch.d.ts +44 -0
  247. package/dist/workspace/checks/ci-branch.d.ts.map +1 -0
  248. package/dist/workspace/checks/ci-branch.js +133 -0
  249. package/dist/workspace/checks/links.d.ts +3 -0
  250. package/dist/workspace/checks/links.d.ts.map +1 -1
  251. package/dist/workspace/checks/links.js +12 -1
  252. package/dist/workspace/checks/org.d.ts +48 -0
  253. package/dist/workspace/checks/org.d.ts.map +1 -0
  254. package/dist/workspace/checks/org.js +156 -0
  255. package/dist/workspace/checks.d.ts +11 -1
  256. package/dist/workspace/checks.d.ts.map +1 -1
  257. package/dist/workspace/checks.js +59 -7
  258. package/dist/workspace/ci-branches.d.ts +16 -0
  259. package/dist/workspace/ci-branches.d.ts.map +1 -1
  260. package/dist/workspace/ci-branches.js +27 -0
  261. package/dist/workspace/ci-green-cli.js +7 -7
  262. package/dist/workspace/ci-green-workflow.d.ts +1 -1
  263. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  264. package/dist/workspace/ci-green-workflow.js +13 -8
  265. package/dist/workspace/ci-green.d.ts +14 -0
  266. package/dist/workspace/ci-green.d.ts.map +1 -1
  267. package/dist/workspace/ci-green.js +44 -11
  268. package/dist/workspace/ci-run.d.ts +9 -1
  269. package/dist/workspace/ci-run.d.ts.map +1 -1
  270. package/dist/workspace/ci-run.js +67 -6
  271. package/dist/workspace/ci-serve.d.ts +9 -0
  272. package/dist/workspace/ci-serve.d.ts.map +1 -1
  273. package/dist/workspace/ci-serve.js +114 -76
  274. package/dist/workspace/compose-graph.d.ts +14 -0
  275. package/dist/workspace/compose-graph.d.ts.map +1 -1
  276. package/dist/workspace/composites.d.ts +4 -1
  277. package/dist/workspace/composites.d.ts.map +1 -1
  278. package/dist/workspace/composites.js +5 -2
  279. package/dist/workspace/composites.schema.json +22 -0
  280. package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  281. package/dist/workspace/cross-root-lint.d.ts +56 -0
  282. package/dist/workspace/cross-root-lint.d.ts.map +1 -0
  283. package/dist/workspace/cross-root-lint.js +111 -0
  284. package/dist/workspace/dbt-models-reader.d.ts +44 -0
  285. package/dist/workspace/dbt-models-reader.d.ts.map +1 -0
  286. package/dist/workspace/dbt-models-reader.js +356 -0
  287. package/dist/workspace/dbt-project-common.d.ts +40 -0
  288. package/dist/workspace/dbt-project-common.d.ts.map +1 -0
  289. package/dist/workspace/dbt-project-common.js +106 -0
  290. package/dist/workspace/dbt-project-reader.d.ts +4 -3
  291. package/dist/workspace/dbt-project-reader.d.ts.map +1 -1
  292. package/dist/workspace/dbt-project-reader.js +14 -108
  293. package/dist/workspace/decision-rank.d.ts +26 -0
  294. package/dist/workspace/decision-rank.d.ts.map +1 -0
  295. package/dist/workspace/decision-rank.js +16 -0
  296. package/dist/workspace/declaration.d.ts +43 -2
  297. package/dist/workspace/declaration.d.ts.map +1 -1
  298. package/dist/workspace/declaration.js +45 -11
  299. package/dist/workspace/declaration.schema.json +133 -24
  300. package/dist/workspace/environments.d.ts +26 -2
  301. package/dist/workspace/environments.d.ts.map +1 -1
  302. package/dist/workspace/environments.js +26 -2
  303. package/dist/workspace/flyway-dir.d.ts +39 -0
  304. package/dist/workspace/flyway-dir.d.ts.map +1 -0
  305. package/dist/workspace/flyway-dir.js +104 -0
  306. package/dist/workspace/flyway-layout.d.ts +96 -0
  307. package/dist/workspace/flyway-layout.d.ts.map +1 -0
  308. package/dist/workspace/flyway-layout.js +308 -0
  309. package/dist/workspace/graph-cli.d.ts.map +1 -1
  310. package/dist/workspace/graph-cli.js +26 -8
  311. package/dist/workspace/graph.schema.json +34 -0
  312. package/dist/workspace/intent.d.ts +2 -3
  313. package/dist/workspace/intent.d.ts.map +1 -1
  314. package/dist/workspace/intent.js +5 -9
  315. package/dist/workspace/kind-link-readers.d.ts +59 -2
  316. package/dist/workspace/kind-link-readers.d.ts.map +1 -1
  317. package/dist/workspace/kind-link-readers.js +17 -1
  318. package/dist/workspace/kinds.d.ts +66 -2
  319. package/dist/workspace/kinds.d.ts.map +1 -1
  320. package/dist/workspace/kinds.js +98 -4
  321. package/dist/workspace/ledger.schema.json +158 -0
  322. package/dist/workspace/lexicon-outputs.d.ts +20 -1
  323. package/dist/workspace/lexicon-outputs.d.ts.map +1 -1
  324. package/dist/workspace/lexicon-outputs.js +102 -10
  325. package/dist/workspace/lineage-check.d.ts +3 -0
  326. package/dist/workspace/lineage-check.d.ts.map +1 -1
  327. package/dist/workspace/lineage-check.js +23 -4
  328. package/dist/workspace/links.d.ts +8 -1
  329. package/dist/workspace/links.d.ts.map +1 -1
  330. package/dist/workspace/links.js +25 -1
  331. package/dist/workspace/ls.d.ts +8 -0
  332. package/dist/workspace/ls.d.ts.map +1 -1
  333. package/dist/workspace/ls.js +7 -1
  334. package/dist/workspace/ls.schema.json +7 -0
  335. package/dist/workspace/member-commands.d.ts.map +1 -1
  336. package/dist/workspace/member-commands.js +54 -5
  337. package/dist/workspace/migrate-links.d.ts +45 -0
  338. package/dist/workspace/migrate-links.d.ts.map +1 -0
  339. package/dist/workspace/migrate-links.js +100 -0
  340. package/dist/workspace/migration-dirs.d.ts +74 -0
  341. package/dist/workspace/migration-dirs.d.ts.map +1 -0
  342. package/dist/workspace/migration-dirs.js +254 -0
  343. package/dist/workspace/reason-codes.d.ts +8 -0
  344. package/dist/workspace/reason-codes.d.ts.map +1 -1
  345. package/dist/workspace/reason-codes.js +11 -0
  346. package/dist/workspace/records-cli.d.ts +11 -0
  347. package/dist/workspace/records-cli.d.ts.map +1 -1
  348. package/dist/workspace/records-cli.js +57 -5
  349. package/dist/workspace/records.schema.json +5 -0
  350. package/dist/workspace/release-candidates.d.ts +2 -0
  351. package/dist/workspace/release-candidates.d.ts.map +1 -1
  352. package/dist/workspace/release-candidates.js +5 -2
  353. package/dist/workspace/release-train.d.ts +53 -0
  354. package/dist/workspace/release-train.d.ts.map +1 -0
  355. package/dist/workspace/release-train.js +134 -0
  356. package/dist/workspace/release-workflow.d.ts.map +1 -1
  357. package/dist/workspace/release-workflow.js +4 -3
  358. package/dist/workspace/repo-ref.d.ts +45 -0
  359. package/dist/workspace/repo-ref.d.ts.map +1 -0
  360. package/dist/workspace/repo-ref.js +114 -0
  361. package/dist/workspace/run-event.schema.json +2 -2
  362. package/dist/workspace/runs-cli.d.ts +9 -0
  363. package/dist/workspace/runs-cli.d.ts.map +1 -1
  364. package/dist/workspace/runs-cli.js +48 -3
  365. package/dist/workspace/runs.schema.json +7 -0
  366. package/dist/workspace/search-contract.d.ts +1 -1
  367. package/dist/workspace/search-contract.d.ts.map +1 -1
  368. package/dist/workspace/status-decisions.d.ts +37 -0
  369. package/dist/workspace/status-decisions.d.ts.map +1 -0
  370. package/dist/workspace/status-decisions.js +147 -0
  371. package/dist/workspace/status-gates.d.ts +9 -0
  372. package/dist/workspace/status-gates.d.ts.map +1 -1
  373. package/dist/workspace/status.d.ts +4 -0
  374. package/dist/workspace/status.d.ts.map +1 -1
  375. package/dist/workspace/status.js +14 -2
  376. package/dist/workspace/status.schema.json +6 -0
  377. package/dist/workspace/work-cli.d.ts.map +1 -1
  378. package/dist/workspace/work-cli.js +5 -3
  379. package/dist/workspace/work-evidence-ops.d.ts +83 -0
  380. package/dist/workspace/work-evidence-ops.d.ts.map +1 -0
  381. package/dist/workspace/work-evidence-ops.js +169 -0
  382. package/dist/workspace/work-evidence.d.ts +12 -2
  383. package/dist/workspace/work-evidence.d.ts.map +1 -1
  384. package/dist/workspace/work-evidence.js +21 -3
  385. package/dist/workspace/work-evidence.schema.json +49 -2
  386. package/dist/workspace/workspace-gates.d.ts +110 -0
  387. package/dist/workspace/workspace-gates.d.ts.map +1 -0
  388. package/dist/workspace/workspace-gates.js +140 -0
  389. package/dist/workspace/workspace-kinds.schema.json +58 -3
  390. package/dist/workspace/workspace-ledger.d.ts +76 -0
  391. package/dist/workspace/workspace-ledger.d.ts.map +1 -0
  392. package/dist/workspace/workspace-ledger.js +91 -0
  393. package/dist/workspace/workspace-run.d.ts +112 -0
  394. package/dist/workspace/workspace-run.d.ts.map +1 -0
  395. package/dist/workspace/workspace-run.js +432 -0
  396. package/package.json +1 -1
  397. package/src/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  398. package/src/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  399. package/src/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  400. package/src/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  401. package/src/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  402. package/src/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  403. package/src/__golden__/wave-loop/op-cost-member.json +13 -0
  404. package/src/__golden__/wave-loop/op-denied-source.json +16 -0
  405. package/src/__golden__/wave-loop/op-denied.json +16 -0
  406. package/src/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  407. package/src/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  408. package/src/__golden__/wave-loop/op-discover-rest.json +26 -0
  409. package/src/__golden__/wave-loop/op-discover-wide.json +26 -0
  410. package/src/__golden__/wave-loop/op-held-on-again.json +16 -0
  411. package/src/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  412. package/src/__golden__/wave-loop/op-held-on.json +16 -0
  413. package/src/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  414. package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  415. package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  416. package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  417. package/src/__golden__/wave-loop/op-hooks-held.json +13 -0
  418. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  419. package/src/__golden__/wave-loop/op-hooks-pass.json +13 -0
  420. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  421. package/src/__golden__/wave-loop/op-hooks-share.json +33 -0
  422. package/src/__golden__/wave-loop/op-outcome-held.json +10 -0
  423. package/src/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  424. package/src/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  425. package/src/__golden__/wave-loop/op-outcome-replan.json +10 -0
  426. package/src/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  427. package/src/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  428. package/src/__golden__/wave-loop/op-overridden.json +16 -0
  429. package/src/__golden__/wave-loop/op-per-run.json +20 -0
  430. package/src/__golden__/wave-loop/op-point.json +13 -0
  431. package/src/__golden__/wave-loop/op-pr-review.json +10 -0
  432. package/src/__golden__/wave-loop/op-sealed.json +10 -0
  433. package/src/__golden__/wave-loop/op-spend-change-only.json +10 -0
  434. package/src/__golden__/wave-loop/op-spend.json +10 -0
  435. package/src/__golden__/wave-loop/op-wave-commands.json +55 -0
  436. package/src/__golden__/wave-loop/op-wide-refused.json +26 -0
  437. package/src/__golden__/wave-loop/op-wide-waiting.json +29 -0
  438. package/src/__golden__/wave-loop/op-wide.json +39 -0
  439. package/src/action-pins.test.ts +78 -0
  440. package/src/action-pins.ts +92 -0
  441. package/src/cli/commands/carve-score-once.test.ts +99 -0
  442. package/src/cli/commands/carve.ts +37 -6
  443. package/src/cli/commands/check-lexicon.test.ts +2 -2
  444. package/src/cli/commands/import-report.ts +71 -0
  445. package/src/cli/commands/import.ts +33 -3
  446. package/src/cli/commands/migrate-dialect.test.ts +145 -0
  447. package/src/cli/commands/migrate.ts +132 -11
  448. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +4 -4
  449. package/src/cli/handlers/approve.ts +23 -2
  450. package/src/cli/handlers/build.ts +7 -1
  451. package/src/cli/handlers/ledger.ts +54 -0
  452. package/src/cli/handlers/migrate.ts +21 -12
  453. package/src/cli/handlers/misc.ts +10 -1
  454. package/src/cli/handlers/pr-env.ts +2 -2
  455. package/src/cli/handlers/pr.ts +138 -36
  456. package/src/cli/handlers/query.ts +16 -4
  457. package/src/cli/handlers/release.ts +6 -3
  458. package/src/cli/handlers/search.ts +13 -34
  459. package/src/cli/main.test.ts +15 -0
  460. package/src/cli/main.ts +65 -14
  461. package/src/cli/parse-args.ts +27 -2
  462. package/src/cli/registry.ts +14 -4
  463. package/src/components/builders.ts +8 -0
  464. package/src/components/capability-plugin-loader.ts +13 -1
  465. package/src/components/capability-plugin.ts +16 -0
  466. package/src/components/pr-hooks.test.ts +73 -5
  467. package/src/components/pr-hooks.ts +95 -5
  468. package/src/components/pr-pipeline.ts +64 -16
  469. package/src/components/pr-policy.test.ts +64 -1
  470. package/src/components/pr-policy.ts +53 -0
  471. package/src/components/registry.test.ts +1 -1
  472. package/src/components/starter-plugin.ts +6 -0
  473. package/src/components/verbs/index.ts +2 -0
  474. package/src/components/verbs/publish-artifact.test.ts +60 -0
  475. package/src/components/verbs/publish-artifact.ts +122 -0
  476. package/src/components/verbs/publish-image.test.ts +101 -0
  477. package/src/components/verbs/publish-image.ts +265 -0
  478. package/src/config-environments.ts +28 -1
  479. package/src/config.ts +8 -1
  480. package/src/gate-decision-bundle.test.ts +1 -0
  481. package/src/gate-decision-identity.test.ts +9 -0
  482. package/src/gate-decision.ts +26 -3
  483. package/src/graph-ir.ts +27 -0
  484. package/src/graph-walk.test.ts +44 -0
  485. package/src/graph-walk.ts +102 -0
  486. package/src/import/parser.ts +20 -0
  487. package/src/lexicon.ts +74 -6
  488. package/src/lifecycle/gate-ledger.ts +8 -0
  489. package/src/lifecycle/git.ts +2 -2
  490. package/src/lifecycle/ledger-read.test.ts +107 -0
  491. package/src/lifecycle/ledger-read.ts +195 -0
  492. package/src/lifecycle/release-workstation.ts +24 -0
  493. package/src/lifecycle/run-ledger.ts +5 -2
  494. package/src/lint/cross-root.test.ts +122 -0
  495. package/src/lint/discover.ts +3 -1
  496. package/src/lint/post-synth.ts +212 -2
  497. package/src/lint/rules/op/index.ts +2 -0
  498. package/src/lint/rules/op/ops019-workspace-op-target.ts +89 -0
  499. package/src/op/activities/activity-contracts.ts +5 -2
  500. package/src/op/activities/index.ts +2 -2
  501. package/src/op/activities/review-agent.test.ts +221 -4
  502. package/src/op/activities/review-agent.ts +265 -22
  503. package/src/op/activities/work-evidence.ts +9 -1
  504. package/src/op/builders.ts +9 -5
  505. package/src/op/composites/apply-op.test.ts +108 -0
  506. package/src/op/composites/apply-op.ts +37 -2
  507. package/src/op/gate-approval.test.ts +6 -1
  508. package/src/op/gate-approval.ts +12 -6
  509. package/src/op/gate-quorum-at-base.test.ts +211 -0
  510. package/src/op/gate-quorum.ts +156 -8
  511. package/src/op/gate-review.ts +14 -0
  512. package/src/op/gate.ts +67 -8
  513. package/src/op/index.ts +3 -2
  514. package/src/op/local-executor.ts +69 -9
  515. package/src/op/op-waves-depends-on.test.ts +81 -0
  516. package/src/op/op-waves-hook-targets.test.ts +4 -2
  517. package/src/op/op-waves-quorum-product.test.ts +158 -0
  518. package/src/op/op-waves-run.ts +288 -14
  519. package/src/op/op-waves.ts +143 -14
  520. package/src/op/runtime.ts +8 -0
  521. package/src/op/schedule-overdue.ts +33 -0
  522. package/src/op/stage-hooks.ts +13 -5
  523. package/src/op/workspace-run-env.ts +29 -0
  524. package/src/pr-forge-bundle.test.ts +2 -2
  525. package/src/pr-forge.test.ts +47 -0
  526. package/src/pr-forge.ts +68 -0
  527. package/src/pr-post-open.e2e.test.ts +36 -0
  528. package/src/pr-post.test.ts +102 -3
  529. package/src/pr-post.ts +89 -1
  530. package/src/read-filter.test.ts +54 -0
  531. package/src/read-filter.ts +193 -0
  532. package/src/run-event.ts +1 -1
  533. package/src/stage-hooks-bundle.test.ts +1 -0
  534. package/src/terraform/carve.ts +52 -15
  535. package/src/terraform/cuts.test.ts +173 -0
  536. package/src/terraform/cuts.ts +188 -0
  537. package/src/terraform/folds.ts +44 -0
  538. package/src/terraform/graph.ts +9 -0
  539. package/src/terraform/radius.test.ts +90 -0
  540. package/src/terraform/reach.test.ts +146 -0
  541. package/src/terraform/reach.ts +247 -0
  542. package/src/terraform/score.ts +27 -29
  543. package/src/terraform/state.ts +3 -1
  544. package/src/terraform/types.ts +8 -0
  545. package/src/wave-blocked.ts +28 -3
  546. package/src/wave-loop-golden.test.ts +4 -3
  547. package/src/workspace/behold-kinds.test.ts +1 -1
  548. package/src/workspace/check-contract.test.ts +7 -6
  549. package/src/workspace/check.schema.json +11 -2
  550. package/src/workspace/checks/ci-branch.test.ts +128 -0
  551. package/src/workspace/checks/ci-branch.ts +141 -0
  552. package/src/workspace/checks/links.ts +12 -1
  553. package/src/workspace/checks/org.ts +169 -0
  554. package/src/workspace/checks.ts +64 -7
  555. package/src/workspace/ci-branches.ts +31 -0
  556. package/src/workspace/ci-green-branches.test.ts +153 -0
  557. package/src/workspace/ci-green-cli.ts +7 -7
  558. package/src/workspace/ci-green-workflow.test.ts +4 -3
  559. package/src/workspace/ci-green-workflow.ts +13 -9
  560. package/src/workspace/ci-green.ts +49 -12
  561. package/src/workspace/ci-run.test.ts +72 -1
  562. package/src/workspace/ci-run.ts +68 -8
  563. package/src/workspace/ci-serve.test.ts +95 -0
  564. package/src/workspace/ci-serve.ts +106 -65
  565. package/src/workspace/component-environments.test.ts +25 -2
  566. package/src/workspace/compose-graph.ts +9 -2
  567. package/src/workspace/composites.schema.json +22 -0
  568. package/src/workspace/composites.test.ts +39 -8
  569. package/src/workspace/composites.ts +6 -2
  570. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  571. package/src/workspace/cross-root-lint.test.ts +90 -0
  572. package/src/workspace/cross-root-lint.ts +126 -0
  573. package/src/workspace/dbt-kind.test.ts +3 -2
  574. package/src/workspace/dbt-models-reader.ts +362 -0
  575. package/src/workspace/dbt-models.test.ts +248 -0
  576. package/src/workspace/dbt-project-common.ts +113 -0
  577. package/src/workspace/dbt-project-reader.ts +29 -112
  578. package/src/workspace/decision-rank.ts +40 -0
  579. package/src/workspace/declaration.schema.json +133 -24
  580. package/src/workspace/declaration.ts +84 -15
  581. package/src/workspace/environments.ts +40 -4
  582. package/src/workspace/flyway-dir.ts +113 -0
  583. package/src/workspace/flyway-layout.ts +337 -0
  584. package/src/workspace/graph-cli.ts +25 -10
  585. package/src/workspace/graph-contract.test.ts +4 -2
  586. package/src/workspace/graph.schema.json +34 -0
  587. package/src/workspace/identity.test.ts +14 -2
  588. package/src/workspace/intent.ts +6 -14
  589. package/src/workspace/kind-link-readers.ts +68 -3
  590. package/src/workspace/kinds.test.ts +12 -7
  591. package/src/workspace/kinds.ts +132 -7
  592. package/src/workspace/ledger.schema.json +158 -0
  593. package/src/workspace/lexicon-outputs.ts +132 -13
  594. package/src/workspace/lineage-check.ts +30 -7
  595. package/src/workspace/links.ts +24 -1
  596. package/src/workspace/ls-contract.test.ts +1 -1
  597. package/src/workspace/ls.schema.json +7 -0
  598. package/src/workspace/ls.ts +10 -1
  599. package/src/workspace/member-commands.ts +60 -2
  600. package/src/workspace/member-file-schema.test.ts +124 -0
  601. package/src/workspace/migrate-links.test.ts +99 -0
  602. package/src/workspace/migrate-links.ts +108 -0
  603. package/src/workspace/migration-dirs.test.ts +272 -0
  604. package/src/workspace/migration-dirs.ts +267 -0
  605. package/src/workspace/org-scope.test.ts +238 -0
  606. package/src/workspace/reason-codes.test.ts +6 -1
  607. package/src/workspace/reason-codes.ts +11 -0
  608. package/src/workspace/records-cli.ts +58 -5
  609. package/src/workspace/records.schema.json +5 -0
  610. package/src/workspace/release-candidates.ts +7 -2
  611. package/src/workspace/release-train.ts +157 -0
  612. package/src/workspace/release-workflow.ts +4 -3
  613. package/src/workspace/repo-ref.ts +113 -0
  614. package/src/workspace/run-event.schema.json +2 -2
  615. package/src/workspace/runs-cli.ts +48 -4
  616. package/src/workspace/runs.schema.json +7 -0
  617. package/src/workspace/status-contract.test.ts +46 -1
  618. package/src/workspace/status-decisions.test.ts +100 -0
  619. package/src/workspace/status-decisions.ts +151 -0
  620. package/src/workspace/status-gates.ts +9 -0
  621. package/src/workspace/status.schema.json +6 -0
  622. package/src/workspace/status.ts +17 -3
  623. package/src/workspace/where-filters.test.ts +39 -0
  624. package/src/workspace/work-cli.ts +5 -3
  625. package/src/workspace/work-evidence-ops.test.ts +176 -0
  626. package/src/workspace/work-evidence-ops.ts +189 -0
  627. package/src/workspace/work-evidence.schema.json +49 -2
  628. package/src/workspace/work-evidence.ts +22 -3
  629. package/src/workspace/workspace-gates.ts +200 -0
  630. package/src/workspace/workspace-kinds.schema.json +58 -3
  631. package/src/workspace/workspace-ledger.ts +130 -0
  632. package/src/workspace/workspace-run.test.ts +322 -0
  633. package/src/workspace/workspace-run.ts +461 -0
package/src/cli/main.ts CHANGED
@@ -183,7 +183,14 @@ Commands:
183
183
  the resources and snapshots each lexicon observed, Op
184
184
  runs, gates, deploys, releases and cuts, loaded into
185
185
  node:sqlite (schema v1, listed in chant_schema).
186
- --member <name> keeps members; --json prints rows
186
+ --member <name> keeps members; --json prints rows.
187
+ For a read contract, use chant ledger
188
+ ledger <table> [dir] One ledger's rows from chant/lifecycle as a read
189
+ contract (resources, snapshots, runs, gates, deploys,
190
+ releases, cuts; the columns chant query loads).
191
+ --where <field><op><value> (= != < <= > >= ~,
192
+ repeatable), --since <time|7d>, --at <rev> of
193
+ chant/lifecycle, --member <name>, --json
187
194
  generate Generate typed code from sources chant.config declares
188
195
  (k8s.crds, helm.charts) into src/generated/<lexicon>/
189
196
  (codegen.outDir); --check exits 1 when the committed
@@ -626,7 +633,7 @@ Workspace (level 1, #2524):
626
633
  restore, from the deploy records, releases and ci
627
634
  revoked tags. Eight weeks back by default. Read only;
628
635
  never fetches. --json prints the read-contract document
629
- workspace records [--kind <kind file>] [--current] [--uncommitted | --at <rev>] [--base <rev>] [--require attested] [--json]
636
+ workspace records [--kind <kind file>] [--current] [--uncommitted | --at <rev>] [--base <rev>] [--require attested] [--where <field><op><value>]... [--json]
630
637
  Without --kind, every record kind the declaration
631
638
  names. Read the records a record kind locates, validated
632
639
  against its schema, with reason codes for invalid
@@ -639,7 +646,9 @@ Workspace (level 1, #2524):
639
646
  asset-drift or asset-missing. In the working tree,
640
647
  each record says whether it is committed, modified
641
648
  or new against HEAD, with the branch and base;
642
- --uncommitted lists only the modified and new ones
649
+ --uncommitted lists only the modified and new ones;
650
+ --where keeps the records a typed filter matches
651
+ (id, path, state, valid, data.<key>, ...)
643
652
  workspace records [--kind <kind file>] --since <rev|session id> [--at <rev>] [--json]
644
653
  What changed in the records between <rev> and --at
645
654
  (default: the working tree): new and removed records,
@@ -709,14 +718,16 @@ Workspace (level 1, #2524):
709
718
  a url or a path pinned by hash) is appended to the
710
719
  item's pins through records amend. Prints the
711
720
  work-evidence document and never commits
712
- workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--follow-squash] [--json]
721
+ workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--where <field><op><value>]... [--follow-squash] [--json]
713
722
  The agent runs in the run ledger on chant/lifecycle, each
714
723
  with its model, tokens, cost and the commits it made,
715
724
  and totals per work item, decision and principal; a run
716
725
  that reports no cost is listed as unpriced, never zero.
717
726
  Read-only, never fetches; --follow-squash joins each
718
727
  squash merge to its pull request's runs, fetching a
719
- missing refs/pull/<n>/head from origin
728
+ missing refs/pull/<n>/head from origin; --where keeps
729
+ the runs a typed filter matches (state, model,
730
+ startedAt, cost.amount, ...) before the totals
720
731
  workspace runs start --from <file|->
721
732
  Record that an agent run started: who it worked for,
722
733
  its harness, model, work item and lease. Prints the run
@@ -888,7 +899,7 @@ Workspace (level 1, #2524):
888
899
  workspace admit <return id> [--note <text>] [--dry-run] [--json]
889
900
  Admit the signers of a return in .chant/trust.json, so
890
901
  its signed work reads as attested once merged
891
- workspace check [--at <rev>] [--json] [--format stylish|json|sarif] [--generated] [--kind <kind file>] [--live --env <env>]
902
+ workspace check [--at <rev>] [--json] [--format stylish|json|sarif] [--generated] [--kind <kind file>] [--live --env <env>] [--forge-repos <file>]
892
903
  Fail on an unreadable lineage lock or an open manual
893
904
  step, and, in a declared workspace, on a WSP check of
894
905
  the declaration, member ledgers, pipelines or
@@ -900,7 +911,10 @@ Workspace (level 1, #2524):
900
911
  resolves each declared member link against the live
901
912
  graph of <env> (each chant member runs chant graph
902
913
  --live, which reaches the account) and reports
903
- WSP141 and WSP142
914
+ WSP141 and WSP142. --forge-repos reads a forge's
915
+ repository list from a file and reports repos an
916
+ org workspace lacks (WSP014) or names that the
917
+ forge no longer has (WSP015)
904
918
  workspace check --changes <base>..<head> [--work <id>] [--severity off|warn|fail] [--kind <kind file>...] [--json]
905
919
  Map each path the diff changes to the current records
906
920
  whose constrains cover it: change-uncovered when none
@@ -928,9 +942,12 @@ Workspace (level 1, #2524):
928
942
  its own chant, one process per toolchain. -o <dir>
929
943
  writes <dir>/<member>.json; --member narrows the run;
930
944
  --dry-run prints which chant runs which project
931
- workspace lint [dir] [--format stylish|json|sarif] [--member <name>]
945
+ workspace lint [dir] [--format stylish|json|sarif] [--member <name>] [--cross-root]
932
946
  Lint every chant member and example project with its
933
- own chant; sarif writes one run per member
947
+ own chant; sarif writes one run per member.
948
+ --cross-root adds the disagreement lint (LNK001 and
949
+ the lexicons' cross-root checks) over every member's
950
+ graph, where declared links and the graphs disagree
934
951
  workspace audit [dir] [--json] [--member <name>]
935
952
  Audit each chant member with its own .chant-audit.json;
936
953
  every finding carries a member field
@@ -967,6 +984,29 @@ Workspace (level 1, #2524):
967
984
  One decision's intent walk over every path: and member:
968
985
  entry it constrains: the commits in its window, each
969
986
  own, worked, within-other or unexplained, with counts
987
+ workspace run <member>/<op>... [--env <env>] [--gate <name>] [--on <lexicon>] [--dry-run] [--json]
988
+ Run Ops that span members (#2554): each target's chant
989
+ run <op> in the member's directory, under its own
990
+ chant, producers before the members that link to them.
991
+ OPS019 checks every target against the composed graph
992
+ first. Member run records cite the workspace run,
993
+ whose record goes under _members/_workspace/<env>/.
994
+ Exits 3 when a step stops at a gate
995
+ workspace run --dependents-of <member> --op <op> [--env <env>] [--gate <name>] [--dry-run] [--json]
996
+ Run <op> in every member that links to <member>,
997
+ directly or not, nearest first; members without the
998
+ Op are listed as skipped
999
+ workspace run approve <gate> [--plan <digest>] [--approver <principal>]
1000
+ Approve a workspace gate's pending fact. It stands in
1001
+ for a member gate only when it covers that member's
1002
+ plan and the member sets acceptWorkspaceGates at base
1003
+ workspace train <env> [--pin <member>=<label|digest>]... [--dry-run] [--json]
1004
+ Record a release train: one cut release of every
1005
+ chant member (the newest, unless --pin picks one),
1006
+ under _members/_workspace/<env>/trains.jsonl. Refuses
1007
+ when a member has no release
1008
+ workspace train list <env> [--json]
1009
+ The release trains recorded for <env>, oldest first
970
1010
 
971
1011
  Lifecycle (alias: lc):
972
1012
  lifecycle snapshot <env> Query API, save metadata to orphan branch
@@ -1040,7 +1080,9 @@ Component release ledger + status:
1040
1080
  from the report pr-plan --no-status wrote
1041
1081
  (--pr <n> --head <sha> --env <env> --forge
1042
1082
  <kind>), in a job that runs none of the
1043
- change's code (#4295)
1083
+ change's code (#4295); --open posts every
1084
+ open merge request's from a GitLab pipeline
1085
+ of the target branch (#4328)
1044
1086
  components pr-record On merge (#4168): plan the same members again,
1045
1087
  store the change set and write the merge record
1046
1088
  _merges/<commit>.json; applies and refuses
@@ -1198,12 +1240,16 @@ Options:
1198
1240
  JSON (lifecycle plan)
1199
1241
  OR '--report markdown': emit a reviewer-facing markdown
1200
1242
  render, holes and disruption included (lifecycle plan)
1201
- --from <name> Source lexicon for migrate (default: github)
1202
- --to <name> Target lexicon for migrate (default: gitlab)
1203
- --emit <fmt> Migration output format: yaml (default) or ts
1243
+ --from <name> Source for migrate: a lexicon, or lexicon/dialect such
1244
+ as sql/postgres (default: github)
1245
+ --to <name> Target for migrate, same form, such as sql/clickhouse
1246
+ (default: gitlab)
1247
+ --emit <fmt> Migration output format: yaml (default), ts, or sql;
1248
+ sql/clickhouse defaults to ts
1204
1249
  --strict Escalate needs-review/validation to errors (migrate);
1205
1250
  exit nonzero on an unresolved identity (lifecycle whoami)
1206
- --validate Run external validator (glci/glab) after migrate
1251
+ --validate Run external validator after migrate: glci/glab, or the
1252
+ ClickHouse emulator for sql/clickhouse
1207
1253
  --use-composites Rewrite to composite calls when patterns match (migrate)
1208
1254
  --components Target discovered Component declarations instead of
1209
1255
  lexicon resources (list, describe, graph, build, run)
@@ -1498,6 +1544,7 @@ export const commandRegistry: CommandDef[] = [
1498
1544
  { name: "search", handler: runSearch },
1499
1545
  // #4297 — SQL over the snapshots, runs, gates, deploys and releases on chant/lifecycle. Imported on first use.
1500
1546
  { name: "query", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/query")).runQueryCommand(ctx) },
1547
+ { name: "ledger", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/ledger")).runLedgerCommand(ctx) },
1501
1548
  // chant#2591 — `import --agents` and `audit` read chant.config statically
1502
1549
  // and never evaluate it (chant#2589 for audit, which audits code it must not run).
1503
1550
  { name: "import", handler: runImport, runsNoConfig: (args) => args.agents === true },
@@ -1629,6 +1676,10 @@ export const commandRegistry: CommandDef[] = [
1629
1676
  { name: "workspace audit", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/member-commands")).runWorkspaceMembers(ctx, "audit") },
1630
1677
  { name: "workspace graph", handler: async (ctx) => (await import("../workspace/member-commands")).runWorkspaceMembers(ctx, "graph") },
1631
1678
  // #4374 — chant search in every member, under its own chant, composed with workspace graph's ids: the search document.
1679
+ // #2554 — Ops that span members, as <member>/<op> targets, each run under its member's own chant.
1680
+ { name: "workspace run", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/workspace-run")).runWorkspaceRun(ctx) },
1681
+ // #2554 — a release train: one release of every chant member, pinned in the workspace ledger.
1682
+ { name: "workspace train", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/release-train")).runWorkspaceTrain(ctx) },
1632
1683
  { name: "workspace search", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/search-cli")).runWorkspaceSearch(ctx) },
1633
1684
  // Each unit decides for itself whether its config is loaded (runCommandInProcess).
1634
1685
  { name: "workspace member-run", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/member-run")).runWorkspaceMemberRun(ctx, runCommandInProcess) },
@@ -47,6 +47,7 @@ const BOOLEAN_FLAGS = new Set([
47
47
  "--up",
48
48
  "--down",
49
49
  "--include-dependents",
50
+ "--cross-root",
50
51
  "--local",
51
52
  "--json",
52
53
  "--progress-json",
@@ -380,6 +381,19 @@ export function parseArgs(args: string[]): ParsedArgs {
380
381
  result.head = args[++i];
381
382
  } else if (arg === "--include-dependents") {
382
383
  result.includeDependents = true;
384
+ } else if (arg === "--cross-root") {
385
+ // `chant workspace lint --cross-root` (#2549): the disagreement lint over every member's graph.
386
+ result.crossRoot = true;
387
+ } else if (arg === "--dependents-of") {
388
+ // `chant workspace run --dependents-of <member> --op <op>` (#2554).
389
+ const value = args[++i];
390
+ if (!value || value.startsWith("-")) throw new Error("--dependents-of needs a member name: --dependents-of <member>");
391
+ result.dependentsOf = value;
392
+ } else if (arg === "--pin") {
393
+ // `chant workspace train <env> --pin <member>=<label|digest>` (#2554), repeatable.
394
+ const value = args[++i];
395
+ if (!value || value.startsWith("-") || !value.includes("=")) throw new Error("--pin needs a member and a release: --pin <member>=<label|digest>");
396
+ (result.pins ??= []).push(value);
383
397
  } else if (arg === "--from-affected") {
384
398
  result.fromAffected = args[++i];
385
399
  } else if (arg === "--gate") {
@@ -932,8 +946,8 @@ export function parseArgs(args: string[]): ParsedArgs {
932
946
  } else if (arg === "--expire") {
933
947
  result.expire = true;
934
948
  } else if (arg === "--role") {
935
- // #2508 — a role the approver claims, which a gate's policy sees. A
936
- // quorum reads roles from .chant/trust.json at base instead (#4290).
949
+ // #2508 — a role the approver claims, recorded on the approval. A quorum
950
+ // (#4290) and a gate policy (#4324) read roles from .chant/trust.json at base.
937
951
  // Repeatable, and a comma list works too.
938
952
  const value = args[++i] ?? "";
939
953
  result.roles = [...(result.roles ?? []), ...value.split(",").map((r) => r.trim()).filter(Boolean)];
@@ -971,6 +985,12 @@ export function parseArgs(args: string[]): ParsedArgs {
971
985
  // #2537 — `chant build` and `chant lint` at a declared workspace root
972
986
  // run on the root project alone instead of refusing with WSP000.
973
987
  result.rootOnly = true;
988
+ } else if (arg === "--forge-repos") {
989
+ // #3168 — `chant workspace check --forge-repos <file>`: the forge's
990
+ // repository list, as a warden reads it, for an org workspace's checks.
991
+ const value = args[++i];
992
+ if (!value || value.startsWith("-")) throw new Error("--forge-repos needs a file: --forge-repos <repos.json>");
993
+ result.forgeRepos = value;
974
994
  } else if (arg === "--generated") {
975
995
  // #2641 — `chant workspace check --generated` runs each declared
976
996
  // generator and compares its output with the file in the tree.
@@ -991,6 +1011,11 @@ export function parseArgs(args: string[]): ParsedArgs {
991
1011
  result.url = args[++i];
992
1012
  } else if (arg === "--op") {
993
1013
  result.op = args[++i];
1014
+ } else if (arg === "--where") {
1015
+ // #4363 — a typed filter on a contract read; repeatable, every clause has to hold.
1016
+ const value = args[++i];
1017
+ if (value === undefined || value.startsWith("--")) throw new Error("--where needs a clause: --where <field><op><value>");
1018
+ result.where = [...(result.where ?? []), value];
994
1019
  } else if (arg === "--since") {
995
1020
  result.since = args[++i];
996
1021
  } else if (arg === "--limit") {
@@ -83,7 +83,7 @@ export interface ParsedArgs {
83
83
  /** `chant run events <run> --follow` (#4042) — stream the run's events as they happen, until `run-settle`. */
84
84
  follow?: boolean;
85
85
  live: boolean;
86
- /** `chant migrate --from <name>` (default "github") */
86
+ /** `chant migrate --from <name>` (default "github"); `lexicon/dialect` for the sql lexicon */
87
87
  migrateFrom?: string;
88
88
  /** `chant import --kustomize <dir>` — render the kustomization, then import (#1548) */
89
89
  kustomize?: string;
@@ -103,9 +103,9 @@ export interface ParsedArgs {
103
103
  write?: boolean;
104
104
  /** `chant carve apply --write-source` — stamp the ownership marker into the emitted source */
105
105
  writeSource?: boolean;
106
- /** `chant migrate --to <name>` (default "gitlab") */
106
+ /** `chant migrate --to <name>` (default "gitlab"); `lexicon/dialect` for the sql lexicon */
107
107
  migrateTo?: string;
108
- /** `chant migrate --emit yaml|ts` */
108
+ /** `chant migrate --emit yaml|ts|sql` */
109
109
  emit?: string;
110
110
  /** Escalate needs-review diagnostics to errors (migrate command) */
111
111
  strict?: boolean;
@@ -263,6 +263,12 @@ export interface ParsedArgs {
263
263
  head?: string;
264
264
  /** `chant lifecycle affected --include-dependents` — add downstream consumers */
265
265
  includeDependents?: boolean;
266
+ /** `chant workspace run --dependents-of <member> --op <op>` (#2554): run the Op in every member that links to this one, directly or not. */
267
+ dependentsOf?: string;
268
+ /** `chant workspace lint --cross-root` (#2549): also run the disagreement lint and the lexicons' cross-root checks over every member's graph. */
269
+ crossRoot?: boolean;
270
+ /** `chant workspace train <env> --pin <member>=<label|digest>` (#2554), repeatable: the release a train pins for that member instead of its newest. */
271
+ pins?: string[];
266
272
  /**
267
273
  * `chant components fan-out --from-affected <file>` (#2420) — read the
268
274
  * stack-level change signal from the JSON `chant lifecycle affected --json`
@@ -640,6 +646,8 @@ export interface ParsedArgs {
640
646
  * generators run member code.
641
647
  */
642
648
  generated?: boolean;
649
+ /** `chant workspace check --forge-repos <file>` (#3168): the forge's repository list, checked against an org workspace's repo members (WSP014, WSP015). */
650
+ forgeRepos?: string;
643
651
  /**
644
652
  * `chant workspace build|lint|audit|graph --member <name>` (#2537): run only
645
653
  * the named members or example groups. Repeatable, and a comma list works too.
@@ -788,7 +796,7 @@ export interface ParsedArgs {
788
796
  * on a model-authored channel.
789
797
  */
790
798
  allowSameOrigin?: boolean;
791
- /** `chant approve --role <name>` (#2508) — roles the approver claims, which a gate's policy sees. A quorum never counts them (#4290): its roles come from `.chant/trust.json` at base. Repeatable; a comma list also works. */
799
+ /** `chant approve --role <name>` (#2508) — roles the approver claims, recorded on the approval. Nothing trusts them: a quorum (#4290) and a gate policy (#4324) read roles from `.chant/trust.json` at base, for a sealed approval. Repeatable; a comma list also works. */
792
800
  roles?: string[];
793
801
  /** `chant approve --agent` (#2508) — record the approval as an agent's. An agent never counts toward a quorum; it passes a gate only through a policy permit in `enforce` mode. */
794
802
  agent?: boolean;
@@ -798,6 +806,8 @@ export interface ParsedArgs {
798
806
  op?: string;
799
807
  /** `chant operator log --since <iso>` (#2029) — only entries at or after this ISO-8601 instant. `chant workspace records --since <rev>` (#2673) — the revision to compare the records with. `chant workspace metrics --since <date>` (#4039) — the start of the window the metrics count. */
800
808
  since?: string;
809
+ /** `chant ledger`, `chant workspace records` and `chant workspace runs --where <field><op><value>` (#4363): typed filters, repeatable, all of which have to hold. */
810
+ where?: string[];
801
811
  /** `chant operator log --limit <n>` (#2029) — keep only the newest n entries (still printed oldest-first). */
802
812
  limit?: number;
803
813
  /** `chant approve <op> <gate> --url <url>` (#2028) — the address this resolution happened at (the PR/MR that carried the change), recorded typed on the gate-resolution fact so a reader is not sniffing `--note` for something link-shaped. Defaults to the PR/MR the surrounding CI job is for, when there is one. Must be an absolute http/https URL. */
@@ -19,6 +19,8 @@ import type {
19
19
  ZipPackageInput,
20
20
  JvmBuildInput,
21
21
  GenerateSbomInput,
22
+ PublishImageInput,
23
+ PublishArtifactInput,
22
24
  SignInput,
23
25
  AttestProvenanceInput,
24
26
  VerifyInput,
@@ -64,6 +66,12 @@ export const vulnGate = step<VulnGateInput>("vuln-gate");
64
66
  // The cloud-specific waits (`wait-for-stack`/`wait-steady-state`/`wait-job`)
65
67
  // ship as builders from the aws lexicon, alongside their capabilities.
66
68
  export const waitClusterHealthy = step<WaitClusterHealthyInput>("wait-cluster-healthy");
69
+ // ── publish (core owns the verb; each cloud lexicon contributes a registry login backend) ──
70
+ export const publishImage = step<PublishImageInput>("publish-image");
71
+ export const publishArtifact = step<PublishArtifactInput>("publish-artifact");
72
+ /** Alias for {@link publishArtifact}. */
73
+ export const publishAsset = publishArtifact;
74
+
67
75
  export const waitEndpoint = step<WaitEndpointInput>("wait-endpoint");
68
76
  export const healthGate = step<HealthGateInput>("health-gate");
69
77
 
@@ -26,6 +26,8 @@ import { lexiconModulePath, importLexiconPackage } from "../lexicon-module";
26
26
  import type { CapabilityPlugin } from "./capability-plugin";
27
27
  import { isCapabilityPlugin } from "./capability-plugin";
28
28
  import { starterCapabilityPlugin } from "./starter-plugin";
29
+ import { isRegistryBackendConsumer, type RegistryLoginBackend } from "./verbs/publish-image";
30
+ import { isStorageBackendConsumer, type ArtifactStorageBackend } from "./verbs/publish-artifact";
29
31
 
30
32
  /** Thrown when a dynamically-imported capability package does not satisfy `CapabilityPlugin` — the "malformed capability package" case #559 asks doctor/validation to detect. */
31
33
  export class MalformedCapabilityPluginError extends Error {
@@ -165,9 +167,19 @@ export function registerCapabilityPlugins(
165
167
  }
166
168
  }
167
169
 
170
+ const backends: RegistryLoginBackend[] = [];
171
+ for (const plugin of plugins) backends.push(...(plugin.registryBackends?.() ?? []));
172
+ const storage: ArtifactStorageBackend[] = [];
173
+ for (const plugin of plugins) storage.push(...(plugin.storageBackends?.() ?? []));
174
+
168
175
  for (const plugin of plugins) {
169
176
  for (const capability of plugin.capabilities()) {
170
- registry.register(capability);
177
+ // A capability that consumes registry login backends (`publish-image`)
178
+ // is re-bound to the backends of the plugins loaded for this registry.
179
+ let bound = capability;
180
+ if (isRegistryBackendConsumer(bound)) bound = bound.withRegistryBackends(backends) as typeof bound;
181
+ if (isStorageBackendConsumer(bound)) bound = bound.withStorageBackends(storage) as typeof bound;
182
+ registry.register(bound);
171
183
  }
172
184
  }
173
185
  return registry;
@@ -32,6 +32,8 @@ import { readFileSync } from "node:fs";
32
32
  import { dirname, join } from "node:path";
33
33
  import { fileURLToPath } from "node:url";
34
34
  import type { Capability } from "./capability";
35
+ import type { RegistryLoginBackend } from "./verbs/publish-image";
36
+ import type { ArtifactStorageBackend } from "./verbs/publish-artifact";
35
37
 
36
38
  /**
37
39
  * Manifest for a packaged capability plugin — the capability-side analogue
@@ -88,6 +90,20 @@ export interface CapabilityPlugin {
88
90
  /** Capability kinds grouped by family, informational — mirrors `STARTER_VERB_FAMILIES` (./registry.ts) shape for third-party plugins that want to publish the same grouping. */
89
91
  families?(): Record<string, readonly string[]>;
90
92
 
93
+ /**
94
+ * Registry login backends this plugin contributes to core's `publish-image`
95
+ * verb (aws: ECR, azure: ACR, gcp: Artifact Registry). Not capabilities, so
96
+ * several plugins may contribute without a duplicate-kind clash.
97
+ */
98
+ registryBackends?(): RegistryLoginBackend[];
99
+
100
+ /**
101
+ * Object-storage backends this plugin contributes to core's
102
+ * `publish-artifact` verb (aws: S3, azure: Blob, gcp: GCS). Like
103
+ * `registryBackends`, not capabilities, so several plugins may contribute.
104
+ */
105
+ storageBackends?(): ArtifactStorageBackend[];
106
+
91
107
  /** Optional initialization hook, called once after load (mirrors `LexiconPlugin.init`). */
92
108
  init?(): void | Promise<void>;
93
109
  }
@@ -8,6 +8,7 @@ import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSy
8
8
  import { join } from "node:path";
9
9
  import { tmpdir } from "node:os";
10
10
  import { parseDeclaration, WorkspaceReadError } from "../workspace/declaration";
11
+ import { PR_LOOP_HOOK_MOMENTS } from "../op/op-waves";
11
12
  import { prLoopHooksAt, prLoopHooksOf, runPrHooks } from "./pr-hooks";
12
13
  import type { OpWaveExec } from "../op/op-waves-run";
13
14
 
@@ -30,6 +31,12 @@ const declaration = (hooks: unknown, memberHooks?: unknown) => ({
30
31
  });
31
32
 
32
33
  describe("ci.hooks in the declaration (#4288)", () => {
34
+ test("accepts after.gate, and runs it after the plan-side moments (#4323)", () => {
35
+ const d = parseDeclaration(JSON.stringify(declaration({ after: { gate: [{ name: "report", command: ["report"] }] } }, { after: { gate: [{ name: "member-report", command: ["mr"] }] } })), "chant.workspace.json", "0.124.41");
36
+ expect(prLoopHooksOf(d, "infra")["after-gate"]!.map((h) => h.name)).toEqual(["report", "member-report"]);
37
+ expect(PR_LOOP_HOOK_MOMENTS).toContain("after-gate");
38
+ });
39
+
33
40
  test("parses at the top level and on a member; the member's run after the top level's", () => {
34
41
  const d = parseDeclaration(
35
42
  JSON.stringify(declaration({ before: { observe: [{ name: "init", command: ["init"] }] } }, { before: { observe: [{ name: "member-init", command: ["mi"], targets: ["db*"] }] } })),
@@ -43,7 +50,7 @@ describe("ci.hooks in the declaration (#4288)", () => {
43
50
 
44
51
  test("refuses hooks a pull-request job cannot run", () => {
45
52
  const read = (hooks: unknown) => () => parseDeclaration(JSON.stringify(declaration(hooks)), "chant.workspace.json", "0.124.41");
46
- expect(read({ after: { gate: [{ command: ["x"] }] } })).toThrow(WorkspaceReadError);
53
+ expect(read({ before: { plan: [{ command: ["x"], onFailure: "bogus" }] } })).toThrow(WorkspaceReadError);
47
54
  expect(read({ before: { plan: [{ command: [] }] } })).toThrow(/must be a command as argv/);
48
55
  expect(read({ apply: { runBy: "apply" } })).toThrow(/runBy is for an Op waves spec/);
49
56
  });
@@ -84,7 +91,7 @@ describe("prLoopHooksAt (#4288)", () => {
84
91
  });
85
92
 
86
93
  describe("runPrHooks (#4288)", () => {
87
- test("runs a moment for the selected components, writes the runs and plan files, and reports a failure", () => {
94
+ test("runs a moment for the selected components, writes the runs and plan files, and reports a failure", async () => {
88
95
  const cwd = tmp();
89
96
  const calls: Array<{ argv: string; env?: Record<string, string> }> = [];
90
97
  const exec: OpWaveExec = (argv, _dir, env) => {
@@ -95,7 +102,7 @@ describe("runPrHooks (#4288)", () => {
95
102
  base: "b".repeat(40),
96
103
  hooks: { "after-plan": [{ name: "check", command: ["check", "{target}", "{plan}"], perRun: true, targets: ["api", "web"] }] },
97
104
  };
98
- const ran = runPrHooks(read, "after-plan", { op: "pr-7", env: "prod", stage: "plan", dir: ".chant/pr", cwd, exec, log: () => {} }, ["api", "db", "web"], {
105
+ const ran = await runPrHooks(read, "after-plan", { op: "pr-7", env: "prod", stage: "plan", dir: ".chant/pr", cwd, exec, log: () => {} }, ["api", "db", "web"], {
99
106
  members: [{ member: "api", component: "api", planDigest: "sha256:1", status: "planned", counts: { create: 1, update: 0, replace: 0, delete: 0 } }],
100
107
  plans: { api: { api: { file: "plan.bin" } } },
101
108
  });
@@ -110,9 +117,70 @@ describe("runPrHooks (#4288)", () => {
110
117
  expect(ran.failed).toBe("after-plan hook check (web) exited 1");
111
118
  });
112
119
 
113
- test("a moment with no hooks runs nothing and writes nothing", () => {
120
+ test("a moment with no hooks runs nothing and writes nothing", async () => {
114
121
  const cwd = tmp();
115
- const ran = runPrHooks({ base: "b", hooks: {} }, "before-apply", { op: "pr-7", env: "prod", stage: "apply", dir: ".chant/pr", cwd, log: () => {} }, ["api"]);
122
+ const ran = await runPrHooks({ base: "b", hooks: {} }, "before-apply", { op: "pr-7", env: "prod", stage: "apply", dir: ".chant/pr", cwd, log: () => {} }, ["api"]);
116
123
  expect(ran).toEqual({ outcomes: [], holds: [] });
117
124
  });
125
+
126
+ const holdBody = { gate: "db-migrate", digest: "sha256:m1", op: "other-op" };
127
+ /** An exec for a hook that holds on db-migrate until CHANT_HOLD_APPROVED is set. */
128
+ const holdingExec = (cwd: string, calls: Array<Record<string, string> | undefined>): OpWaveExec => (argv, _dir, env) => {
129
+ calls.push(env);
130
+ if (env?.CHANT_HOLD_APPROVED) return 0;
131
+ writeFileSync(join(cwd, env!.CHANT_HOLD!), JSON.stringify(holdBody));
132
+ return argv[0] === "hold" ? 1 : 0;
133
+ };
134
+ const gateHooks = { "after-gate": [{ name: "migrate", command: ["hold"], onFailure: "approve" as const }] };
135
+ const place = (cwd: string, exec: OpWaveExec) => ({ op: "pr-7", env: "prod", stage: "apply" as const, dir: ".chant/pr", cwd, exec, log: () => {} });
136
+
137
+ test("an after-gate hook gets the gate's decision in its environment (#4323)", async () => {
138
+ const cwd = tmp();
139
+ const calls: Array<Record<string, string> | undefined> = [];
140
+ const exec: OpWaveExec = (_argv, _dir, env) => (calls.push(env), 0);
141
+ const read = { base: "b", hooks: { "after-gate": [{ name: "report", command: ["report"] }] } };
142
+ const decision = { CHANT_GATE_STATUS: "approved", CHANT_GATE_OP: "pr-7", CHANT_GATE: "pr-apply", CHANT_GATE_DIGEST: "sha256:p" };
143
+ const ran = await runPrHooks(read, "after-gate", place(cwd, exec), ["api"], undefined, { env: decision });
144
+ expect(calls[0]).toMatchObject({ CHANT_HOOK: "after-gate", ...decision });
145
+ expect(ran.outcomes).toEqual([{ moment: "after-gate", name: "report", exit: 0, outcome: "passed" }]);
146
+ });
147
+
148
+ test("a hold on another gate clears once that gate is approved: the hook runs again with CHANT_HOLD_APPROVED (#4323)", async () => {
149
+ const cwd = tmp();
150
+ const calls: Array<Record<string, string> | undefined> = [];
151
+ let approved = false;
152
+ const asked: string[] = [];
153
+ const settle = async (hold: { request: { gate: string; digest: string; op?: string } }) => {
154
+ asked.push(`${hold.request.op} / ${hold.request.gate} @ ${hold.request.digest}`);
155
+ return approved ? { status: "approved" as const, approvedBy: "alice", approvedAt: "2026-10-23T00:00:00Z" } : { status: "waiting" as const };
156
+ };
157
+ const read = { base: "b", hooks: gateHooks };
158
+ const first = await runPrHooks(read, "after-gate", place(cwd, holdingExec(cwd, calls)), ["api"], undefined, { settle });
159
+ expect(first.failed).toMatch(/^after-gate hook migrate waits on other-op \/ db-migrate, which nobody approved at sha256:m1\. Approve: /);
160
+ expect(first.holds).toEqual([]);
161
+
162
+ approved = true;
163
+ calls.length = 0;
164
+ const second = await runPrHooks(read, "after-gate", place(cwd, holdingExec(cwd, calls)), ["api"], undefined, { settle });
165
+ expect(second.failed).toBeUndefined();
166
+ expect(second.holds).toEqual([]);
167
+ expect(asked).toEqual(["other-op / db-migrate @ sha256:m1", "other-op / db-migrate @ sha256:m1"]);
168
+ expect(JSON.parse(calls[1]!.CHANT_HOLD_APPROVED!)).toEqual({ op: "other-op", gate: "db-migrate", digest: "sha256:m1", approvedBy: "alice", approvedAt: "2026-10-23T00:00:00Z" });
169
+ expect(second.outcomes.map((o) => o.outcome)).toEqual(["held", "passed"]);
170
+ });
171
+
172
+ test("a re-run that holds on a gate already approved fails the stage (#4323)", async () => {
173
+ const cwd = tmp();
174
+ const exec: OpWaveExec = (_argv, _dir, env) => (writeFileSync(join(cwd, env!.CHANT_HOLD!), JSON.stringify(holdBody)), 1);
175
+ const settle = async () => ({ status: "approved" as const, approvedBy: "alice", approvedAt: "t" });
176
+ const ran = await runPrHooks({ base: "b", hooks: gateHooks }, "after-gate", place(cwd, exec), ["api"], undefined, { settle });
177
+ expect(ran.failed).toMatch(/holds again on other-op \/ db-migrate at sha256:m1, which is already approved/);
178
+ });
179
+
180
+ test("without a settler a hold on another gate stays a plain hold", async () => {
181
+ const cwd = tmp();
182
+ const ran = await runPrHooks({ base: "b", hooks: gateHooks }, "after-gate", place(cwd, holdingExec(cwd, [])), ["api"]);
183
+ expect(ran.holds).toEqual(["after-gate hook migrate (on other-op / db-migrate)"]);
184
+ expect(ran.failed).toBeUndefined();
185
+ });
118
186
  });