@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
@@ -56,6 +56,7 @@ import {
56
56
  } from "./records";
57
57
  import { readCheckoutHead, worktreeStates, type CheckoutView, type WorktreeState } from "./records-checkout";
58
58
  import { lastWriteReader, type LastWrite } from "./write-lock";
59
+ import { FilterError, matchesWhere, parseWhere, type FilterFields, type WhereClause } from "../read-filter";
59
60
 
60
61
  function readTextOrNull(file: string): string | null {
61
62
  try {
@@ -78,7 +79,7 @@ export const RECORDS_CONTRACT_VERSION = 1;
78
79
  export const RECORDS_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/records/v1/records.schema.json";
79
80
 
80
81
  const USAGE =
81
- "chant workspace records [--kind <kind file>] [--current] [--uncommitted | --at <rev>] [--base <rev>] [--require attested] [--json] | chant workspace records [--kind <kind file>] --since <rev|session id> [--at <rev>] [--json] | chant workspace records pin <path> [--kind <kind file>] | chant workspace records new|amend|review|close (#2670, #2693)";
82
+ "chant workspace records [--kind <kind file>] [--current] [--uncommitted | --at <rev>] [--base <rev>] [--require attested] [--where <field><op><value>]... [--json] | chant workspace records [--kind <kind file>] --since <rev|session id> [--at <rev>] [--json] | chant workspace records pin <path> [--kind <kind file>] | chant workspace records new|amend|review|close (#2670, #2693)";
82
83
 
83
84
  /** Exit code when the read worked and a record falls below `--require`. */
84
85
  export const EXIT_BELOW_REQUIRED = 2;
@@ -196,6 +197,8 @@ export type RecordsDocument =
196
197
  summary: { total: number; valid: number; invalid: number; superseded: number };
197
198
  /** For a work kind (#2683): each decision its decision kind reads, with the work records implementing it. */
198
199
  decisions?: DecisionWork[];
200
+ /** The `--where` clauses the records were filtered with (#4363); absent without one. `summary` then counts the records listed. */
201
+ where?: string[];
199
202
  }
200
203
  | { $schema: string; contract: number; error: { code: ReadErrorCode; message: string } };
201
204
 
@@ -697,7 +700,12 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
697
700
  console.error(formatError({ message: pairing, hint: USAGE }));
698
701
  return 1;
699
702
  }
700
- if (!args.kind) return runDeclaredRecords(args);
703
+ const filter = recordsWhere(args);
704
+ if ("error" in filter) {
705
+ console.error(formatError({ message: filter.error, hint: USAGE }));
706
+ return 1;
707
+ }
708
+ if (!args.kind) return runDeclaredRecords(args, filter.clauses);
701
709
  if (args.require !== undefined && args.require !== "attested") {
702
710
  console.error(formatError({ message: `--require takes one level, attested, not ${JSON.stringify(args.require)}`, hint: USAGE }));
703
711
  return 1;
@@ -715,7 +723,7 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
715
723
  else console.log(formatSince(since));
716
724
  return "error" in since ? 1 : 0;
717
725
  }
718
- const doc = await queryRecords({ kind: args.kind, current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd(), fetchLedger: true });
726
+ const doc = filterRecordsDocument(await queryRecords({ kind: args.kind, current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd(), fetchLedger: true }), filter.clauses, args.where ?? []);
719
727
  if (args.json) {
720
728
  console.log(JSON.stringify(doc, null, 2));
721
729
  } else if ("error" in doc) {
@@ -756,7 +764,7 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
756
764
  * declaration-level code the schema doesn't carry (`declaration-invalid` and
757
765
  * the rest of `WorkspaceErrorCode`) still prints text on stderr only.
758
766
  */
759
- async function runDeclaredRecords(args: CommandContext["args"]): Promise<number> {
767
+ async function runDeclaredRecords(args: CommandContext["args"], clauses: readonly WhereClause[] = []): Promise<number> {
760
768
  if (args.require !== undefined && args.require !== "attested") {
761
769
  console.error(formatError({ message: `--require takes one level, attested, not ${JSON.stringify(args.require)}`, hint: USAGE }));
762
770
  return 1;
@@ -779,7 +787,9 @@ async function runDeclaredRecords(args: CommandContext["args"]): Promise<number>
779
787
  return 1;
780
788
  }
781
789
  if (args.since !== undefined) return runDeclaredSince(declared, args);
782
- const set = await queryDeclaredRecords(declared, { current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd(), fetchLedger: true });
790
+ const read = await queryDeclaredRecords(declared, { current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd(), fetchLedger: true });
791
+ // The spec stays the whole current spec; --where narrows only the records each kind lists.
792
+ const set = { ...read, kinds: read.kinds.map((k) => filterRecordsDocument(k, clauses, args.where ?? [])) };
783
793
  if (args.json) console.log(JSON.stringify(set, null, 2));
784
794
  for (const doc of args.json ? [] : set.kinds) {
785
795
  if ("error" in doc) {
@@ -854,6 +864,49 @@ export function belowRequired(records: RecordView[], required: ProvenanceLevel):
854
864
  }
855
865
 
856
866
  /** Why `--uncommitted` can't be given with the other flags given, or undefined when it can (#3160). */
867
+ /**
868
+ * The fields `records --where` filters on (#4363). `data` is the record's
869
+ * own fields, so `data.<key>` names any of them.
870
+ */
871
+ export const RECORD_FILTER_FIELDS: FilterFields = {
872
+ id: "text",
873
+ path: "text",
874
+ state: "text",
875
+ valid: "boolean",
876
+ supersededBy: "text",
877
+ digest: "text",
878
+ ready: "boolean",
879
+ attested: "boolean",
880
+ worktree: "text",
881
+ ledger: "text",
882
+ "provenance.level": "text",
883
+ data: "json",
884
+ };
885
+
886
+ /** Keep the records every clause matches, counting `summary` over the records listed. A failure is left as it is. */
887
+ export function filterRecordsDocument<D extends RecordsDocument>(doc: D, clauses: readonly WhereClause[], where: readonly string[]): D {
888
+ if (where.length === 0 || "error" in doc) return doc;
889
+ const records = doc.records.filter((r) => matchesWhere(r, clauses));
890
+ return {
891
+ ...doc,
892
+ records,
893
+ summary: { ...doc.summary, total: records.length, valid: records.filter((r) => r.valid).length, invalid: records.filter((r) => !r.valid).length },
894
+ where: [...where],
895
+ };
896
+ }
897
+
898
+ /** The `--where` clauses, or a message for a person. */
899
+ function recordsWhere(args: CommandContext["args"]): { clauses: WhereClause[] } | { error: string } {
900
+ const where = args.where ?? [];
901
+ if (where.length > 0 && args.since !== undefined) return { error: "--where filters a records read and takes no --since: --since compares two revisions" };
902
+ try {
903
+ return { clauses: parseWhere(where, RECORD_FILTER_FIELDS) };
904
+ } catch (err) {
905
+ if (err instanceof FilterError) return { error: err.message };
906
+ throw err;
907
+ }
908
+ }
909
+
857
910
  function uncommittedPairing(args: CommandContext["args"]): string | undefined {
858
911
  if (!args.uncommitted) return undefined;
859
912
  if (args.at !== undefined) return "--uncommitted reads the working tree against HEAD, and takes no --at";
@@ -152,6 +152,11 @@
152
152
  "description": "Added in contract 1 by #2683, for a work kind only. Every decision the kind file named in the work kind's work.decisions reads at the same revision, whether or not a work record names it, each with the work records implementing it. A decision with an empty implementedBy has no work item yet.",
153
153
  "type": "array",
154
154
  "items": { "$ref": "#/$defs/decisionWork" }
155
+ },
156
+ "where": {
157
+ "description": "Added in contract 1 by #4363. Each --where clause, <field><op><value>, as given; every one held for each record listed, and summary's total, valid and invalid count the records listed. Absent without --where.",
158
+ "type": "array",
159
+ "items": { "type": "string" }
155
160
  }
156
161
  },
157
162
  "allOf": [
@@ -39,7 +39,7 @@ import { latestPerComponent, readReleaseLedger } from "../lifecycle/release-ledg
39
39
  import { readAllReleases, readCuts } from "../lifecycle/release-store";
40
40
  import { resolveMemberLedger } from "../lifecycle/member-ledger";
41
41
  import { readBlobFromPath } from "../lifecycle/git";
42
- import { readerVersion } from "./declaration";
42
+ import { greenBranches, readerVersion } from "./declaration";
43
43
 
44
44
  export const RELEASE_CANDIDATES_CONTRACT_VERSION = 1;
45
45
  export const RELEASE_CANDIDATES_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/release-candidates/v1/release-candidates.schema.json";
@@ -145,6 +145,8 @@ export interface ReleaseCandidatesQuery {
145
145
  /** Only this environment's view: the walk stops at what it runs, and `newFor` names only it. */
146
146
  env?: string;
147
147
  limit?: number;
148
+ /** Walk this branch of `ci.green.branch` (#4325) instead of the first. */
149
+ branch?: string;
148
150
  }
149
151
 
150
152
  function git(top: string, args: string[]): string {
@@ -218,7 +220,10 @@ export async function releaseCandidatesDocument(query: ReleaseCandidatesQuery):
218
220
  const { commitDetails } = await import("./intent");
219
221
  const source = scopeSource(cwd);
220
222
  const green = source.declaration?.ci?.green ?? null;
221
- const trunk = resolveTrunk(top, green?.branch ?? null);
223
+ if (query.branch !== undefined && !(green && greenBranches(green).includes(query.branch))) {
224
+ throw new Error(`--branch ${query.branch} is not a branch ci.green judges${green ? ` (${greenBranches(green).join(", ")})` : ", and the declaration has no ci.green"}`);
225
+ }
226
+ const trunk = resolveTrunk(top, query.branch ?? green?.branch ?? null);
222
227
  const tip =
223
228
  tryGit(top, ["rev-parse", "--verify", "--quiet", `refs/remotes/${trunk.remote}/${trunk.branch}^{commit}`]) ??
224
229
  tryGit(top, ["rev-parse", "--verify", "--quiet", `refs/heads/${trunk.branch}^{commit}`]);
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Release trains (#2554, #2524 D19, ws-043): one record that pins a release
3
+ * of every `chant` member, so several members move together.
4
+ *
5
+ * Releases stay per member (ws-043): each is a digest over its artifacts and
6
+ * a git commit, written to the member's own ledger by `chant release cut`. A
7
+ * train names one of them per member: by default the newest cut release, or
8
+ * the one `--pin <member>=<label|digest>` picks. It refuses unless every
9
+ * `chant` member of the declaration has a release, so a train never leaves a
10
+ * member out. It is written to `_members/_workspace/<env>/trains.jsonl`.
11
+ */
12
+
13
+ import { randomUUID } from "node:crypto";
14
+ import { resolve } from "node:path";
15
+ import { formatError } from "../cli/format";
16
+ import type { CommandContext } from "../cli/registry";
17
+ import { readAllReleases, readCuts } from "../lifecycle/release-store";
18
+ import { readDeclaration, WorkspaceReadError } from "./declaration";
19
+ import { locateWorkspace } from "./which-chant";
20
+ import { appendWorkspaceLine, readWorkspaceLines } from "./workspace-ledger";
21
+
22
+ export interface TrainMember {
23
+ member: string;
24
+ releaseDigest: string;
25
+ /** The release's git commit. */
26
+ commit: string;
27
+ /** The label its newest cut gave it, or null. */
28
+ label: string | null;
29
+ }
30
+
31
+ export interface ReleaseTrainRecord {
32
+ version: 1;
33
+ id: string;
34
+ env: string;
35
+ timestamp: string;
36
+ actor: string;
37
+ /** Every `chant` member, by name. */
38
+ members: TrainMember[];
39
+ }
40
+
41
+ const TRAINS_FILE = "trains.jsonl";
42
+
43
+ /** What one member offers a train: its releases, newest cut first. */
44
+ export interface MemberReleases {
45
+ member: string;
46
+ releases: TrainMember[];
47
+ }
48
+
49
+ /**
50
+ * Pick each member's release, pure. `pins` maps a member to a label or a
51
+ * digest; a member with no pin takes its newest. Returns the members, or
52
+ * the problems that stop the train.
53
+ */
54
+ export function pickTrain(members: readonly MemberReleases[], pins: ReadonlyMap<string, string> = new Map()): { members: TrainMember[] } | { problems: string[] } {
55
+ const problems: string[] = [];
56
+ const picked: TrainMember[] = [];
57
+ for (const name of pins.keys()) {
58
+ if (!members.some((m) => m.member === name)) problems.push(`--pin ${name}: not a chant member of the workspace`);
59
+ }
60
+ for (const m of members) {
61
+ const pin = pins.get(m.member);
62
+ const r = pin === undefined ? m.releases[0] : m.releases.find((x) => x.releaseDigest === pin || x.label === pin);
63
+ if (!r) {
64
+ problems.push(pin === undefined ? `member ${m.member} has no release; cut one with chant release cut in its directory` : `member ${m.member} has no release ${pin}`);
65
+ continue;
66
+ }
67
+ picked.push(r);
68
+ }
69
+ return problems.length > 0 ? { problems } : { members: picked };
70
+ }
71
+
72
+ /** Every `chant` member's releases, newest cut first, read from its ledger. */
73
+ export async function readMemberReleases(cwd: string): Promise<MemberReleases[]> {
74
+ const located = locateWorkspace(cwd);
75
+ const declaration = readDeclaration(located.tree, "", { rootChant: true });
76
+ const out: MemberReleases[] = [];
77
+ for (const m of declaration.members) {
78
+ if (m.kind !== "chant") continue;
79
+ const loc = { cwd: located.rootOnDisk, member: m.dir === "." ? "." : m.name };
80
+ const { releases } = await readAllReleases(loc);
81
+ const { cuts } = await readCuts(loc);
82
+ const newest = (digest: string) => cuts.filter((c) => c.releaseDigest === digest).map((c) => c.timestamp).sort().at(-1) ?? "";
83
+ const label = (digest: string) => cuts.filter((c) => c.releaseDigest === digest && c.label !== null).sort((a, b) => (a.timestamp < b.timestamp ? -1 : 1)).at(-1)?.label ?? null;
84
+ const list = releases
85
+ .filter((r) => newest(r.releaseDigest) !== "")
86
+ .sort((a, b) => (newest(b.releaseDigest) < newest(a.releaseDigest) ? -1 : newest(b.releaseDigest) > newest(a.releaseDigest) ? 1 : 0))
87
+ .map((r) => ({ member: m.name, releaseDigest: r.releaseDigest, commit: r.commit, label: label(r.releaseDigest) }));
88
+ out.push({ member: m.name, releases: list });
89
+ }
90
+ return out;
91
+ }
92
+
93
+ function isTrain(v: unknown): v is ReleaseTrainRecord {
94
+ const t = v as ReleaseTrainRecord;
95
+ return typeof t === "object" && t !== null && t.version === 1 && typeof t.id === "string" && typeof t.env === "string" && Array.isArray(t.members);
96
+ }
97
+
98
+ export async function readReleaseTrains(cwd: string, env: string): Promise<{ records: ReleaseTrainRecord[]; malformed: number }> {
99
+ return readWorkspaceLines(cwd, env, TRAINS_FILE, isTrain);
100
+ }
101
+
102
+ export async function appendReleaseTrain(cwd: string, record: ReleaseTrainRecord): Promise<string> {
103
+ return appendWorkspaceLine(cwd, record.env, TRAINS_FILE, record, `Release train: ${record.env} ${record.id}`);
104
+ }
105
+
106
+ const USAGE = "chant workspace train <env> [--pin <member>=<label|digest>]... [--dry-run] [--json] | chant workspace train list <env> [--json]";
107
+
108
+ export async function runWorkspaceTrain(ctx: CommandContext): Promise<number> {
109
+ const words = ctx.args.positionals ?? [];
110
+ const rest = words[0] === "train" ? words.slice(1) : words;
111
+ const listing = rest[0] === "list";
112
+ const env = listing ? rest[1] : rest[0];
113
+ if (!env) {
114
+ console.error(formatError({ message: "chant workspace train needs an environment", hint: USAGE }));
115
+ return 1;
116
+ }
117
+ const cwd = resolve(".");
118
+ try {
119
+ if (listing) {
120
+ const root = locateWorkspace(cwd).rootOnDisk;
121
+ const { records } = await readReleaseTrains(root, env);
122
+ if (ctx.args.json) console.log(JSON.stringify({ env, trains: records }, null, 2));
123
+ else if (records.length === 0) console.log(`No release train recorded for ${env}.`);
124
+ else for (const t of records) console.log(`${t.timestamp} ${t.id} ${t.members.map((m) => `${m.member}@${m.label ?? m.releaseDigest.slice(0, 19)}`).join(" ")}`);
125
+ return 0;
126
+ }
127
+ const pins = new Map<string, string>();
128
+ for (const p of ctx.args.pins ?? []) {
129
+ const at = p.indexOf("=");
130
+ if (at <= 0) {
131
+ console.error(formatError({ message: `--pin ${p}: expected <member>=<label|digest>`, hint: USAGE }));
132
+ return 1;
133
+ }
134
+ pins.set(p.slice(0, at), p.slice(at + 1));
135
+ }
136
+ const root = locateWorkspace(cwd).rootOnDisk;
137
+ const picked = pickTrain(await readMemberReleases(root), pins);
138
+ if ("problems" in picked) {
139
+ console.error(formatError({ message: `no release train for ${env}: every chant member needs a release`, hint: USAGE }));
140
+ for (const p of picked.problems) console.error(` ${p}`);
141
+ return 1;
142
+ }
143
+ const actor = ctx.args.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER ?? "unknown";
144
+ const record: ReleaseTrainRecord = { version: 1, id: `train-${randomUUID()}`, env, timestamp: new Date().toISOString(), actor, members: picked.members };
145
+ if (!ctx.args.dryRun) await appendReleaseTrain(root, record);
146
+ if (ctx.args.json) console.log(JSON.stringify({ ...record, written: !ctx.args.dryRun }, null, 2));
147
+ else {
148
+ console.log(`${ctx.args.dryRun ? "Would record" : "Recorded"} release train ${record.id} for ${env}:`);
149
+ for (const m of record.members) console.log(` ${m.member.padEnd(24)} ${m.label ?? "(no label)"} ${m.releaseDigest} ${m.commit.slice(0, 12)}`);
150
+ }
151
+ return 0;
152
+ } catch (err) {
153
+ if (!(err instanceof WorkspaceReadError)) throw err;
154
+ console.error(formatError({ message: `${err.code}: ${err.describe()}`, hint: USAGE }));
155
+ return 1;
156
+ }
157
+ }
@@ -16,6 +16,7 @@
16
16
  * there: it costs nothing, and with it what the job uploads carries
17
17
  * provenance whether or not the group requires it.
18
18
  */
19
+ import { ACTION_PINS, tagComments, usesRef } from "../action-pins";
19
20
  import { GENERATED_MARKER } from "../discovery/files";
20
21
  import type { SecurityGroup } from "./security";
21
22
 
@@ -91,10 +92,10 @@ export function renderReleaseWorkflow(input: ReleaseWorkflowInput): string {
91
92
  " runs-on: ubuntu-latest",
92
93
  ...(root ? [" defaults:", " run:", ` working-directory: ${root}`] : []),
93
94
  " steps:",
94
- " - uses: actions/checkout@v4",
95
+ ` - uses: ${usesRef(ACTION_PINS.checkout4)}`,
95
96
  " with:",
96
97
  " fetch-depth: 0",
97
- " - uses: actions/setup-node@v4",
98
+ ` - uses: ${usesRef(ACTION_PINS.setupNode4)}`,
98
99
  " with:",
99
100
  " node-version: 24",
100
101
  " registry-url: https://registry.npmjs.org",
@@ -105,7 +106,7 @@ export function renderReleaseWorkflow(input: ReleaseWorkflowInput): string {
105
106
  ...(verify ? [` - run: ${chant} release verify ${label}`] : []),
106
107
  ` - run: ${chant} release publish ${label} ${flags}`,
107
108
  ];
108
- return `${lines.join("\n")}\n`;
109
+ return tagComments(`${lines.join("\n")}\n`);
109
110
  }
110
111
 
111
112
  /** The header that marks the file generated, naming the command that regenerates it. */
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Another repository's workspace, named by URL and pinned revision (#3168).
3
+ *
4
+ * A member of kind `repo` is read the way a nested `workspace` member is
5
+ * (./nested-graph.ts): through that repository's own `chant workspace graph`,
6
+ * at the pinned revision, and nothing is ever written there. To have a tree
7
+ * to run that read in, chant fetches the revision into the user's cache
8
+ * directory, never under the workspace: one clone per URL, and one detached
9
+ * worktree per revision, which is immutable once made, so two reads of one
10
+ * revision share it and a read never moves another's checkout.
11
+ *
12
+ * This is the only place `chant workspace graph` reaches a remote, and only
13
+ * for repo members. `chant workspace check` never fetches.
14
+ *
15
+ * The read contract's identity names the repository a document came from
16
+ * (`workspace.repo`): the URL of the `origin` remote, credentials removed.
17
+ */
18
+
19
+ import { execFileSync } from "node:child_process";
20
+ import { createHash } from "node:crypto";
21
+ import { existsSync, mkdirSync } from "node:fs";
22
+ import { homedir } from "node:os";
23
+ import { isAbsolute, join, resolve } from "node:path";
24
+ import { CACHE_DIR_ENV } from "./graph-cache";
25
+ import { urlCredential } from "./checks/org";
26
+ import type { RepoRef } from "./declaration";
27
+
28
+ /** A URL with any user name and password removed; scp-style and local paths are returned as they are. */
29
+ export function redactUrl(url: string): string {
30
+ if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(url)) return url;
31
+ try {
32
+ const u = new URL(url);
33
+ if (u.username === "" && u.password === "") return url;
34
+ u.username = "";
35
+ u.password = "";
36
+ return u.toString();
37
+ } catch {
38
+ return url;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * The repository a workspace on disk came from (#3168): its git repository's
44
+ * `origin` URL, redacted, or null without git or an origin. Reads local git
45
+ * config only; never reaches the remote.
46
+ */
47
+ export function repoIdentity(dir: string): string | null {
48
+ try {
49
+ const url = execFileSync("git", ["-C", dir, "config", "--get", "remote.origin.url"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
50
+ return url ? redactUrl(url) : null;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /** Where fetched repositories live: `<cache>/repos`, with the cache dir as the graph cache finds it. */
57
+ export function repoCacheDir(env: NodeJS.ProcessEnv = process.env): string {
58
+ const base = env[CACHE_DIR_ENV] || join(env.XDG_CACHE_HOME || join(homedir(), ".cache"), "chant");
59
+ return join(base, "repos");
60
+ }
61
+
62
+ /** The URL git is handed: a relative local path is resolved from the workspace root, since git runs in the cache. */
63
+ export function fetchUrl(url: string, workspaceRoot: string): string {
64
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(url)) return url;
65
+ if (/^(?:[^@/\s]+@)?[^:/\s]+:(?!\/\/)/.test(url) && !isAbsolute(url) && !/^[a-z]:[\\/]/i.test(url)) return url;
66
+ return resolve(workspaceRoot, url);
67
+ }
68
+
69
+ export type RepoFetch = { ok: true; dir: string; worktree: string } | { ok: false; message: string };
70
+
71
+ const GIT_ENV = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
72
+
73
+ function git(args: string[], cwd?: string): { ok: boolean; out: string } {
74
+ try {
75
+ const out = execFileSync("git", args, { cwd, encoding: "utf-8", env: GIT_ENV, stdio: ["ignore", "pipe", "pipe"], timeout: 120_000 });
76
+ return { ok: true, out };
77
+ } catch (err) {
78
+ const e = err as { stderr?: string | Buffer; message: string };
79
+ return { ok: false, out: String(e.stderr ?? e.message).trim().split("\n").slice(-3).join("; ") };
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Make `ref`'s revision available in the cache, fetching it when the cache
85
+ * doesn't hold it, and return the directory of its workspace root there.
86
+ * Never throws; a failure says why.
87
+ */
88
+ export function fetchRepoAt(ref: RepoRef, workspaceRoot: string, env: NodeJS.ProcessEnv = process.env): RepoFetch {
89
+ if (urlCredential(ref.url)) return { ok: false, message: `the repo url carries a credential, which chant refuses to use (WSP016)` };
90
+ const url = fetchUrl(ref.url, workspaceRoot);
91
+ const clone = join(repoCacheDir(env), createHash("sha256").update(url).digest("hex").slice(0, 24));
92
+ const worktree = join(clone, "at", ref.rev);
93
+ const dir = ref.workspace === "." ? worktree : join(worktree, ...ref.workspace.split("/"));
94
+ if (existsSync(join(worktree, ".git"))) return { ok: true, dir, worktree };
95
+ if (!existsSync(join(clone, ".git"))) {
96
+ mkdirSync(clone, { recursive: true });
97
+ const init = git(["init", "-q", clone]);
98
+ if (!init.ok) return { ok: false, message: `could not make the cache clone in ${clone}: ${init.out}` };
99
+ git(["-C", clone, "remote", "add", "origin", url]);
100
+ }
101
+ const has = () => git(["-C", clone, "cat-file", "-e", `${ref.rev}^{commit}`]).ok;
102
+ if (!has()) {
103
+ // A pinned commit by id first; a server that refuses unadvertised ids gets a full fetch.
104
+ const byId = git(["-C", clone, "fetch", "-q", "origin", ref.rev]);
105
+ if (!has()) {
106
+ const all = git(["-C", clone, "fetch", "-q", "origin", "+refs/heads/*:refs/remotes/origin/*", "+refs/tags/*:refs/tags/*"]);
107
+ if (!has()) return { ok: false, message: `could not fetch ${ref.rev} from ${redactUrl(ref.url)}: ${all.ok ? (byId.ok ? "the repository has no such commit" : byId.out) : all.out}` };
108
+ }
109
+ }
110
+ const add = git(["-C", clone, "worktree", "add", "-q", "--detach", worktree, ref.rev]);
111
+ if (!add.ok && !existsSync(join(worktree, ".git"))) return { ok: false, message: `could not check out ${ref.rev} in the cache: ${add.out}` };
112
+ return { ok: true, dir, worktree };
113
+ }
@@ -296,8 +296,8 @@
296
296
  "resolvedBy": { "type": "string", "description": "Who approved it." },
297
297
  "resolvedAt": { "type": "string", "description": "When the approval was recorded, ISO-8601." },
298
298
  "via": {
299
- "enum": ["quorum", "policy", "point", "alias", "empty", null],
300
- "description": "How the approval passed when it was more than one person's approval of this gate: a quorum, a policy permit, a decision point's answer, or an approval recorded under one of the gate's aliases. empty (added within contract 1 by #4045, chant 0.124.28 and newer): a release gate passed over an empty change set with no approval, on the resolution the promote wrote, whose resolvedBy is the actor running it."
299
+ "enum": ["quorum", "policy", "point", "alias", "empty", "workspace", null],
300
+ "description": "How the approval passed when it was more than one person's approval of this gate: a quorum, a policy permit, a decision point's answer, or an approval recorded under one of the gate's aliases. empty (added within contract 1 by #4045, chant 0.124.28 and newer): a release gate passed over an empty change set with no approval, on the resolution the promote wrote, whose resolvedBy is the actor running it. workspace (added within contract 1 by #2554, chant 0.124.47 and newer): a workspace gate's approval stood in for this member gate, because the member opts in at base and the approval covers this plan."
301
301
  },
302
302
  "url": { "type": "string", "description": "Where the approval was made, when known." }
303
303
  }
@@ -49,6 +49,7 @@ import {
49
49
  type RunWriteContext,
50
50
  } from "./runs";
51
51
  import { RUN_TRAILER } from "./trailers";
52
+ import { FilterError, matchesWhere, parseWhere, type FilterFields } from "../read-filter";
52
53
  import { locateWorkspace } from "./which-chant";
53
54
  import { idList } from "./work";
54
55
  import { AGENT_ENV } from "./write-scope";
@@ -71,7 +72,7 @@ export const RUNS_WRITE_SCHEMA_ID = "https://intentius.io/chant/schemas/workspac
71
72
  export const RUN_STATEMENT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/run-statement/v1/run-statement.schema.json";
72
73
 
73
74
  const USAGE = [
74
- "chant workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--follow-squash] [--json]",
75
+ "chant workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--where <field><op><value>]... [--follow-squash] [--json]",
75
76
  "chant workspace runs start --from <file|->",
76
77
  "chant workspace runs end <run id> [--from <file|->]",
77
78
  "chant workspace runs record --from <file|->",
@@ -177,8 +178,36 @@ export interface RunsQuery {
177
178
  base?: string;
178
179
  /** Follow squash merges on HEAD to their pull requests' original commits, fetching missing pull request refs (#3035). */
179
180
  followSquash?: boolean;
181
+ /** Typed filters (#4363), each `<field><op><value>` over {@link RUN_FILTER_FIELDS}; every one has to hold. Applied before the totals. */
182
+ where?: readonly string[];
180
183
  }
181
184
 
185
+ /** The fields `runs --where` filters on (#4363). */
186
+ export const RUN_FILTER_FIELDS: FilterFields = {
187
+ id: "text",
188
+ state: "text",
189
+ startedAt: "time",
190
+ endedAt: "time",
191
+ outcome: "text",
192
+ by: "text",
193
+ agent: "text",
194
+ model: "text",
195
+ provider: "text",
196
+ lease: "text",
197
+ "harness.name": "text",
198
+ "harness.version": "text",
199
+ "unit.id": "text",
200
+ "unit.kind": "text",
201
+ "cost.amount": "number",
202
+ "cost.currency": "text",
203
+ "usage.inputTokens": "number",
204
+ "usage.outputTokens": "number",
205
+ ledger: "text",
206
+ };
207
+
208
+ /** Thrown for a `--where` that can't be applied: a usage error, before the ledger is read. */
209
+ export class RunsFilterError extends Error {}
210
+
182
211
  export interface RunsReason {
183
212
  code: RunsReasonCode;
184
213
  message: string;
@@ -218,7 +247,7 @@ export type RunsDocument =
218
247
  workspace: { name: string; root: string };
219
248
  ledger: { branch: string; dir: string; commit: string | null };
220
249
  /** The options given. `followSquash` was added within contract 1 (#3035). */
221
- filter: { unit: string | null; decision: string | null; by: string | null; since: string | null; followSquash?: boolean };
250
+ filter: { unit: string | null; decision: string | null; by: string | null; since: string | null; followSquash?: boolean; where?: string[] };
222
251
  /** The trust policy the run statements are judged by (#3192). */
223
252
  trust: RunsTrust;
224
253
  /** Newest first, by when each started. Each carries its `attestation`. */
@@ -281,6 +310,13 @@ function tryGit(top: string, args: string[]): string | undefined {
281
310
  /** Read the run ledger and build the document. Never throws a {@link WorkspaceReadError}. */
282
311
  export async function workspaceRuns(query: RunsQuery): Promise<RunsDocument> {
283
312
  const head = { $schema: RUNS_OUTPUT_SCHEMA_ID, contract: RUNS_CONTRACT_VERSION, chant: readerVersion() };
313
+ let clauses;
314
+ try {
315
+ clauses = parseWhere(query.where ?? [], RUN_FILTER_FIELDS);
316
+ } catch (err) {
317
+ if (err instanceof FilterError) throw new RunsFilterError(err.message);
318
+ throw err;
319
+ }
284
320
  try {
285
321
  const located = locateWorkspace(query.cwd);
286
322
  const declaration = readDeclaration(located.tree, "", { rootChant: true });
@@ -318,6 +354,7 @@ export async function workspaceRuns(query: RunsQuery): Promise<RunsDocument> {
318
354
  .filter((r) => wantDecision === undefined || r.decisions.some((d) => d === wantDecision || d.slice(d.indexOf("/") + 1) === wantDecision))
319
355
  .filter((r) => query.by === undefined || r.by === query.by)
320
356
  .filter((r) => !since || r.commits.some((c) => since!.commits.has(c.sha)) || Date.parse(r.startedAt) > since.date)
357
+ .filter((r) => matchesWhere(r, clauses))
321
358
  .sort((a, b) => (a.startedAt < b.startedAt ? 1 : a.startedAt > b.startedAt ? -1 : a.id < b.id ? 1 : -1));
322
359
 
323
360
  const byUnit = [...groupBy(selected, (r) => [r.unit?.id ?? null])].map(([unit, rs]) => ({ unit, ...totalRuns(rs) }));
@@ -327,7 +364,7 @@ export async function workspaceRuns(query: RunsQuery): Promise<RunsDocument> {
327
364
  ...head,
328
365
  workspace: { name: declaration.name, root: located.root },
329
366
  ledger: { branch: LEDGER_BRANCH, dir, commit: tip },
330
- filter: { unit: query.unit ?? null, decision: query.decision ?? null, by: query.by ?? null, since: query.since ?? null, followSquash: query.followSquash === true },
367
+ filter: { unit: query.unit ?? null, decision: query.decision ?? null, by: query.by ?? null, since: query.since ?? null, followSquash: query.followSquash === true, ...(query.where && query.where.length > 0 ? { where: [...query.where] } : {}) },
331
368
  trust,
332
369
  runs: selected,
333
370
  totals: { all: totalRuns(selected), byUnit, byDecision, byPrincipal },
@@ -652,7 +689,14 @@ export async function runWorkspaceRuns(ctx: CommandContext): Promise<number> {
652
689
  console.error(formatError({ message: "runs is a read and takes no --from; write with runs start, end or record", hint: USAGE }));
653
690
  return 1;
654
691
  }
655
- const doc = await workspaceRuns({ cwd, unit: args.unit, decision: args.decision, by: args.by, since: args.since, base: args.base, followSquash: args.followSquash });
692
+ let doc: RunsDocument;
693
+ try {
694
+ doc = await workspaceRuns({ cwd, unit: args.unit, decision: args.decision, by: args.by, since: args.since, base: args.base, followSquash: args.followSquash, where: args.where });
695
+ } catch (err) {
696
+ if (!(err instanceof RunsFilterError)) throw err;
697
+ console.error(formatError({ message: err.message, hint: USAGE }));
698
+ return 1;
699
+ }
656
700
  if (args.json) console.log(JSON.stringify(doc, null, 2));
657
701
  else if ("error" in doc) console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
658
702
  else console.log(formatRuns(doc));
@@ -111,6 +111,13 @@
111
111
  "followSquash": {
112
112
  "type": "boolean",
113
113
  "description": "Whether --follow-squash was given (#3035). Added within version 1."
114
+ },
115
+ "where": {
116
+ "type": "array",
117
+ "items": {
118
+ "type": "string"
119
+ },
120
+ "description": "Each --where clause, <field><op><value>, as given (#4363); every one held for each run listed, and the totals count only those runs. Absent without --where. Added within version 1."
114
121
  }
115
122
  }
116
123
  },