@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
@@ -1,13 +1,14 @@
1
1
  import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from "fs";
2
2
  import { join, resolve, basename, dirname } from "path";
3
3
  import { formatSuccess, formatWarning, formatError } from "../format";
4
- import type { TemplateIR, ResourceIR, ParameterIR, ParseContext } from "../../import/parser";
4
+ import type { TemplateIR, ResourceIR, ParameterIR, ParseContext, ImportDiagnostic } from "../../import/parser";
5
5
  import type { GeneratedFile, TypeScriptGenerator } from "../../import/generator";
6
6
  import { listInstalledLexicons, loadPlugin, loadPlugins, resolveProjectLexicons } from "../plugins";
7
7
  import type { ExportedTemplate, LexiconPlugin, ResourceSelector } from "../../lexicon";
8
8
  import { parseYAMLDocument, splitYAMLDocuments } from "../../yaml";
9
9
  import { importLexiconPackage } from "../../lexicon-module";
10
10
  import { resolveParserOptions } from "../../import/parser-options";
11
+ import { summarizeImportDiagnostics } from "./import-report";
11
12
  import { EmbeddedImports, type EmbeddedContent, type RegisteredEmbeddedImporter } from "../../import/embedded";
12
13
 
13
14
  /**
@@ -31,6 +32,8 @@ export interface ImportOptions {
31
32
  * fails the import before anything is parsed.
32
33
  */
33
34
  parserOptions?: string[];
35
+ /** `--validate` (#4398): check the parsed template on the lexicon's emulator before writing. */
36
+ validate?: boolean;
34
37
  }
35
38
 
36
39
  /**
@@ -52,6 +55,12 @@ export interface ImportResult {
52
55
  * it was named (`--lexicon`, `--kustomize`) (#2965).
53
56
  */
54
57
  detected?: boolean;
58
+ /**
59
+ * What the parser flagged for review (#4398), then what `--validate`
60
+ * rejected. `--report` writes them as SARIF and `--strict` fails while a
61
+ * `warning` or `error` is open; see `./import-report.ts`.
62
+ */
63
+ diagnostics?: ImportDiagnostic[];
55
64
  }
56
65
 
57
66
  /**
@@ -353,6 +362,7 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
353
362
  output: options.output,
354
363
  force: options.force,
355
364
  parserOptions: options.parserOptions,
365
+ validate: options.validate,
356
366
  });
357
367
  }
358
368
 
@@ -391,7 +401,7 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
391
401
  );
392
402
  }
393
403
 
394
- const result = await parseAndWrite(plugin, content, outputDir, options.force, warnings, generatedFiles, plugin.name, projectDir, options.parserOptions);
404
+ const result = await parseAndWrite(plugin, content, outputDir, options.force, warnings, generatedFiles, plugin.name, projectDir, options.parserOptions, options.validate);
395
405
  return { ...result, detected: true };
396
406
  }
397
407
 
@@ -412,6 +422,8 @@ export interface ContentImportOptions {
412
422
  force?: boolean;
413
423
  /** `--parser-option` entries for the lexicon's parser (#2994); see {@link ImportOptions.parserOptions}. */
414
424
  parserOptions?: string[];
425
+ /** `--validate`; see {@link ImportOptions.validate}. */
426
+ validate?: boolean;
415
427
  }
416
428
 
417
429
  export async function importFromContent(options: ContentImportOptions): Promise<ImportResult> {
@@ -432,7 +444,7 @@ export async function importFromContent(options: ContentImportOptions): Promise<
432
444
  return { success: false, generatedFiles: [], warnings: [], error: `Lexicon "${options.lexicon}" not available.` };
433
445
  }
434
446
  const projectDir = resolve(options.output ? dirname(options.output) : ".");
435
- return parseAndWrite(plugin, options.content, outputDir, options.force, [], [], plugin.name, projectDir, options.parserOptions);
447
+ return parseAndWrite(plugin, options.content, outputDir, options.force, [], [], plugin.name, projectDir, options.parserOptions, options.validate);
436
448
  }
437
449
 
438
450
  /**
@@ -568,6 +580,7 @@ async function parseAndWrite(
568
580
  lexicon: string,
569
581
  projectDir: string,
570
582
  parserOptionEntries?: readonly string[],
583
+ validate?: boolean,
571
584
  ): Promise<ImportResult> {
572
585
  // A lexicon can recognize a template (detectTemplate) without being able to
573
586
  // import it (grafana had no parser until #2945). Every path funnels through here, so
@@ -609,6 +622,18 @@ async function parseAndWrite(
609
622
  if (ir.warnings) {
610
623
  warnings.push(...ir.warnings);
611
624
  }
625
+ // Findings for review (#4398), and what the lexicon's emulator rejects.
626
+ const diagnostics: ImportDiagnostic[] = [...(ir.diagnostics ?? [])];
627
+ if (validate) {
628
+ if (!plugin.validateImport) {
629
+ return { success: false, generatedFiles: [], warnings: [], error: `lexicon "${plugin.name}" has no emulator to validate an import against`, lexicon: plugin.name };
630
+ }
631
+ try {
632
+ diagnostics.push(...(await plugin.validateImport(ir, resolved.options)));
633
+ } catch (err) {
634
+ return { success: false, generatedFiles: [], warnings: [], error: `--validate failed to run: ${err instanceof Error ? err.message : String(err)}`, lexicon: plugin.name };
635
+ }
636
+ }
612
637
  // Embedded content the owner could not carry, or that no installed lexicon imports.
613
638
  if (embedded) warnings.push(...embedded.warnings);
614
639
 
@@ -660,6 +685,7 @@ async function parseAndWrite(
660
685
  generatedFiles,
661
686
  warnings,
662
687
  lexicon,
688
+ ...(diagnostics.length > 0 ? { diagnostics } : {}),
663
689
  };
664
690
  }
665
691
 
@@ -894,6 +920,10 @@ export function printImportResult(result: ImportResult): void {
894
920
  console.log(`${result.detected ? "Detected lexicon" : "Lexicon"}: ${result.lexicon}`);
895
921
  }
896
922
 
923
+ if (result.diagnostics && result.diagnostics.length > 0) {
924
+ console.error(summarizeImportDiagnostics(result.diagnostics));
925
+ }
926
+
897
927
  if (result.generatedFiles.length > 0) {
898
928
  console.log(formatSuccess("Generated files:"));
899
929
  for (const file of result.generatedFiles) {
@@ -0,0 +1,145 @@
1
+ /**
2
+ * `chant migrate` naming and the target-server `--validate` (#4399): a side is
3
+ * `lexicon` or `lexicon/dialect`, a directory is a source only when the
4
+ * lexicon says so, and a result's own `validate` replaces the external linter.
5
+ */
6
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
7
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { lexiconOfSide, migrateCommand } from "./migrate";
11
+ import type { LexiconPlugin, MigrationResult, MigrationSource } from "../../lexicon";
12
+
13
+ const seen: Array<{ from: string; to: string | undefined }> = [];
14
+
15
+ function sqlPlugin(over: { directory?: boolean; result?: Partial<MigrationResult>; transform?: MigrationSource["transform"] } = {}): LexiconPlugin {
16
+ return {
17
+ name: "sql",
18
+ serializer: { name: "sql", rulePrefix: "SQL", serialize: () => "" },
19
+ async generate() {},
20
+ async validate() {},
21
+ async coverage() {},
22
+ async package() {},
23
+ migrationSource(from: string, to?: string) {
24
+ seen.push({ from, to });
25
+ if (from !== "sql/postgres" || (to !== undefined && to !== "sql/clickhouse")) return undefined;
26
+ return {
27
+ detect: (content: string) => content.includes("CREATE TABLE"),
28
+ ...(over.directory ? { directory: true } : {}),
29
+ transform: over.transform ?? (async () => ({ output: "-- ddl\n", provenance: [], diagnostics: [], ...over.result })),
30
+ };
31
+ },
32
+ };
33
+ }
34
+
35
+ const base = { emit: "sql" as const, strict: false, validate: false, useComposites: false };
36
+
37
+ describe("lexiconOfSide", () => {
38
+ test("is the part before the slash", () => {
39
+ expect(lexiconOfSide("sql/clickhouse")).toBe("sql");
40
+ expect(lexiconOfSide("gitlab")).toBe("gitlab");
41
+ });
42
+ });
43
+
44
+ describe("migrateCommand with lexicon/dialect sides", () => {
45
+ let dir: string;
46
+ beforeEach(() => {
47
+ dir = mkdtempSync(join(tmpdir(), "chant-migrate-dialect-"));
48
+ seen.length = 0;
49
+ });
50
+ afterEach(() => rmSync(dir, { recursive: true, force: true }));
51
+ const file = () => {
52
+ const f = join(dir, "schema.sql");
53
+ writeFileSync(f, "CREATE TABLE t (id int);");
54
+ return f;
55
+ };
56
+
57
+ test("finds the plugin by the lexicon part, and hands the source both sides as written", async () => {
58
+ const r = await migrateCommand({ ...base, sourceFile: file(), from: "sql/postgres", to: "sql/clickhouse", output: join(dir, "out.sql"), plugins: [sqlPlugin()] });
59
+ expect(r.exitCode).toBe(0);
60
+ expect(seen).toEqual([{ from: "sql/postgres", to: "sql/clickhouse" }]);
61
+ expect(readFileSync(join(dir, "out.sql"), "utf8")).toBe("-- ddl\n");
62
+ });
63
+
64
+ test("a dialect the lexicon does not know is refused by name", async () => {
65
+ const r = await migrateCommand({ ...base, sourceFile: file(), from: "sql/postgres", to: "sql/mysql", plugins: [sqlPlugin()] });
66
+ expect(r.exitCode).toBe(1);
67
+ expect(r.error).toContain('Lexicon "sql/mysql" does not support migration from "sql/postgres"');
68
+ });
69
+
70
+ test("creates the output's directory", async () => {
71
+ const out = join(dir, "a/b/out.sql");
72
+ const r = await migrateCommand({ ...base, sourceFile: file(), from: "sql/postgres", to: "sql/clickhouse", output: out, plugins: [sqlPlugin()] });
73
+ expect(r.exitCode).toBe(0);
74
+ expect(readFileSync(out, "utf8")).toBe("-- ddl\n");
75
+ });
76
+
77
+ test("a directory is refused unless the source reads directories; then detect is skipped and content is empty", async () => {
78
+ const member = join(dir, "db");
79
+ mkdirSync(member);
80
+ const refused = await migrateCommand({ ...base, sourceFile: member, from: "sql/postgres", to: "sql/clickhouse", plugins: [sqlPlugin()] });
81
+ expect(refused.exitCode).toBe(1);
82
+ expect(refused.error).toContain("is a directory");
83
+ let got: { content: string; sourceFile?: string } | undefined;
84
+ const ok = await migrateCommand({
85
+ ...base,
86
+ sourceFile: member,
87
+ from: "sql/postgres",
88
+ to: "sql/clickhouse",
89
+ output: join(dir, "o.sql"),
90
+ plugins: [sqlPlugin({ directory: true, transform: async (content, opts) => ((got = { content, sourceFile: opts.sourceFile }), { output: "x", provenance: [], diagnostics: [] }) })],
91
+ });
92
+ expect(ok.exitCode).toBe(0);
93
+ expect(got).toEqual({ content: "", sourceFile: member });
94
+ });
95
+ });
96
+
97
+ describe("--validate with a result that checks itself", () => {
98
+ let dir: string;
99
+ beforeEach(() => {
100
+ dir = mkdtempSync(join(tmpdir(), "chant-migrate-validate-"));
101
+ });
102
+ afterEach(() => rmSync(dir, { recursive: true, force: true }));
103
+ const run = (result: Partial<MigrationResult>, extra: { strict?: boolean } = {}) => {
104
+ const f = join(dir, "schema.sql");
105
+ writeFileSync(f, "CREATE TABLE t (id int);");
106
+ return migrateCommand({ ...base, ...extra, validate: true, sourceFile: f, from: "sql/postgres", to: "sql/clickhouse", output: join(dir, "o.sql"), plugins: [sqlPlugin({ result })] });
107
+ };
108
+
109
+ test("a clean check passes and its diagnostics join the result", async () => {
110
+ const r = await run({ validate: async () => [] });
111
+ expect(r.exitCode).toBe(0);
112
+ });
113
+
114
+ test("a refused statement fails the run without --strict, and is in the diagnostics", async () => {
115
+ const r = await run({ validate: async () => [{ ruleId: "SQLRV012", severity: "error", message: "t: the emulator rejected the statement." }] });
116
+ expect(r.exitCode).toBe(1);
117
+ expect(r.diagnostics.map((d) => d.ruleId)).toEqual(["SQLRV012"]);
118
+ });
119
+
120
+ test("a server that cannot start is a validation that did not run, which fails", async () => {
121
+ const r = await run({
122
+ validate: async () => {
123
+ throw new Error("Docker is not available, so the ClickHouse emulator cannot start");
124
+ },
125
+ });
126
+ expect(r.exitCode).toBe(1);
127
+ expect(r.error).toContain("--validate did not run: Docker is not available");
128
+ });
129
+
130
+ test("without --validate the check is not run", async () => {
131
+ let ran = false;
132
+ const f = join(dir, "schema.sql");
133
+ writeFileSync(f, "CREATE TABLE t (id int);");
134
+ const r = await migrateCommand({
135
+ ...base,
136
+ sourceFile: f,
137
+ from: "sql/postgres",
138
+ to: "sql/clickhouse",
139
+ output: join(dir, "o.sql"),
140
+ plugins: [sqlPlugin({ result: { validate: async () => ((ran = true), []) } })],
141
+ });
142
+ expect(r.exitCode).toBe(0);
143
+ expect(ran).toBe(false);
144
+ });
145
+ });
@@ -6,19 +6,30 @@
6
6
  * stdout/stderr surfaces, and exit codes.
7
7
  */
8
8
 
9
- import { readFileSync, writeFileSync } from "node:fs";
9
+ import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
10
+ import { dirname } from "node:path";
10
11
  import { spawnSync } from "node:child_process";
11
12
  import { formatError, formatInfo } from "../format";
12
13
  import { formatSarif } from "../reporters/stylish";
13
14
  import type { LexiconPlugin } from "../../lexicon";
14
15
  import type { LintRule, LintDiagnostic } from "../../lint/rule";
15
16
  import { ensureTypeScriptLoader } from "../../ts-loader";
17
+ import { planMigrateLinks } from "../../workspace/migrate-links";
18
+
19
+ /**
20
+ * A side of `--from` or `--to`: a lexicon, or a lexicon and one of its
21
+ * dialects, `sql/clickhouse` (#4399). The plugin is the part before the slash.
22
+ */
23
+ export function lexiconOfSide(side: string): string {
24
+ const slash = side.indexOf("/");
25
+ return slash === -1 ? side : side.slice(0, slash);
26
+ }
16
27
 
17
28
  export interface MigrateCliOpts {
18
29
  sourceFile: string;
19
30
  from: string;
20
31
  to: string;
21
- emit: "yaml" | "ts";
32
+ emit: "yaml" | "ts" | "sql";
22
33
  strict: boolean;
23
34
  validate: boolean;
24
35
  useComposites: boolean;
@@ -39,10 +50,13 @@ export interface MigrateCliResult {
39
50
  error?: string;
40
51
  /** Markdown summary lines printed to stderr */
41
52
  markdownSummary?: string;
53
+ /** What was done about workspace links, or why nothing was (#4399) */
54
+ linkNote?: string;
42
55
  }
43
56
 
44
57
  export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliResult> {
45
- const targetPlugin = opts.plugins.find((p) => p.name === opts.to);
58
+ const targetLexicon = lexiconOfSide(opts.to);
59
+ const targetPlugin = opts.plugins.find((p) => p.name === targetLexicon);
46
60
  if (!targetPlugin) {
47
61
  return {
48
62
  exitCode: 1,
@@ -59,7 +73,7 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
59
73
  error: `Lexicon "${opts.to}" does not support migration`,
60
74
  };
61
75
  }
62
- const source = targetPlugin.migrationSource(opts.from);
76
+ const source = targetPlugin.migrationSource(opts.from, opts.to);
63
77
  if (!source) {
64
78
  return {
65
79
  exitCode: 1,
@@ -70,8 +84,23 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
70
84
  }
71
85
 
72
86
  let content: string;
87
+ // A directory is a workspace member: the source reads it itself (#4399).
88
+ let isDirectory = false;
89
+ try {
90
+ isDirectory = statSync(opts.sourceFile).isDirectory();
91
+ } catch {
92
+ // The read below names the problem.
93
+ }
94
+ if (isDirectory && !source.directory) {
95
+ return {
96
+ exitCode: 1,
97
+ diagnostics: [],
98
+ provenance: [],
99
+ error: `${opts.sourceFile} is a directory, and migrating from "${opts.from}" reads a file`,
100
+ };
101
+ }
73
102
  try {
74
- content = readFileSync(opts.sourceFile, "utf-8");
103
+ content = isDirectory ? "" : readFileSync(opts.sourceFile, "utf-8");
75
104
  } catch (err) {
76
105
  return {
77
106
  exitCode: 1,
@@ -81,7 +110,7 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
81
110
  };
82
111
  }
83
112
 
84
- if (!source.detect(content)) {
113
+ if (!isDirectory && !source.detect(content)) {
85
114
  return {
86
115
  exitCode: 1,
87
116
  diagnostics: [],
@@ -113,6 +142,7 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
113
142
  // Write output (--output file or stdout)
114
143
  if (opts.output && opts.output !== "-") {
115
144
  try {
145
+ mkdirSync(dirname(opts.output), { recursive: true });
116
146
  writeFileSync(opts.output, result.output);
117
147
  } catch (err) {
118
148
  return {
@@ -126,9 +156,34 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
126
156
  process.stdout.write(result.output);
127
157
  }
128
158
 
159
+ // The target's own server (--validate), when the source has one (#4399).
160
+ // A refusal always fails the run; so does a server that could not start,
161
+ // since a validation that did not run is not a pass.
162
+ let validationFailed = false;
163
+ if (opts.validate && result.validate) {
164
+ try {
165
+ const found = await result.validate();
166
+ result.diagnostics.push(...found);
167
+ if (found.some((d) => d.severity === "error")) {
168
+ validationFailed = true;
169
+ console.error(`Validator found ${found.filter((d) => d.severity === "error").length} refused statement(s):\n${found.map((d) => ` ${String(d.message)}`).join("\n")}`);
170
+ } else {
171
+ console.error("Validator (target server) OK");
172
+ }
173
+ } catch (err) {
174
+ return {
175
+ exitCode: 1,
176
+ output: result.output,
177
+ diagnostics: result.diagnostics,
178
+ provenance: result.provenance,
179
+ error: `--validate did not run: ${err instanceof Error ? err.message : String(err)}`,
180
+ };
181
+ }
182
+ }
183
+
129
184
  // External validator (--validate) — glci preferred, glab fallback.
130
185
  let validatorWarning: string | undefined;
131
- if (opts.validate && opts.emit === "yaml") {
186
+ if (opts.validate && !result.validate && opts.emit === "yaml") {
132
187
  const v = tryValidateExternal(result.output);
133
188
  if (!v.ran) {
134
189
  validatorWarning = "neither glci nor glab is on PATH; skipping --validate";
@@ -157,11 +212,35 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
157
212
  }
158
213
  }
159
214
 
215
+ // Links from the output's workspace member to the outputs it reads (#4399).
216
+ let linkNote: string | undefined;
217
+ if (result.links && result.links.length > 0) {
218
+ const outcome = planMigrateLinks({
219
+ sourcePath: opts.sourceFile,
220
+ outputPath: opts.output && opts.output !== "-" ? opts.output : undefined,
221
+ outputs: result.links,
222
+ });
223
+ if (outcome && "skipped" in outcome) linkNote = outcome.skipped;
224
+ else if (outcome) {
225
+ const { plan } = outcome;
226
+ if (plan.added.length === 0) {
227
+ linkNote = `member ${plan.consumer} already links every output it reads from ${plan.producer}`;
228
+ } else {
229
+ try {
230
+ writeFileSync(plan.file, plan.text);
231
+ linkNote = `added ${plan.added.length} link${plan.added.length === 1 ? "" : "s"} from member ${plan.consumer} to ${plan.producer} in ${plan.file}`;
232
+ } catch (err) {
233
+ linkNote = `could not write the links to ${plan.file}: ${err instanceof Error ? err.message : String(err)}`;
234
+ }
235
+ }
236
+ }
237
+ }
238
+
160
239
  // SARIF report (--report <path>) — reuse the lint-side formatSarif so any
161
240
  // CI SARIF ingest path treats migration findings uniformly.
162
241
  if (opts.reportFile) {
163
242
  try {
164
- const rules = await loadMigrationRules(opts.to);
243
+ const rules = await loadMigrationRules(targetLexicon);
165
244
  const lintShape = result.diagnostics as unknown as LintDiagnostic[];
166
245
  const sarif = formatSarif(lintShape, rules);
167
246
  writeFileSync(opts.reportFile, sarif);
@@ -172,7 +251,8 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
172
251
  }
173
252
 
174
253
  // Markdown summary (always to stderr — leaves stdout clean for piping)
175
- let markdownSummary = formatMarkdownSummary(result.provenance, result.diagnostics, content);
254
+ let markdownSummary =
255
+ targetLexicon === "gitlab" ? formatMarkdownSummary(result.provenance, result.diagnostics, content) : formatDiagnosticSummary(result.provenance, result.diagnostics);
176
256
  // Append the security posture section when security analysis ran (#306).
177
257
  if (result.securityPosture) {
178
258
  markdownSummary += `\n\n${result.securityPosture}`;
@@ -182,7 +262,7 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
182
262
  // The transformer already escalates needs-review → error when opts.strict
183
263
  // is passed via MigrationSource.transform(); we double-check here.
184
264
  const errorDiagnostics = result.diagnostics.filter((d) => d.severity === "error");
185
- const exitCode = opts.strict && errorDiagnostics.length > 0 ? 1 : 0;
265
+ const exitCode = validationFailed || (opts.strict && errorDiagnostics.length > 0) ? 1 : 0;
186
266
 
187
267
  return {
188
268
  exitCode,
@@ -190,6 +270,7 @@ export async function migrateCommand(opts: MigrateCliOpts): Promise<MigrateCliRe
190
270
  diagnostics: result.diagnostics,
191
271
  provenance: result.provenance,
192
272
  markdownSummary,
273
+ ...(linkNote ? { linkNote } : {}),
193
274
  };
194
275
  }
195
276
 
@@ -231,7 +312,7 @@ export function tryValidateExternal(yamlText: string): ValidatorResult {
231
312
  * Returns an empty array if the lexicon doesn't expose them.
232
313
  */
233
314
  async function loadMigrationRules(targetLexicon: string): Promise<LintRule[]> {
234
- // For now only gitlab exposes migration rules. Hard-coded import keeps
315
+ // gitlab and sql expose migration rules. Hard-coded imports keep
235
316
  // the dependency direction explicit; widen the switch when more
236
317
  // lexicons ship their own migrate paths.
237
318
  if (targetLexicon === "gitlab") {
@@ -243,9 +324,46 @@ async function loadMigrationRules(targetLexicon: string): Promise<LintRule[]> {
243
324
  return [];
244
325
  }
245
326
  }
327
+ // The sql lexicon's needs-review catalog (#4399), one rule per finding id.
328
+ if (targetLexicon === "sql") {
329
+ try {
330
+ ensureTypeScriptLoader();
331
+ const mod = await import("@intentius/chant-lexicon-sql/clickhouse/review-catalog");
332
+ const rules = (mod as { REVIEW_RULES: Record<string, { id: string; name: string; summary: string; docs: readonly string[] }> }).REVIEW_RULES;
333
+ return Object.values(rules).map((r): LintRule => ({
334
+ id: r.id,
335
+ severity: "warning",
336
+ category: "correctness",
337
+ description: `${r.name}: ${r.summary}`,
338
+ helpUri: r.docs[0] ?? "https://intentius.io/chant/lexicons/sql/migrate-postgres-to-clickhouse/",
339
+ check: () => [],
340
+ }));
341
+ } catch {
342
+ return [];
343
+ }
344
+ }
246
345
  return [];
247
346
  }
248
347
 
348
+ /**
349
+ * The report for a pair that is not GitHub to GitLab: the counts and the
350
+ * diagnostics, with none of that pair's workflow advice (#4399).
351
+ */
352
+ export function formatDiagnosticSummary(provenance: Array<Record<string, unknown>>, diagnostics: Array<Record<string, unknown>>): string {
353
+ const totals = { error: 0, warning: 0, info: 0 };
354
+ for (const d of diagnostics) {
355
+ const sev = d.severity as string;
356
+ if (sev === "error" || sev === "warning" || sev === "info") totals[sev]++;
357
+ }
358
+ const lines = ["", "## Migration report", "", `- Translated: ${provenance.length}`, `- Diagnostics: ${totals.error} error, ${totals.warning} warning, ${totals.info} info`];
359
+ if (diagnostics.length > 0) {
360
+ lines.push("", "### Diagnostics", "", "| Severity | Rule | Message |", "|---|---|---|");
361
+ for (const d of diagnostics.slice(0, 50)) lines.push(`| ${String(d.severity)} | ${String(d.ruleId)} | ${String(d.message).replace(/\|/g, "\\|").slice(0, 160)} |`);
362
+ if (diagnostics.length > 50) lines.push(`| ... | ... | ${diagnostics.length - 50} more diagnostics omitted |`);
363
+ }
364
+ return lines.join("\n");
365
+ }
366
+
249
367
  /**
250
368
  * Format the migration report as Markdown, mirroring the output format
251
369
  * prescribed by the upstream gitlab-org/ci-cd/github-actions-to-gitlab-ci
@@ -471,6 +589,9 @@ export function printMigrateResult(result: MigrateCliResult): void {
471
589
  if (result.markdownSummary) {
472
590
  console.error(result.markdownSummary);
473
591
  }
592
+ if (result.linkNote) {
593
+ console.error(`\nWorkspace links: ${result.linkNote}`);
594
+ }
474
595
  if (result.exitCode === 0) {
475
596
  console.error(formatInfo("\nMigration complete."));
476
597
  } else {
@@ -14,10 +14,10 @@ jobs:
14
14
  runs-on: ubuntu-latest
15
15
  container: node:22
16
16
  steps:
17
- - uses: actions/checkout@v7
17
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
18
  - run: chant run --components shared-alb --env staging --dump-outputs shared-alb.outputs.json
19
19
  - name: Upload shared-alb outputs
20
- uses: actions/upload-artifact@v4
20
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
21
21
  with:
22
22
  name: shared-alb-outputs
23
23
  path: shared-alb.outputs.json
@@ -27,9 +27,9 @@ jobs:
27
27
  - shared-alb
28
28
  container: node:22
29
29
  steps:
30
- - uses: actions/checkout@v7
30
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
31
  - name: Download shared-alb outputs
32
- uses: actions/download-artifact@v4
32
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
33
33
  with:
34
34
  name: shared-alb-outputs
35
35
  path: .
@@ -599,6 +599,21 @@ export async function recordGateApproval(
599
599
  // decision is written next to the approval, so a pass can be traced to the
600
600
  // rule that allowed it.
601
601
  const approval = standingForOrigin?.approval;
602
+ // #4290, #4324: roles come from .chant/trust.json at base, for an approver
603
+ // a seal proves. The quorum and the policy both read these, never --role.
604
+ const judge = trustQuorumJudge({ signers: scope.policy, roles: scope.policy.roles });
605
+ const trusted = judge({
606
+ version: 1,
607
+ op: opName,
608
+ gate,
609
+ ...(environment !== undefined ? { environment } : {}),
610
+ ...(planDigest !== undefined ? { planDigest } : {}),
611
+ resolvedBy,
612
+ ...(relayedBy ? { relayedBy } : {}),
613
+ timestamp,
614
+ ...(checkpointDigest !== undefined ? { checkpointDigest } : {}),
615
+ ...(seal ? { seal } : {}),
616
+ }).roles;
602
617
  let policyDecision: GatePolicyDecision | undefined;
603
618
  if (approval?.policy) {
604
619
  try {
@@ -610,6 +625,7 @@ export async function recordGateApproval(
610
625
  gate,
611
626
  resolvedBy,
612
627
  approver,
628
+ roles: trusted,
613
629
  ...(planDigest !== undefined ? { planDigest } : {}),
614
630
  ...(approval.context ? { context: approval.context } : {}),
615
631
  }),
@@ -680,9 +696,14 @@ export async function recordGateApproval(
680
696
  "after it plans again. A moved plan or another commit starts it at the first phase.",
681
697
  ));
682
698
  }
699
+ const untrusted = (approver.roles ?? []).filter((r) => !trusted.includes(r));
700
+ if (untrusted.length > 0) {
701
+ console.error(formatInfo(
702
+ `--role ${untrusted.join(", ")} is recorded on the approval and trusted by nothing: a gate's quorum and policy read ` +
703
+ `${record.resolvedBy}'s roles from .chant/trust.json at base, and only for an approval sealed by a signer listed at base (chant approve --sign).`,
704
+ ));
705
+ }
683
706
  if (approval) {
684
- // #4290: the rules count roles from .chant/trust.json at base, for a proven approver.
685
- const judge = trustQuorumJudge({ signers: scope.policy, roles: scope.policy.roles });
686
707
  for (const line of describeApprovalProgress(approval, record, ledger.resolutions, standingForOrigin, ledger.pending, judge)) {
687
708
  console.error(formatInfo(line));
688
709
  }
@@ -171,6 +171,12 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
171
171
  }
172
172
  }
173
173
 
174
+ // #4327: ci.locks: plan adds the lock and unlock jobs. Read from the declaration, as the security groups are; GitLab keeps its own jobs.
175
+ let locks: "plan" | undefined;
176
+ if (found && args.prLoop && lexicon !== "gitlab") {
177
+ locks = (await import("../../workspace/ci-locks")).ciLocksAt(resolve(args.path)) === "plan" ? "plan" : undefined;
178
+ }
179
+
174
180
  // Which lexicons support generate mode is a property of the loaded lexicon
175
181
  // plugins (those implementing `generateComponentPipeline`, #688), not a
176
182
  // hard-coded core list — `generateComponentsPipeline` returns a descriptive
@@ -192,7 +198,7 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {
192
198
  : {}),
193
199
  // #3183: a plan job per pull request and an apply job per merge.
194
200
  // #4292: on the branch ci.branches routes the member to, when it routes it.
195
- ...(args.prLoop ? { prLoop: { ...prLoopOption(args, reportUrl, reviewRules, isolatedRunners), ...(plan?.branch ? { branch: plan.branch } : {}), ...(identity ? { identity } : {}), ...(prEnvIdentity ? { prEnvIdentity } : {}) } } : {}),
201
+ ...(args.prLoop ? { prLoop: { ...prLoopOption(args, reportUrl, reviewRules, isolatedRunners), ...(plan?.branch ? { branch: plan.branch } : {}), ...(identity ? { identity } : {}), ...(prEnvIdentity ? { prEnvIdentity } : {}), ...(locks ? { locks } : {}) } } : {}),
196
202
  // #4002: the job image and the lines that install what it lacks, chant itself included.
197
203
  ...(args.image ? { image: args.image } : {}),
198
204
  ...(args.beforeScript?.length ? { beforeScript: args.beforeScript } : {}),
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `chant ledger <table> [dir] [--member <name>] [--where <field><op><value>]...
3
+ * [--since <time>] [--at <rev>] [--json]` (#4363): one ledger's rows from the
4
+ * local `chant/lifecycle` branch, filtered with typed filters. The read and
5
+ * its contract live in `../../lifecycle/ledger-read.ts`; the ledgers are read
6
+ * as `chant query` reads them (`./query.ts` {@link queryLedgers}).
7
+ */
8
+ import { existsSync } from "node:fs";
9
+ import { resolve } from "node:path";
10
+ import { formatError, formatWarning } from "../format";
11
+ import type { CommandContext } from "../registry";
12
+ import { queryLedgers } from "./query";
13
+
14
+ const USAGE = "chant ledger <resources|snapshots|runs|gates|deploys|releases|cuts> [dir] [--member <name>] [--where <field><op><value>]... [--since <time>] [--at <rev>] [--json]";
15
+
16
+ const cell = (v: unknown): string => (v === null || v === undefined ? "" : typeof v === "object" ? JSON.stringify(v) : String(v));
17
+
18
+ export async function runLedgerCommand(ctx: CommandContext): Promise<number> {
19
+ const { args } = ctx;
20
+ const table = args.positionals?.[0];
21
+ if (table === undefined || table.trim() === "") {
22
+ console.error(formatError({ message: "chant ledger needs a table", hint: USAGE }));
23
+ return 1;
24
+ }
25
+ const cwd = resolve(args.positionals?.[1] ?? ".");
26
+ if (!existsSync(cwd)) {
27
+ console.error(formatError({ message: `${cwd} does not exist`, hint: USAGE }));
28
+ return 1;
29
+ }
30
+ const { readLedgerTable, ledgerReadFailure, LedgerRevisionError } = await import("../../lifecycle/ledger-read");
31
+ const { QueryError } = await import("../../lifecycle/query");
32
+ let doc;
33
+ try {
34
+ doc = await readLedgerTable(table, () => queryLedgers(cwd, args.members, args.at), { where: args.where, since: args.since, at: args.at ?? null });
35
+ } catch (err) {
36
+ if (err instanceof LedgerRevisionError) doc = ledgerReadFailure("revision-unknown", err.message);
37
+ else if (err instanceof QueryError) doc = ledgerReadFailure("ledger-unreadable", err.message);
38
+ else throw err;
39
+ }
40
+ if (args.json) {
41
+ console.log(JSON.stringify(doc, null, 2));
42
+ return "error" in doc ? 1 : 0;
43
+ }
44
+ if ("error" in doc) {
45
+ console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
46
+ return 1;
47
+ }
48
+ const widths = doc.columns.map((c) => Math.max(c.length, ...doc.rows.map((r) => cell(r[c]).length)));
49
+ const line = (vals: string[]): string => vals.map((v, i) => v.padEnd(widths[i])).join(" ").trimEnd();
50
+ console.log([line(doc.columns), line(widths.map((w) => "-".repeat(w))), ...doc.rows.map((r) => line(doc.columns.map((c) => cell(r[c]))))].join("\n"));
51
+ console.log(`(${doc.rows.length} of ${doc.total} ${doc.total === 1 ? "row" : "rows"})`);
52
+ if (doc.malformed > 0) console.error(formatWarning({ message: `left out ${doc.malformed} in ${doc.table} that could not be read` }));
53
+ return 0;
54
+ }