@intentius/chant 0.124.46 → 0.124.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (726) hide show
  1. package/dist/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  2. package/dist/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  3. package/dist/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  4. package/dist/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  6. package/dist/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  7. package/dist/__golden__/wave-loop/op-cost-member.json +13 -0
  8. package/dist/__golden__/wave-loop/op-denied-source.json +16 -0
  9. package/dist/__golden__/wave-loop/op-denied.json +16 -0
  10. package/dist/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  11. package/dist/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  12. package/dist/__golden__/wave-loop/op-discover-rest.json +26 -0
  13. package/dist/__golden__/wave-loop/op-discover-wide.json +26 -0
  14. package/dist/__golden__/wave-loop/op-held-on-again.json +16 -0
  15. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  16. package/dist/__golden__/wave-loop/op-held-on.json +16 -0
  17. package/dist/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  18. package/dist/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  19. package/dist/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  20. package/dist/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  21. package/dist/__golden__/wave-loop/op-hooks-held.json +13 -0
  22. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  23. package/dist/__golden__/wave-loop/op-hooks-pass.json +13 -0
  24. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  25. package/dist/__golden__/wave-loop/op-hooks-share.json +33 -0
  26. package/dist/__golden__/wave-loop/op-outcome-held.json +10 -0
  27. package/dist/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  28. package/dist/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  29. package/dist/__golden__/wave-loop/op-outcome-replan.json +10 -0
  30. package/dist/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  31. package/dist/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  32. package/dist/__golden__/wave-loop/op-overridden.json +16 -0
  33. package/dist/__golden__/wave-loop/op-per-run.json +20 -0
  34. package/dist/__golden__/wave-loop/op-point.json +13 -0
  35. package/dist/__golden__/wave-loop/op-pr-review.json +10 -0
  36. package/dist/__golden__/wave-loop/op-sealed.json +10 -0
  37. package/dist/__golden__/wave-loop/op-spend-change-only.json +10 -0
  38. package/dist/__golden__/wave-loop/op-spend.json +10 -0
  39. package/dist/__golden__/wave-loop/op-wave-commands.json +55 -0
  40. package/dist/__golden__/wave-loop/op-wide-refused.json +26 -0
  41. package/dist/__golden__/wave-loop/op-wide-waiting.json +29 -0
  42. package/dist/__golden__/wave-loop/op-wide.json +39 -0
  43. package/dist/action-pins.d.ts +61 -0
  44. package/dist/action-pins.d.ts.map +1 -0
  45. package/dist/action-pins.js +77 -0
  46. package/dist/cli/commands/carve.d.ts +3 -0
  47. package/dist/cli/commands/carve.d.ts.map +1 -1
  48. package/dist/cli/commands/carve.js +36 -7
  49. package/dist/cli/commands/import-report.d.ts +26 -0
  50. package/dist/cli/commands/import-report.d.ts.map +1 -0
  51. package/dist/cli/commands/import-report.js +65 -0
  52. package/dist/cli/commands/import.d.ts +11 -1
  53. package/dist/cli/commands/import.d.ts.map +1 -1
  54. package/dist/cli/commands/import.js +22 -3
  55. package/dist/cli/commands/migrate.d.ts +13 -1
  56. package/dist/cli/commands/migrate.d.ts.map +1 -1
  57. package/dist/cli/commands/migrate.js +134 -10
  58. package/dist/cli/handlers/approve.d.ts.map +1 -1
  59. package/dist/cli/handlers/approve.js +41 -14
  60. package/dist/cli/handlers/build.d.ts.map +1 -1
  61. package/dist/cli/handlers/build.js +6 -1
  62. package/dist/cli/handlers/graph-reader.d.ts +26 -0
  63. package/dist/cli/handlers/graph-reader.d.ts.map +1 -0
  64. package/dist/cli/handlers/graph-reader.js +41 -0
  65. package/dist/cli/handlers/graph.d.ts.map +1 -1
  66. package/dist/cli/handlers/graph.js +14 -0
  67. package/dist/cli/handlers/ledger.d.ts +3 -0
  68. package/dist/cli/handlers/ledger.d.ts.map +1 -0
  69. package/dist/cli/handlers/ledger.js +55 -0
  70. package/dist/cli/handlers/migrate.d.ts.map +1 -1
  71. package/dist/cli/handlers/migrate.js +18 -11
  72. package/dist/cli/handlers/misc.d.ts.map +1 -1
  73. package/dist/cli/handlers/misc.js +12 -1
  74. package/dist/cli/handlers/pr-env.js +2 -2
  75. package/dist/cli/handlers/pr.d.ts +15 -1
  76. package/dist/cli/handlers/pr.d.ts.map +1 -1
  77. package/dist/cli/handlers/pr.js +183 -40
  78. package/dist/cli/handlers/query.d.ts +7 -2
  79. package/dist/cli/handlers/query.d.ts.map +1 -1
  80. package/dist/cli/handlers/query.js +16 -4
  81. package/dist/cli/handlers/release.d.ts.map +1 -1
  82. package/dist/cli/handlers/release.js +6 -3
  83. package/dist/cli/handlers/search.d.ts +2 -4
  84. package/dist/cli/handlers/search.d.ts.map +1 -1
  85. package/dist/cli/handlers/search.js +13 -39
  86. package/dist/cli/main.d.ts.map +1 -1
  87. package/dist/cli/main.js +87 -14
  88. package/dist/cli/parse-args.d.ts.map +1 -1
  89. package/dist/cli/parse-args.js +47 -2
  90. package/dist/cli/registry.d.ts +22 -4
  91. package/dist/cli/registry.d.ts.map +1 -1
  92. package/dist/components/builders.d.ts +5 -1
  93. package/dist/components/builders.d.ts.map +1 -1
  94. package/dist/components/builders.js +5 -0
  95. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  96. package/dist/components/capability-plugin-loader.js +16 -1
  97. package/dist/components/capability-plugin.d.ts +14 -0
  98. package/dist/components/capability-plugin.d.ts.map +1 -1
  99. package/dist/components/pr-hooks.d.ts +21 -1
  100. package/dist/components/pr-hooks.d.ts.map +1 -1
  101. package/dist/components/pr-hooks.js +72 -4
  102. package/dist/components/pr-pipeline.d.ts +45 -10
  103. package/dist/components/pr-pipeline.d.ts.map +1 -1
  104. package/dist/components/pr-pipeline.js +47 -14
  105. package/dist/components/pr-policy.d.ts +36 -0
  106. package/dist/components/pr-policy.d.ts.map +1 -1
  107. package/dist/components/pr-policy.js +36 -0
  108. package/dist/components/pr-run.d.ts +3 -0
  109. package/dist/components/pr-run.d.ts.map +1 -1
  110. package/dist/components/pr-run.js +1 -0
  111. package/dist/components/starter-plugin.d.ts +1 -0
  112. package/dist/components/starter-plugin.d.ts.map +1 -1
  113. package/dist/components/starter-plugin.js +5 -1
  114. package/dist/components/verbs/index.d.ts +2 -0
  115. package/dist/components/verbs/index.d.ts.map +1 -1
  116. package/dist/components/verbs/index.js +2 -0
  117. package/dist/components/verbs/publish-artifact.d.ts +65 -0
  118. package/dist/components/verbs/publish-artifact.d.ts.map +1 -0
  119. package/dist/components/verbs/publish-artifact.js +74 -0
  120. package/dist/components/verbs/publish-image.d.ts +129 -0
  121. package/dist/components/verbs/publish-image.d.ts.map +1 -0
  122. package/dist/components/verbs/publish-image.js +145 -0
  123. package/dist/config-environments.d.ts +20 -0
  124. package/dist/config-environments.d.ts.map +1 -1
  125. package/dist/config-environments.js +12 -0
  126. package/dist/config.d.ts +25 -1
  127. package/dist/config.d.ts.map +1 -1
  128. package/dist/config.js +10 -2
  129. package/dist/gate-decision.d.ts +20 -2
  130. package/dist/gate-decision.d.ts.map +1 -1
  131. package/dist/gate-decision.js +30 -3
  132. package/dist/graph-ir.d.ts +25 -0
  133. package/dist/graph-ir.d.ts.map +1 -1
  134. package/dist/graph-walk.d.ts +66 -0
  135. package/dist/graph-walk.d.ts.map +1 -0
  136. package/dist/graph-walk.js +70 -0
  137. package/dist/import/parser.d.ts +19 -0
  138. package/dist/import/parser.d.ts.map +1 -1
  139. package/dist/lexicon.d.ts +94 -6
  140. package/dist/lexicon.d.ts.map +1 -1
  141. package/dist/lifecycle/gate-ledger.d.ts +11 -0
  142. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  143. package/dist/lifecycle/git.d.ts +2 -5
  144. package/dist/lifecycle/git.d.ts.map +1 -1
  145. package/dist/lifecycle/ledger-read.d.ts +65 -0
  146. package/dist/lifecycle/ledger-read.d.ts.map +1 -0
  147. package/dist/lifecycle/ledger-read.js +160 -0
  148. package/dist/lifecycle/release-workstation.d.ts +7 -0
  149. package/dist/lifecycle/release-workstation.d.ts.map +1 -1
  150. package/dist/lifecycle/release-workstation.js +25 -0
  151. package/dist/lifecycle/run-ledger.d.ts +4 -3
  152. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  153. package/dist/lifecycle/run-ledger.js +1 -0
  154. package/dist/lint/discover.js +3 -1
  155. package/dist/lint/post-synth.d.ts +127 -1
  156. package/dist/lint/post-synth.d.ts.map +1 -1
  157. package/dist/lint/post-synth.js +84 -2
  158. package/dist/lint/rules/op/index.d.ts +1 -0
  159. package/dist/lint/rules/op/index.d.ts.map +1 -1
  160. package/dist/lint/rules/op/index.js +2 -0
  161. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts +32 -0
  162. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts.map +1 -0
  163. package/dist/lint/rules/op/ops019-workspace-op-target.js +63 -0
  164. package/dist/op/activities/activity-contracts.d.ts +9 -6
  165. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  166. package/dist/op/activities/activity-contracts.js +5 -2
  167. package/dist/op/activities/index.d.ts +2 -2
  168. package/dist/op/activities/index.d.ts.map +1 -1
  169. package/dist/op/activities/index.js +1 -1
  170. package/dist/op/activities/review-agent.d.ts +90 -4
  171. package/dist/op/activities/review-agent.d.ts.map +1 -1
  172. package/dist/op/activities/review-agent.js +217 -19
  173. package/dist/op/activities/work-evidence.d.ts +8 -1
  174. package/dist/op/activities/work-evidence.d.ts.map +1 -1
  175. package/dist/op/activities/work-evidence.js +1 -0
  176. package/dist/op/builders.d.ts +9 -5
  177. package/dist/op/builders.d.ts.map +1 -1
  178. package/dist/op/builders.js +9 -5
  179. package/dist/op/composites/apply-op.d.ts +29 -2
  180. package/dist/op/composites/apply-op.d.ts.map +1 -1
  181. package/dist/op/composites/apply-op.js +20 -1
  182. package/dist/op/gate-approval-base.d.ts +25 -0
  183. package/dist/op/gate-approval-base.d.ts.map +1 -0
  184. package/dist/op/gate-approval-base.js +71 -0
  185. package/dist/op/gate-approval.d.ts +18 -9
  186. package/dist/op/gate-approval.d.ts.map +1 -1
  187. package/dist/op/gate-approval.js +13 -31
  188. package/dist/op/gate-name.d.ts +13 -0
  189. package/dist/op/gate-name.d.ts.map +1 -1
  190. package/dist/op/gate-name.js +14 -1
  191. package/dist/op/gate-quorum.d.ts +100 -3
  192. package/dist/op/gate-quorum.d.ts.map +1 -1
  193. package/dist/op/gate-quorum.js +108 -6
  194. package/dist/op/gate-review.d.ts +9 -0
  195. package/dist/op/gate-review.d.ts.map +1 -1
  196. package/dist/op/gate-review.js +13 -0
  197. package/dist/op/gate.d.ts +47 -5
  198. package/dist/op/gate.d.ts.map +1 -1
  199. package/dist/op/gate.js +25 -4
  200. package/dist/op/index.d.ts +3 -3
  201. package/dist/op/index.d.ts.map +1 -1
  202. package/dist/op/index.js +1 -1
  203. package/dist/op/local-executor.d.ts +45 -3
  204. package/dist/op/local-executor.d.ts.map +1 -1
  205. package/dist/op/local-executor.js +68 -7
  206. package/dist/op/op-waves-run.d.ts +26 -1
  207. package/dist/op/op-waves-run.d.ts.map +1 -1
  208. package/dist/op/op-waves-run.js +259 -13
  209. package/dist/op/op-waves.d.ts +72 -5
  210. package/dist/op/op-waves.d.ts.map +1 -1
  211. package/dist/op/op-waves.js +110 -15
  212. package/dist/op/runtime.d.ts +11 -0
  213. package/dist/op/runtime.d.ts.map +1 -1
  214. package/dist/op/schedule-overdue.d.ts +16 -0
  215. package/dist/op/schedule-overdue.d.ts.map +1 -1
  216. package/dist/op/schedule-overdue.js +36 -0
  217. package/dist/op/stage-hooks.d.ts +9 -2
  218. package/dist/op/stage-hooks.d.ts.map +1 -1
  219. package/dist/op/stage-hooks.js +7 -5
  220. package/dist/op/workspace-run-env.d.ts +22 -0
  221. package/dist/op/workspace-run-env.d.ts.map +1 -0
  222. package/dist/op/workspace-run-env.js +21 -0
  223. package/dist/pr-forge.d.ts +24 -0
  224. package/dist/pr-forge.d.ts.map +1 -1
  225. package/dist/pr-forge.js +47 -0
  226. package/dist/pr-loop.d.ts +10 -0
  227. package/dist/pr-loop.d.ts.map +1 -1
  228. package/dist/pr-loop.js +4 -0
  229. package/dist/pr-migrations.d.ts +62 -0
  230. package/dist/pr-migrations.d.ts.map +1 -0
  231. package/dist/pr-migrations.js +50 -0
  232. package/dist/pr-post.d.ts +42 -0
  233. package/dist/pr-post.d.ts.map +1 -1
  234. package/dist/pr-post.js +68 -2
  235. package/dist/read-filter.d.ts +65 -0
  236. package/dist/read-filter.d.ts.map +1 -0
  237. package/dist/read-filter.js +179 -0
  238. package/dist/run-event.d.ts +1 -1
  239. package/dist/run-event.d.ts.map +1 -1
  240. package/dist/terraform/carve.d.ts +24 -3
  241. package/dist/terraform/carve.d.ts.map +1 -1
  242. package/dist/terraform/carve.js +32 -14
  243. package/dist/terraform/cuts.d.ts +97 -0
  244. package/dist/terraform/cuts.d.ts.map +1 -0
  245. package/dist/terraform/cuts.js +138 -0
  246. package/dist/terraform/folds.d.ts +26 -0
  247. package/dist/terraform/folds.d.ts.map +1 -0
  248. package/dist/terraform/folds.js +38 -0
  249. package/dist/terraform/graph.d.ts.map +1 -1
  250. package/dist/terraform/graph.js +10 -0
  251. package/dist/terraform/reach.d.ts +140 -0
  252. package/dist/terraform/reach.d.ts.map +1 -0
  253. package/dist/terraform/reach.js +170 -0
  254. package/dist/terraform/score.d.ts +14 -0
  255. package/dist/terraform/score.d.ts.map +1 -1
  256. package/dist/terraform/score.js +14 -31
  257. package/dist/terraform/state.d.ts.map +1 -1
  258. package/dist/terraform/state.js +4 -2
  259. package/dist/terraform/types.d.ts +8 -0
  260. package/dist/terraform/types.d.ts.map +1 -1
  261. package/dist/wave-blocked.d.ts +10 -1
  262. package/dist/wave-blocked.d.ts.map +1 -1
  263. package/dist/wave-blocked.js +31 -3
  264. package/dist/wave-runtime.mjs +264 -254
  265. package/dist/workspace/check.schema.json +13 -2
  266. package/dist/workspace/checks/ci-branch.d.ts +44 -0
  267. package/dist/workspace/checks/ci-branch.d.ts.map +1 -0
  268. package/dist/workspace/checks/ci-branch.js +133 -0
  269. package/dist/workspace/checks/gate-approval.d.ts +58 -0
  270. package/dist/workspace/checks/gate-approval.d.ts.map +1 -0
  271. package/dist/workspace/checks/gate-approval.js +103 -0
  272. package/dist/workspace/checks/links.d.ts +3 -0
  273. package/dist/workspace/checks/links.d.ts.map +1 -1
  274. package/dist/workspace/checks/links.js +12 -1
  275. package/dist/workspace/checks/org.d.ts +48 -0
  276. package/dist/workspace/checks/org.d.ts.map +1 -0
  277. package/dist/workspace/checks/org.js +156 -0
  278. package/dist/workspace/checks.d.ts +12 -1
  279. package/dist/workspace/checks.d.ts.map +1 -1
  280. package/dist/workspace/checks.js +63 -7
  281. package/dist/workspace/ci-branches.d.ts +16 -0
  282. package/dist/workspace/ci-branches.d.ts.map +1 -1
  283. package/dist/workspace/ci-branches.js +27 -0
  284. package/dist/workspace/ci-green-cli.js +7 -7
  285. package/dist/workspace/ci-green-workflow.d.ts +1 -1
  286. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  287. package/dist/workspace/ci-green-workflow.js +13 -8
  288. package/dist/workspace/ci-green.d.ts +14 -0
  289. package/dist/workspace/ci-green.d.ts.map +1 -1
  290. package/dist/workspace/ci-green.js +44 -11
  291. package/dist/workspace/ci-run.d.ts +9 -1
  292. package/dist/workspace/ci-run.d.ts.map +1 -1
  293. package/dist/workspace/ci-run.js +67 -6
  294. package/dist/workspace/ci-serve.d.ts +9 -0
  295. package/dist/workspace/ci-serve.d.ts.map +1 -1
  296. package/dist/workspace/ci-serve.js +114 -76
  297. package/dist/workspace/compose-graph.d.ts +14 -0
  298. package/dist/workspace/compose-graph.d.ts.map +1 -1
  299. package/dist/workspace/composites.d.ts +4 -1
  300. package/dist/workspace/composites.d.ts.map +1 -1
  301. package/dist/workspace/composites.js +5 -2
  302. package/dist/workspace/composites.schema.json +22 -0
  303. package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  304. package/dist/workspace/cross-root-lint.d.ts +56 -0
  305. package/dist/workspace/cross-root-lint.d.ts.map +1 -0
  306. package/dist/workspace/cross-root-lint.js +111 -0
  307. package/dist/workspace/dbt-models-reader.d.ts +44 -0
  308. package/dist/workspace/dbt-models-reader.d.ts.map +1 -0
  309. package/dist/workspace/dbt-models-reader.js +356 -0
  310. package/dist/workspace/dbt-project-common.d.ts +40 -0
  311. package/dist/workspace/dbt-project-common.d.ts.map +1 -0
  312. package/dist/workspace/dbt-project-common.js +106 -0
  313. package/dist/workspace/dbt-project-reader.d.ts +4 -3
  314. package/dist/workspace/dbt-project-reader.d.ts.map +1 -1
  315. package/dist/workspace/dbt-project-reader.js +14 -108
  316. package/dist/workspace/decision-rank.d.ts +26 -0
  317. package/dist/workspace/decision-rank.d.ts.map +1 -0
  318. package/dist/workspace/decision-rank.js +16 -0
  319. package/dist/workspace/declaration.d.ts +50 -2
  320. package/dist/workspace/declaration.d.ts.map +1 -1
  321. package/dist/workspace/declaration.js +57 -11
  322. package/dist/workspace/declaration.schema.json +166 -24
  323. package/dist/workspace/environments.d.ts +26 -2
  324. package/dist/workspace/environments.d.ts.map +1 -1
  325. package/dist/workspace/environments.js +26 -2
  326. package/dist/workspace/exit-cli.d.ts +44 -0
  327. package/dist/workspace/exit-cli.d.ts.map +1 -0
  328. package/dist/workspace/exit-cli.js +198 -0
  329. package/dist/workspace/exit.d.ts +159 -0
  330. package/dist/workspace/exit.d.ts.map +1 -0
  331. package/dist/workspace/exit.js +480 -0
  332. package/dist/workspace/exit.schema.json +257 -0
  333. package/dist/workspace/flyway-dir.d.ts +39 -0
  334. package/dist/workspace/flyway-dir.d.ts.map +1 -0
  335. package/dist/workspace/flyway-dir.js +104 -0
  336. package/dist/workspace/flyway-layout.d.ts +96 -0
  337. package/dist/workspace/flyway-layout.d.ts.map +1 -0
  338. package/dist/workspace/flyway-layout.js +308 -0
  339. package/dist/workspace/graph-cache.d.ts +3 -1
  340. package/dist/workspace/graph-cache.d.ts.map +1 -1
  341. package/dist/workspace/graph-cache.js +12 -1
  342. package/dist/workspace/graph-cli.d.ts.map +1 -1
  343. package/dist/workspace/graph-cli.js +26 -8
  344. package/dist/workspace/graph.schema.json +34 -0
  345. package/dist/workspace/import-fetch.d.ts +38 -0
  346. package/dist/workspace/import-fetch.d.ts.map +1 -0
  347. package/dist/workspace/import-fetch.js +99 -0
  348. package/dist/workspace/import-init.d.ts +26 -0
  349. package/dist/workspace/import-init.d.ts.map +1 -0
  350. package/dist/workspace/import-init.js +130 -0
  351. package/dist/workspace/import-source.d.ts +147 -0
  352. package/dist/workspace/import-source.d.ts.map +1 -0
  353. package/dist/workspace/import-source.js +370 -0
  354. package/dist/workspace/init.d.ts +4 -0
  355. package/dist/workspace/init.d.ts.map +1 -1
  356. package/dist/workspace/init.js +70 -3
  357. package/dist/workspace/intent.d.ts +2 -3
  358. package/dist/workspace/intent.d.ts.map +1 -1
  359. package/dist/workspace/intent.js +5 -9
  360. package/dist/workspace/kind-link-readers.d.ts +59 -2
  361. package/dist/workspace/kind-link-readers.d.ts.map +1 -1
  362. package/dist/workspace/kind-link-readers.js +17 -1
  363. package/dist/workspace/kind-readers.d.ts +6 -0
  364. package/dist/workspace/kind-readers.d.ts.map +1 -1
  365. package/dist/workspace/kind-readers.js +18 -0
  366. package/dist/workspace/kinds.d.ts +90 -3
  367. package/dist/workspace/kinds.d.ts.map +1 -1
  368. package/dist/workspace/kinds.js +161 -7
  369. package/dist/workspace/ledger.schema.json +158 -0
  370. package/dist/workspace/lexicon-outputs.d.ts +20 -1
  371. package/dist/workspace/lexicon-outputs.d.ts.map +1 -1
  372. package/dist/workspace/lexicon-outputs.js +102 -10
  373. package/dist/workspace/lineage-check.d.ts +3 -0
  374. package/dist/workspace/lineage-check.d.ts.map +1 -1
  375. package/dist/workspace/lineage-check.js +23 -4
  376. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  377. package/dist/workspace/lineage-upgrade.js +13 -4
  378. package/dist/workspace/links.d.ts +8 -1
  379. package/dist/workspace/links.d.ts.map +1 -1
  380. package/dist/workspace/links.js +25 -1
  381. package/dist/workspace/ls.d.ts +8 -0
  382. package/dist/workspace/ls.d.ts.map +1 -1
  383. package/dist/workspace/ls.js +7 -1
  384. package/dist/workspace/ls.schema.json +7 -0
  385. package/dist/workspace/member-commands.d.ts.map +1 -1
  386. package/dist/workspace/member-commands.js +54 -5
  387. package/dist/workspace/migrate-links.d.ts +45 -0
  388. package/dist/workspace/migrate-links.d.ts.map +1 -0
  389. package/dist/workspace/migrate-links.js +100 -0
  390. package/dist/workspace/migration-dirs.d.ts +74 -0
  391. package/dist/workspace/migration-dirs.d.ts.map +1 -0
  392. package/dist/workspace/migration-dirs.js +254 -0
  393. package/dist/workspace/new-migrations.d.ts +47 -0
  394. package/dist/workspace/new-migrations.d.ts.map +1 -0
  395. package/dist/workspace/new-migrations.js +91 -0
  396. package/dist/workspace/pr-report.schema.json +46 -0
  397. package/dist/workspace/promote.d.ts +96 -0
  398. package/dist/workspace/promote.d.ts.map +1 -0
  399. package/dist/workspace/promote.js +263 -0
  400. package/dist/workspace/reason-codes.d.ts +21 -0
  401. package/dist/workspace/reason-codes.d.ts.map +1 -1
  402. package/dist/workspace/reason-codes.js +25 -0
  403. package/dist/workspace/records-cli.d.ts +11 -0
  404. package/dist/workspace/records-cli.d.ts.map +1 -1
  405. package/dist/workspace/records-cli.js +57 -5
  406. package/dist/workspace/records.schema.json +5 -0
  407. package/dist/workspace/release-candidates.d.ts +2 -0
  408. package/dist/workspace/release-candidates.d.ts.map +1 -1
  409. package/dist/workspace/release-candidates.js +5 -2
  410. package/dist/workspace/release-train.d.ts +53 -0
  411. package/dist/workspace/release-train.d.ts.map +1 -0
  412. package/dist/workspace/release-train.js +134 -0
  413. package/dist/workspace/release-workflow.d.ts.map +1 -1
  414. package/dist/workspace/release-workflow.js +4 -3
  415. package/dist/workspace/repo-ref.d.ts +45 -0
  416. package/dist/workspace/repo-ref.d.ts.map +1 -0
  417. package/dist/workspace/repo-ref.js +114 -0
  418. package/dist/workspace/run-event.schema.json +2 -2
  419. package/dist/workspace/runs-cli.d.ts +9 -0
  420. package/dist/workspace/runs-cli.d.ts.map +1 -1
  421. package/dist/workspace/runs-cli.js +48 -3
  422. package/dist/workspace/runs.schema.json +7 -0
  423. package/dist/workspace/search-contract.d.ts +1 -1
  424. package/dist/workspace/search-contract.d.ts.map +1 -1
  425. package/dist/workspace/status-decisions.d.ts +37 -0
  426. package/dist/workspace/status-decisions.d.ts.map +1 -0
  427. package/dist/workspace/status-decisions.js +147 -0
  428. package/dist/workspace/status-gates.d.ts +9 -0
  429. package/dist/workspace/status-gates.d.ts.map +1 -1
  430. package/dist/workspace/status.d.ts +4 -0
  431. package/dist/workspace/status.d.ts.map +1 -1
  432. package/dist/workspace/status.js +14 -2
  433. package/dist/workspace/status.schema.json +6 -0
  434. package/dist/workspace/work-cli.d.ts.map +1 -1
  435. package/dist/workspace/work-cli.js +5 -3
  436. package/dist/workspace/work-evidence-ops.d.ts +83 -0
  437. package/dist/workspace/work-evidence-ops.d.ts.map +1 -0
  438. package/dist/workspace/work-evidence-ops.js +169 -0
  439. package/dist/workspace/work-evidence.d.ts +12 -2
  440. package/dist/workspace/work-evidence.d.ts.map +1 -1
  441. package/dist/workspace/work-evidence.js +21 -3
  442. package/dist/workspace/work-evidence.schema.json +49 -2
  443. package/dist/workspace/workspace-gates.d.ts +110 -0
  444. package/dist/workspace/workspace-gates.d.ts.map +1 -0
  445. package/dist/workspace/workspace-gates.js +140 -0
  446. package/dist/workspace/workspace-kinds.schema.json +108 -10
  447. package/dist/workspace/workspace-ledger.d.ts +76 -0
  448. package/dist/workspace/workspace-ledger.d.ts.map +1 -0
  449. package/dist/workspace/workspace-ledger.js +91 -0
  450. package/dist/workspace/workspace-run.d.ts +112 -0
  451. package/dist/workspace/workspace-run.d.ts.map +1 -0
  452. package/dist/workspace/workspace-run.js +432 -0
  453. package/package.json +1 -1
  454. package/src/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  455. package/src/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  456. package/src/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  457. package/src/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  458. package/src/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  459. package/src/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  460. package/src/__golden__/wave-loop/op-cost-member.json +13 -0
  461. package/src/__golden__/wave-loop/op-denied-source.json +16 -0
  462. package/src/__golden__/wave-loop/op-denied.json +16 -0
  463. package/src/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  464. package/src/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  465. package/src/__golden__/wave-loop/op-discover-rest.json +26 -0
  466. package/src/__golden__/wave-loop/op-discover-wide.json +26 -0
  467. package/src/__golden__/wave-loop/op-held-on-again.json +16 -0
  468. package/src/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  469. package/src/__golden__/wave-loop/op-held-on.json +16 -0
  470. package/src/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  471. package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  472. package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  473. package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  474. package/src/__golden__/wave-loop/op-hooks-held.json +13 -0
  475. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  476. package/src/__golden__/wave-loop/op-hooks-pass.json +13 -0
  477. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  478. package/src/__golden__/wave-loop/op-hooks-share.json +33 -0
  479. package/src/__golden__/wave-loop/op-outcome-held.json +10 -0
  480. package/src/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  481. package/src/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  482. package/src/__golden__/wave-loop/op-outcome-replan.json +10 -0
  483. package/src/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  484. package/src/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  485. package/src/__golden__/wave-loop/op-overridden.json +16 -0
  486. package/src/__golden__/wave-loop/op-per-run.json +20 -0
  487. package/src/__golden__/wave-loop/op-point.json +13 -0
  488. package/src/__golden__/wave-loop/op-pr-review.json +10 -0
  489. package/src/__golden__/wave-loop/op-sealed.json +10 -0
  490. package/src/__golden__/wave-loop/op-spend-change-only.json +10 -0
  491. package/src/__golden__/wave-loop/op-spend.json +10 -0
  492. package/src/__golden__/wave-loop/op-wave-commands.json +55 -0
  493. package/src/__golden__/wave-loop/op-wide-refused.json +26 -0
  494. package/src/__golden__/wave-loop/op-wide-waiting.json +29 -0
  495. package/src/__golden__/wave-loop/op-wide.json +39 -0
  496. package/src/action-pins.test.ts +78 -0
  497. package/src/action-pins.ts +92 -0
  498. package/src/cli/commands/carve-score-once.test.ts +99 -0
  499. package/src/cli/commands/carve.ts +37 -6
  500. package/src/cli/commands/check-lexicon.test.ts +2 -2
  501. package/src/cli/commands/import-report.ts +71 -0
  502. package/src/cli/commands/import.ts +33 -3
  503. package/src/cli/commands/migrate-dialect.test.ts +145 -0
  504. package/src/cli/commands/migrate.ts +132 -11
  505. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +4 -4
  506. package/src/cli/handlers/approve.ts +33 -4
  507. package/src/cli/handlers/build.ts +7 -1
  508. package/src/cli/handlers/graph-reader.test.ts +31 -0
  509. package/src/cli/handlers/graph-reader.ts +51 -0
  510. package/src/cli/handlers/graph.ts +14 -0
  511. package/src/cli/handlers/ledger.ts +54 -0
  512. package/src/cli/handlers/migrate.ts +21 -12
  513. package/src/cli/handlers/misc.ts +10 -1
  514. package/src/cli/handlers/pr-env.ts +2 -2
  515. package/src/cli/handlers/pr.ts +176 -37
  516. package/src/cli/handlers/query.ts +16 -4
  517. package/src/cli/handlers/release.ts +6 -3
  518. package/src/cli/handlers/search.ts +13 -34
  519. package/src/cli/main.test.ts +17 -0
  520. package/src/cli/main.ts +87 -14
  521. package/src/cli/parse-args.ts +35 -2
  522. package/src/cli/registry.ts +22 -4
  523. package/src/components/builders.ts +8 -0
  524. package/src/components/capability-plugin-loader.ts +13 -1
  525. package/src/components/capability-plugin.ts +16 -0
  526. package/src/components/pr-env-run-bundle.test.ts +1 -0
  527. package/src/components/pr-hooks.test.ts +73 -5
  528. package/src/components/pr-hooks.ts +95 -5
  529. package/src/components/pr-pipeline.ts +64 -16
  530. package/src/components/pr-policy.test.ts +64 -1
  531. package/src/components/pr-policy.ts +53 -0
  532. package/src/components/pr-run.ts +4 -0
  533. package/src/components/registry.test.ts +1 -1
  534. package/src/components/starter-plugin.ts +6 -0
  535. package/src/components/verbs/index.ts +2 -0
  536. package/src/components/verbs/publish-artifact.test.ts +60 -0
  537. package/src/components/verbs/publish-artifact.ts +122 -0
  538. package/src/components/verbs/publish-image.test.ts +101 -0
  539. package/src/components/verbs/publish-image.ts +265 -0
  540. package/src/config-environments.ts +28 -1
  541. package/src/config.ts +24 -1
  542. package/src/gate-decision-bundle.test.ts +5 -2
  543. package/src/gate-decision-identity.test.ts +20 -0
  544. package/src/gate-decision.ts +46 -3
  545. package/src/graph-ir.ts +27 -0
  546. package/src/graph-walk.test.ts +44 -0
  547. package/src/graph-walk.ts +102 -0
  548. package/src/import/parser.ts +20 -0
  549. package/src/lexicon.ts +98 -6
  550. package/src/lifecycle/gate-ledger.ts +8 -0
  551. package/src/lifecycle/git.ts +2 -2
  552. package/src/lifecycle/ledger-read.test.ts +107 -0
  553. package/src/lifecycle/ledger-read.ts +195 -0
  554. package/src/lifecycle/release-workstation.ts +24 -0
  555. package/src/lifecycle/run-ledger.ts +5 -2
  556. package/src/lint/cross-root.test.ts +122 -0
  557. package/src/lint/discover.ts +3 -1
  558. package/src/lint/post-synth.ts +212 -2
  559. package/src/lint/rules/op/index.ts +2 -0
  560. package/src/lint/rules/op/ops019-workspace-op-target.ts +89 -0
  561. package/src/op/activities/activity-contracts.ts +5 -2
  562. package/src/op/activities/index.ts +2 -2
  563. package/src/op/activities/review-agent.test.ts +221 -4
  564. package/src/op/activities/review-agent.ts +265 -22
  565. package/src/op/activities/work-evidence.ts +9 -1
  566. package/src/op/builders.ts +9 -5
  567. package/src/op/composites/apply-op.test.ts +108 -0
  568. package/src/op/composites/apply-op.ts +37 -2
  569. package/src/op/gate-approval-at-base.test.ts +97 -0
  570. package/src/op/gate-approval-base.ts +72 -0
  571. package/src/op/gate-approval.test.ts +6 -1
  572. package/src/op/gate-approval.ts +22 -27
  573. package/src/op/gate-name.ts +16 -1
  574. package/src/op/gate-quorum-at-base.test.ts +211 -0
  575. package/src/op/gate-quorum.ts +156 -8
  576. package/src/op/gate-review.ts +14 -0
  577. package/src/op/gate.ts +69 -9
  578. package/src/op/index.ts +3 -2
  579. package/src/op/local-executor.ts +91 -11
  580. package/src/op/op-waves-depends-on.test.ts +81 -0
  581. package/src/op/op-waves-hook-targets.test.ts +4 -2
  582. package/src/op/op-waves-quorum-product.test.ts +158 -0
  583. package/src/op/op-waves-run.ts +288 -14
  584. package/src/op/op-waves.ts +143 -14
  585. package/src/op/runtime.ts +8 -0
  586. package/src/op/schedule-overdue.ts +33 -0
  587. package/src/op/stage-hooks.ts +13 -5
  588. package/src/op/workspace-run-env.ts +29 -0
  589. package/src/pr-forge-bundle.test.ts +2 -2
  590. package/src/pr-forge.test.ts +47 -0
  591. package/src/pr-forge.ts +68 -0
  592. package/src/pr-loop-bundle.test.ts +3 -2
  593. package/src/pr-loop.test.ts +52 -0
  594. package/src/pr-loop.ts +12 -0
  595. package/src/pr-migrations.ts +97 -0
  596. package/src/pr-post-open.e2e.test.ts +36 -0
  597. package/src/pr-post.test.ts +102 -3
  598. package/src/pr-post.ts +90 -2
  599. package/src/read-filter.test.ts +54 -0
  600. package/src/read-filter.ts +193 -0
  601. package/src/run-event.ts +1 -1
  602. package/src/stage-hooks-bundle.test.ts +1 -0
  603. package/src/terraform/carve.ts +52 -15
  604. package/src/terraform/cuts.test.ts +173 -0
  605. package/src/terraform/cuts.ts +188 -0
  606. package/src/terraform/folds.ts +44 -0
  607. package/src/terraform/graph.ts +9 -0
  608. package/src/terraform/radius.test.ts +90 -0
  609. package/src/terraform/reach.test.ts +146 -0
  610. package/src/terraform/reach.ts +247 -0
  611. package/src/terraform/score.ts +27 -29
  612. package/src/terraform/state.ts +3 -1
  613. package/src/terraform/types.ts +8 -0
  614. package/src/wave-blocked.ts +28 -3
  615. package/src/wave-loop-golden.test.ts +4 -3
  616. package/src/workspace/__fixtures__/import/spacelift.json +9 -0
  617. package/src/workspace/__fixtures__/import/tfc.json +13 -0
  618. package/src/workspace/behold-kinds.test.ts +2 -1
  619. package/src/workspace/check-contract.test.ts +8 -6
  620. package/src/workspace/check.schema.json +13 -2
  621. package/src/workspace/checks/ci-branch.test.ts +128 -0
  622. package/src/workspace/checks/ci-branch.ts +141 -0
  623. package/src/workspace/checks/gate-approval.test.ts +77 -0
  624. package/src/workspace/checks/gate-approval.ts +121 -0
  625. package/src/workspace/checks/links.ts +12 -1
  626. package/src/workspace/checks/org.ts +169 -0
  627. package/src/workspace/checks.ts +68 -7
  628. package/src/workspace/ci-branches.ts +31 -0
  629. package/src/workspace/ci-green-branches.test.ts +153 -0
  630. package/src/workspace/ci-green-cli.ts +7 -7
  631. package/src/workspace/ci-green-workflow.test.ts +4 -3
  632. package/src/workspace/ci-green-workflow.ts +13 -9
  633. package/src/workspace/ci-green.ts +49 -12
  634. package/src/workspace/ci-run.test.ts +72 -1
  635. package/src/workspace/ci-run.ts +68 -8
  636. package/src/workspace/ci-serve.test.ts +95 -0
  637. package/src/workspace/ci-serve.ts +106 -65
  638. package/src/workspace/component-environments.test.ts +25 -2
  639. package/src/workspace/compose-graph.ts +9 -2
  640. package/src/workspace/composites.schema.json +22 -0
  641. package/src/workspace/composites.test.ts +39 -8
  642. package/src/workspace/composites.ts +6 -2
  643. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  644. package/src/workspace/cross-root-lint.test.ts +90 -0
  645. package/src/workspace/cross-root-lint.ts +126 -0
  646. package/src/workspace/dbt-kind.test.ts +3 -2
  647. package/src/workspace/dbt-models-reader.ts +362 -0
  648. package/src/workspace/dbt-models.test.ts +248 -0
  649. package/src/workspace/dbt-project-common.ts +113 -0
  650. package/src/workspace/dbt-project-reader.ts +29 -112
  651. package/src/workspace/decision-rank.ts +40 -0
  652. package/src/workspace/declaration.schema.json +166 -24
  653. package/src/workspace/declaration.ts +101 -15
  654. package/src/workspace/environments.ts +40 -4
  655. package/src/workspace/exit-cli.ts +219 -0
  656. package/src/workspace/exit.schema.json +257 -0
  657. package/src/workspace/exit.test.ts +185 -0
  658. package/src/workspace/exit.ts +560 -0
  659. package/src/workspace/flyway-dir.ts +113 -0
  660. package/src/workspace/flyway-layout.ts +337 -0
  661. package/src/workspace/graph-cache.ts +12 -2
  662. package/src/workspace/graph-cli.ts +25 -10
  663. package/src/workspace/graph-contract.test.ts +4 -2
  664. package/src/workspace/graph.schema.json +34 -0
  665. package/src/workspace/identity.test.ts +29 -2
  666. package/src/workspace/import-fetch.ts +151 -0
  667. package/src/workspace/import-init.ts +147 -0
  668. package/src/workspace/import-source.test.ts +183 -0
  669. package/src/workspace/import-source.ts +462 -0
  670. package/src/workspace/init.test.ts +55 -1
  671. package/src/workspace/init.ts +66 -4
  672. package/src/workspace/intent.ts +6 -14
  673. package/src/workspace/kind-link-readers.ts +68 -3
  674. package/src/workspace/kind-readers.test.ts +36 -0
  675. package/src/workspace/kind-readers.ts +18 -1
  676. package/src/workspace/kinds.test.ts +35 -7
  677. package/src/workspace/kinds.ts +212 -11
  678. package/src/workspace/ledger.schema.json +158 -0
  679. package/src/workspace/lexicon-outputs.ts +132 -13
  680. package/src/workspace/lineage-check.ts +30 -7
  681. package/src/workspace/lineage-upgrade.test.ts +20 -0
  682. package/src/workspace/lineage-upgrade.ts +16 -7
  683. package/src/workspace/links.ts +24 -1
  684. package/src/workspace/ls-contract.test.ts +1 -1
  685. package/src/workspace/ls.schema.json +7 -0
  686. package/src/workspace/ls.ts +10 -1
  687. package/src/workspace/member-commands.ts +60 -2
  688. package/src/workspace/member-file-schema.test.ts +124 -0
  689. package/src/workspace/migrate-links.test.ts +99 -0
  690. package/src/workspace/migrate-links.ts +108 -0
  691. package/src/workspace/migration-dirs.test.ts +272 -0
  692. package/src/workspace/migration-dirs.ts +267 -0
  693. package/src/workspace/new-migrations.test.ts +98 -0
  694. package/src/workspace/new-migrations.ts +109 -0
  695. package/src/workspace/org-scope.test.ts +238 -0
  696. package/src/workspace/pr-report.schema.json +46 -0
  697. package/src/workspace/promote.test.ts +114 -0
  698. package/src/workspace/promote.ts +302 -0
  699. package/src/workspace/reason-codes.test.ts +10 -1
  700. package/src/workspace/reason-codes.ts +25 -0
  701. package/src/workspace/records-cli.ts +58 -5
  702. package/src/workspace/records.schema.json +5 -0
  703. package/src/workspace/release-candidates.ts +7 -2
  704. package/src/workspace/release-train.ts +157 -0
  705. package/src/workspace/release-workflow.ts +4 -3
  706. package/src/workspace/repo-ref.ts +113 -0
  707. package/src/workspace/run-event.schema.json +2 -2
  708. package/src/workspace/runs-cli.ts +48 -4
  709. package/src/workspace/runs.schema.json +7 -0
  710. package/src/workspace/status-contract.test.ts +46 -1
  711. package/src/workspace/status-decisions.test.ts +100 -0
  712. package/src/workspace/status-decisions.ts +151 -0
  713. package/src/workspace/status-gates.ts +9 -0
  714. package/src/workspace/status.schema.json +6 -0
  715. package/src/workspace/status.ts +17 -3
  716. package/src/workspace/where-filters.test.ts +39 -0
  717. package/src/workspace/work-cli.ts +5 -3
  718. package/src/workspace/work-evidence-ops.test.ts +176 -0
  719. package/src/workspace/work-evidence-ops.ts +189 -0
  720. package/src/workspace/work-evidence.schema.json +49 -2
  721. package/src/workspace/work-evidence.ts +22 -3
  722. package/src/workspace/workspace-gates.ts +200 -0
  723. package/src/workspace/workspace-kinds.schema.json +108 -10
  724. package/src/workspace/workspace-ledger.ts +130 -0
  725. package/src/workspace/workspace-run.test.ts +322 -0
  726. package/src/workspace/workspace-run.ts +461 -0
@@ -0,0 +1,337 @@
1
+ /**
2
+ * A Flyway project's layout, read from a workspace tree (#4400): its
3
+ * `flyway.toml` or `flyway.conf`, the locations it names, read recursively,
4
+ * and the placeholders it substitutes.
5
+ *
6
+ * This is the layout reading of sql-yodeler's `src/check/flyway.ts` (#250),
7
+ * moved here so the workspace kind and `yodel check` share one reader. It
8
+ * works on a {@link WorkspaceTree} and tree-relative paths instead of the
9
+ * file system, and it never runs Flyway or connects to a database.
10
+ *
11
+ * The configuration is the first `flyway.toml`, else `flyway.conf`, found in
12
+ * the directory checked or a directory above it, up to the tree's root. Its
13
+ * locations are read relative to the file's own directory:
14
+ *
15
+ * filesystem:<path> that directory
16
+ * classpath:<path> src/main/resources/<path> (and, for code
17
+ * <path> migrations, src/main/java/<path> and src/main/kotlin/<path>)
18
+ *
19
+ * With no configuration, or one that names no location, the directory
20
+ * checked is the one location. A location outside the tree is reported by
21
+ * {@link FlywayConfig.outside}, not read. A Java or Kotlin migration in a
22
+ * location is not SQL and is reported by {@link codeMigrations}.
23
+ *
24
+ * Placeholders are `${name}` (the prefix and suffix are configurable), from
25
+ * `flyway.placeholders.<name>` (conf) or `[flyway.placeholders]` (toml), plus
26
+ * Flyway's own: `flyway:defaultSchema`, `flyway:user`, `flyway:database`,
27
+ * `flyway:timestamp`, `flyway:filename`, `flyway:workingDirectory`,
28
+ * `flyway:table` and `flyway:environment`. {@link substitute} replaces them
29
+ * for a replay; the reader that lists migrations leaves the text as it is.
30
+ */
31
+
32
+ import { posix } from "node:path";
33
+ import { joinPath, type WorkspaceTree } from "./tree";
34
+
35
+ export interface FlywayConfig {
36
+ /** The configuration file, tree-relative; undefined with none. */
37
+ file?: string;
38
+ /** Where file names are relative to: the configuration's directory, else the directory checked. Tree-relative. */
39
+ root: string;
40
+ /** The SQL locations, tree-relative directories. */
41
+ locations: string[];
42
+ /** Directories code migrations (Java, Kotlin) of a classpath location would be in. */
43
+ codeLocations: string[];
44
+ /** Locations the configuration names that lie outside the tree, as written. */
45
+ outside: string[];
46
+ placeholders: Record<string, string>;
47
+ placeholderPrefix: string;
48
+ placeholderSuffix: string;
49
+ placeholderReplacement: boolean;
50
+ sqlMigrationPrefix: string;
51
+ repeatableSqlMigrationPrefix: string;
52
+ baselineMigrationPrefix: string;
53
+ undoSqlMigrationPrefix: string;
54
+ sqlMigrationSeparator: string;
55
+ sqlMigrationSuffixes: string[];
56
+ defaultSchema?: string;
57
+ user?: string;
58
+ database?: string;
59
+ /** The JDBC URL, when the configuration has one. */
60
+ url?: string;
61
+ /** The dialect the URL names: `postgres` or `clickhouse`; undefined for another or none. */
62
+ dialect?: string;
63
+ table: string;
64
+ environment?: string;
65
+ }
66
+
67
+ /** The first `flyway.toml`, else `flyway.conf`, in `dir` or a directory above it, up to the tree's root. */
68
+ export function findFlywayConfig(tree: WorkspaceTree, dir: string): string | undefined {
69
+ let at = dir;
70
+ for (;;) {
71
+ for (const name of ["flyway.toml", "flyway.conf"]) {
72
+ const f = joinPath(at, name);
73
+ if (tree.stat(f) === "file") return f;
74
+ }
75
+ if (at === "" || at === ".") return undefined;
76
+ at = posix.dirname(at) === "." ? "" : posix.dirname(at);
77
+ }
78
+ }
79
+
80
+ type Values = Record<string, string | string[] | boolean>;
81
+
82
+ /** Java-properties `flyway.*` keys, without the `flyway.` prefix. */
83
+ export function parseFlywayConf(text: string): Values {
84
+ const out: Values = {};
85
+ const lines = text.split(/\r?\n/);
86
+ for (let i = 0; i < lines.length; i++) {
87
+ let line = lines[i] ?? "";
88
+ while (/\\$/.test(line) && i + 1 < lines.length) line = line.slice(0, -1) + (lines[++i] ?? "").trimStart();
89
+ const t = line.trim();
90
+ if (t === "" || t.startsWith("#") || t.startsWith("!")) continue;
91
+ const m = /^([^=:\s]+)\s*[=:]\s*(.*)$/.exec(t);
92
+ if (!m) continue;
93
+ const key = m[1] ?? "";
94
+ if (!key.startsWith("flyway.")) continue;
95
+ out[key.slice("flyway.".length)] = (m[2] ?? "").trim();
96
+ }
97
+ return out;
98
+ }
99
+
100
+ /** A TOML document, as far as Flyway's configuration uses it: tables, strings, arrays of strings, booleans and numbers. */
101
+ export function parseToml(text: string): Record<string, Values> {
102
+ const tables: Record<string, Values> = { "": {} };
103
+ let current = "";
104
+ const lines = text.split(/\r?\n/);
105
+ for (let i = 0; i < lines.length; i++) {
106
+ const t = stripComment(lines[i] ?? "").trim();
107
+ if (t === "") continue;
108
+ const table = /^\[\s*([^\]]+?)\s*\]$/.exec(t);
109
+ if (table) {
110
+ current = (table[1] ?? "").split(".").map((k) => unquote(k.trim())).join(".");
111
+ tables[current] ??= {};
112
+ continue;
113
+ }
114
+ const kv = /^("[^"]*"|'[^']*'|[A-Za-z0-9_.:-]+)\s*=\s*(.*)$/.exec(t);
115
+ if (!kv) continue;
116
+ let raw = kv[2] ?? "";
117
+ // A multi-line array runs to its closing bracket.
118
+ while (raw.trimStart().startsWith("[") && !balanced(raw) && i + 1 < lines.length) raw += ` ${stripComment(lines[++i] ?? "").trim()}`;
119
+ const path = unquote(kv[1] ?? "").split(".");
120
+ const key = path.pop() ?? "";
121
+ const where = [current, ...path].filter(Boolean).join(".");
122
+ tables[where] ??= {};
123
+ tables[where][key] = tomlValue(raw.trim());
124
+ }
125
+ return tables;
126
+ }
127
+
128
+ function stripComment(line: string): string {
129
+ let quote: string | undefined;
130
+ for (let i = 0; i < line.length; i++) {
131
+ const c = line[i];
132
+ if (quote) {
133
+ if (c === "\\" && quote === '"') i++;
134
+ else if (c === quote) quote = undefined;
135
+ } else if (c === '"' || c === "'") quote = c;
136
+ else if (c === "#") return line.slice(0, i);
137
+ }
138
+ return line;
139
+ }
140
+
141
+ const balanced = (s: string) => (s.match(/\[/g) ?? []).length <= (s.match(/\]/g) ?? []).length;
142
+
143
+ function unquote(s: string): string {
144
+ if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) return s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
145
+ return s;
146
+ }
147
+
148
+ function tomlValue(raw: string): string | string[] | boolean {
149
+ if (raw === "true" || raw === "false") return raw === "true";
150
+ if (raw.startsWith("[")) {
151
+ const inner = raw.replace(/^\[/, "").replace(/\]\s*$/, "");
152
+ return (inner.match(/"(?:[^"\\]|\\.)*"|'[^']*'|[^,\s]+/g) ?? []).map((v) => unquote(v.trim())).filter((v) => v !== "");
153
+ }
154
+ return unquote(raw);
155
+ }
156
+
157
+ const list = (v: string | string[] | boolean | undefined): string[] | undefined =>
158
+ v === undefined || typeof v === "boolean" ? undefined : Array.isArray(v) ? v : v.split(",").map((s) => s.trim()).filter(Boolean);
159
+ const str = (v: string | string[] | boolean | undefined): string | undefined => (typeof v === "string" ? v : Array.isArray(v) ? v.join(",") : undefined);
160
+
161
+ /** The database name a JDBC URL names: the path after the host. */
162
+ function databaseOf(url: string | undefined): string | undefined {
163
+ const m = url === undefined ? null : /^jdbc:[a-z0-9]+:\/\/[^/]+\/([^?;]+)/i.exec(url);
164
+ return m?.[1] ?? undefined;
165
+ }
166
+
167
+ /** The dialect a JDBC URL names. */
168
+ function dialectOf(url: string | undefined): string | undefined {
169
+ const m = url === undefined ? null : /^jdbc:([a-z0-9]+):/i.exec(url);
170
+ const driver = m?.[1]?.toLowerCase();
171
+ return driver === "postgresql" ? "postgres" : driver === "clickhouse" ? "clickhouse" : undefined;
172
+ }
173
+
174
+ /** The configuration `file` holds, or the defaults with `dir` as the one location when there is none. */
175
+ export function readFlywayConfig(tree: WorkspaceTree, file: string | undefined, dir: string): FlywayConfig {
176
+ let values: Values = {};
177
+ let placeholders: Record<string, string> = {};
178
+ if (file !== undefined) {
179
+ const text = tree.read(file);
180
+ if (file.endsWith(".toml")) {
181
+ const t = parseToml(text);
182
+ const flyway = t.flyway ?? {};
183
+ const name = str(flyway.environment) ?? "default";
184
+ const env = t[`environments.${name}`] ?? {};
185
+ values = { ...flyway, ...(t[`environments.${name}.flyway`] ?? {}), ...(env.url !== undefined ? { url: env.url } : {}), ...(env.user !== undefined ? { user: env.user } : {}), ...(env.schemas !== undefined ? { schemas: env.schemas } : {}) };
186
+ if (str(flyway.environment) !== undefined) values.environment = name;
187
+ placeholders = Object.fromEntries(Object.entries({ ...(t["flyway.placeholders"] ?? {}), ...(t[`environments.${name}.flyway.placeholders`] ?? {}) }).map(([k, v]) => [k, str(v) ?? String(v)]));
188
+ } else {
189
+ values = parseFlywayConf(text);
190
+ placeholders = Object.fromEntries(Object.entries(values).flatMap(([k, v]) => (k.startsWith("placeholders.") ? [[k.slice("placeholders.".length), str(v) ?? ""]] : [])));
191
+ }
192
+ }
193
+ const root = file === undefined ? dir : posix.dirname(file) === "." ? "" : posix.dirname(file);
194
+ const named = list(values.locations) ?? [];
195
+ const locations: string[] = [];
196
+ const codeLocations: string[] = [];
197
+ const outside: string[] = [];
198
+ const inside = (...parts: string[]): string | undefined => {
199
+ const p = posix.normalize(joinPath(...parts)) || ".";
200
+ if (p === ".." || p.startsWith("../") || posix.isAbsolute(p)) return undefined;
201
+ return p === "." ? "" : p;
202
+ };
203
+ for (const l of named) {
204
+ const m = /^(filesystem|classpath):(.*)$/.exec(l);
205
+ const kind = m?.[1] ?? "classpath";
206
+ const raw = m?.[2] ?? l;
207
+ if (kind === "filesystem") {
208
+ const at = posix.isAbsolute(raw) ? undefined : inside(root, raw);
209
+ if (at === undefined) outside.push(l);
210
+ else locations.push(at);
211
+ } else {
212
+ const path = raw.replace(/^\/+/, "");
213
+ const res = inside(root, "src/main/resources", path);
214
+ if (res === undefined) outside.push(l);
215
+ else {
216
+ locations.push(res);
217
+ for (const lang of ["java", "kotlin"]) {
218
+ const c = inside(root, "src/main", lang, path);
219
+ if (c !== undefined) codeLocations.push(c);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ if (named.length === 0) locations.push(dir);
225
+ const schemas = list(values.schemas);
226
+ const replacement = values.placeholderReplacement;
227
+ const defaultSchema = str(values.defaultSchema) ?? schemas?.[0];
228
+ const user = str(values.user);
229
+ const url = str(values.url);
230
+ const database = databaseOf(url);
231
+ const dialect = dialectOf(url);
232
+ const environment = str(values.environment);
233
+ return {
234
+ ...(file !== undefined ? { file } : {}),
235
+ root,
236
+ locations,
237
+ codeLocations,
238
+ outside,
239
+ placeholders,
240
+ placeholderPrefix: str(values.placeholderPrefix) ?? "${",
241
+ placeholderSuffix: str(values.placeholderSuffix) ?? "}",
242
+ placeholderReplacement: !(replacement === false || replacement === "false"),
243
+ sqlMigrationPrefix: str(values.sqlMigrationPrefix) ?? "V",
244
+ repeatableSqlMigrationPrefix: str(values.repeatableSqlMigrationPrefix) ?? "R",
245
+ baselineMigrationPrefix: str(values.baselineMigrationPrefix) ?? "B",
246
+ undoSqlMigrationPrefix: str(values.undoSqlMigrationPrefix) ?? "U",
247
+ sqlMigrationSeparator: str(values.sqlMigrationSeparator) ?? "__",
248
+ sqlMigrationSuffixes: list(values.sqlMigrationSuffixes) ?? [".sql"],
249
+ ...(defaultSchema !== undefined ? { defaultSchema } : {}),
250
+ ...(user !== undefined ? { user } : {}),
251
+ ...(database !== undefined ? { database } : {}),
252
+ ...(url !== undefined ? { url } : {}),
253
+ ...(dialect !== undefined ? { dialect } : {}),
254
+ table: str(values.table) ?? "flyway_schema_history",
255
+ ...(environment !== undefined ? { environment } : {}),
256
+ };
257
+ }
258
+
259
+ /** Every file under `dir`, recursively, tree-relative, sorted; none when it does not exist. */
260
+ export function walk(tree: WorkspaceTree, dir: string): string[] {
261
+ const entries = tree.list(dir);
262
+ if (!entries) return [];
263
+ const out: string[] = [];
264
+ for (const e of [...entries].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
265
+ const p = joinPath(dir, e.name);
266
+ if (e.type === "dir") out.push(...walk(tree, p));
267
+ else out.push(p);
268
+ }
269
+ return out;
270
+ }
271
+
272
+ const base = (f: string): string => posix.basename(f);
273
+
274
+ /** A Flyway SQL callback (`beforeMigrate.sql`, `afterEachMigrate__notify.sql`, ...): run around a migrate, not a migration. */
275
+ export const isCallback = (file: string): boolean => /^(before|after)[A-Z][A-Za-z]*(__.*)?\.[A-Za-z]+$/.test(base(file));
276
+
277
+ /** The code migrations (Java, Kotlin) in `config`'s locations, relative to the tree. */
278
+ export function codeMigrations(tree: WorkspaceTree, config: FlywayConfig): string[] {
279
+ const code = /\.(java|kt|class)$/;
280
+ const prefixes = [config.sqlMigrationPrefix, config.repeatableSqlMigrationPrefix, config.baselineMigrationPrefix].filter(Boolean);
281
+ const migration = (f: string) => code.test(f) && prefixes.some((p) => base(f).startsWith(p)) && base(f).includes(config.sqlMigrationSeparator);
282
+ return [...new Set([...config.locations, ...config.codeLocations])].flatMap((d) => walk(tree, d)).filter(migration);
283
+ }
284
+
285
+ /** The values Flyway gives its own placeholders, for `file`. */
286
+ function builtins(config: FlywayConfig, file: string, fallback: { defaultSchema?: string | undefined }): Record<string, string | undefined> {
287
+ return {
288
+ "flyway:defaultSchema": config.defaultSchema ?? fallback.defaultSchema,
289
+ "flyway:user": config.user,
290
+ "flyway:database": config.database,
291
+ "flyway:timestamp": new Date().toISOString().replace("T", " ").replace(/\..*$/, ""),
292
+ "flyway:filename": base(file),
293
+ "flyway:workingDirectory": process.cwd(),
294
+ "flyway:table": config.table,
295
+ "flyway:environment": config.environment,
296
+ };
297
+ }
298
+
299
+ const escape = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
300
+
301
+ /** A placeholder with no value; `file` is as the caller passed it, `line` is from 1. */
302
+ export class FlywayPlaceholderError extends Error {
303
+ constructor(
304
+ readonly file: string,
305
+ readonly line: number,
306
+ readonly placeholder: string,
307
+ readonly name: string,
308
+ readonly where: string,
309
+ ) {
310
+ super(`${file}:${String(line)}: no value for the placeholder ${placeholder}; set it in ${where} (flyway.placeholders.${name} in flyway.conf, [flyway.placeholders] in flyway.toml)`);
311
+ }
312
+ }
313
+
314
+ /**
315
+ * `text` with its placeholders replaced, line by line so line numbers hold.
316
+ * A placeholder with no value throws a {@link FlywayPlaceholderError} naming
317
+ * `file` and the line, as Flyway's own run would fail.
318
+ */
319
+ export function substitute(text: string, file: string, config: FlywayConfig, fallback: { defaultSchema?: string | undefined } = {}): string {
320
+ if (!config.placeholderReplacement) return text;
321
+ const values: Record<string, string | undefined> = { ...builtins(config, file, fallback), ...config.placeholders };
322
+ const lower = new Map(Object.entries(values).map(([k, v]) => [k.toLowerCase(), v]));
323
+ const pattern = new RegExp(`${escape(config.placeholderPrefix)}([A-Za-z0-9_.:-]+)${escape(config.placeholderSuffix)}`, "g");
324
+ return text
325
+ .split("\n")
326
+ .map((line, i) =>
327
+ line.replace(pattern, (whole, name: string) => {
328
+ const v = values[name] ?? lower.get(name.toLowerCase());
329
+ if (v === undefined) {
330
+ const where = config.file === undefined ? "a flyway.toml or flyway.conf beside the migrations" : config.file;
331
+ throw new FlywayPlaceholderError(file, i + 1, whole, name, where);
332
+ }
333
+ return v;
334
+ }),
335
+ )
336
+ .join("\n");
337
+ }
@@ -204,14 +204,24 @@ export function toolchainStamp(toolchain: Toolchain): string {
204
204
  }
205
205
 
206
206
  /** What a kind's reader adds to the key: its graph block, and the installed version of the package supplying it. */
207
- export function readerStamp(reader: { lexicon: string; config: unknown; packageDir: string }): string {
207
+ export function readerStamp(reader: { lexicon?: string; reader?: string; readerFile?: string; config: unknown; packageDir: string }): string {
208
208
  let version = "";
209
209
  try {
210
210
  version = String((JSON.parse(readFileSync(join(reader.packageDir, "package.json"), "utf-8")) as { version?: unknown }).version ?? "");
211
211
  } catch {
212
212
  // An unreadable manifest keys on the directory alone.
213
213
  }
214
- return JSON.stringify([reader.lexicon, reader.config, reader.packageDir, version]);
214
+ // A reader module is code: its size and mtime key too, so an edit under the same version is a new read (#4413).
215
+ let module = "";
216
+ if (reader.readerFile) {
217
+ try {
218
+ const st = statSync(reader.readerFile);
219
+ module = `${reader.reader}:${st.size}:${st.mtimeMs}`;
220
+ } catch {
221
+ module = `${reader.reader}:unreadable`;
222
+ }
223
+ }
224
+ return JSON.stringify([reader.lexicon, reader.config, reader.packageDir, version, ...(module ? [module] : [])]);
215
225
  }
216
226
 
217
227
  /** The ambient environment, less {@link VOLATILE_ENV}, as one digest. */
@@ -35,9 +35,10 @@ import { composedId, composeWorkspaceGraph, readMemberIr, type ComposedMember, t
35
35
  import { isViewDocument, prefixView } from "../capabilities/view";
36
36
  import { refusedCapabilityView } from "./nested-graph";
37
37
  import { openGraphCache, planStamps, splitCached, storeReads, withoutUnits } from "./graph-cache";
38
- import { readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type Declaration, type ErrorLocation } from "./declaration";
38
+ import { readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type Declaration, type ErrorLocation, type RepoRef } from "./declaration";
39
39
  import { describePlan, emitDocument, executePlan, memberStatus, planJson, planMembers, type MemberArgv, type MemberPlan, type Toolchain, type UnitResult } from "./member-commands";
40
- import { loadKindRegistry, type KindRegistry } from "./kinds";
40
+ import { loadKindRegistry, REPO_KIND, type KindRegistry } from "./kinds";
41
+ import { repoIdentity } from "./repo-ref";
41
42
  import { readKindLinks } from "./kind-link-readers";
42
43
  import { prepareKindReaders, type PreparedReaders } from "./kind-readers";
43
44
  import { recordLinkRows } from "./record-assets";
@@ -335,10 +336,11 @@ export async function workspaceGraph(query: GraphQuery): Promise<GraphResult> {
335
336
  }
336
337
  // Links (#2539) resolve against the declaration that was read, the revision's for --at, and the kinds installed now.
337
338
  // A kind's link reader (#3932) reads the member's files in the tree read, as check does.
338
- const graph = composeWorkspaceGraph({ name: declaration.name, root: located.root }, inputs, { declaration, kinds, read: readKindLinks(declaration, located.tree, kinds) });
339
+ const graph = composeWorkspaceGraph({ name: declaration.name, root: located.root, repo: repoIdentity(located.rootOnDisk) }, inputs, { declaration, kinds, read: readKindLinks(declaration, located.tree, kinds) });
339
340
  // The box's factory and listing (#3146), from the declaration read, so --at gives them at that revision.
340
341
  for (const member of graph.members) member.box = graphBox(declaration.members.find((m) => m.name === member.name)?.box ?? null, located.tree, declaration.members);
341
342
  // Nested workspaces (#2551, ws-071): read-only, through their own chant workspace graph.
343
+ // Repo members (#3168): another repository's workspace at its pinned revision, read the same way.
342
344
  const nestedFailed = await expandNested(graph, declaration.members, query, located.rootOnDisk, located.at);
343
345
  let recordsFailed = false;
344
346
  if (query.kind !== undefined) {
@@ -365,33 +367,46 @@ export async function workspaceGraph(query: GraphQuery): Promise<GraphResult> {
365
367
 
366
368
  /**
367
369
  * Expand every member of kind `workspace` the query covers, read-only (#2551,
368
- * ws-071). Returns whether one could not be read.
370
+ * ws-071), and every member of kind `repo` the same way, at its pinned
371
+ * revision, fetched into the user's cache (#3168). Returns whether one could
372
+ * not be read.
369
373
  */
370
374
  async function expandNested(
371
375
  graph: WorkspaceGraph,
372
- members: readonly { name: string; dir: string; kind: string }[],
376
+ members: readonly { name: string; dir: string; kind: string; repo: RepoRef | null }[],
373
377
  query: GraphQuery,
374
378
  rootOnDisk: string,
375
379
  at: string | null,
376
380
  ): Promise<boolean> {
377
- const nested = members.filter((m) => m.kind === "workspace" && (!query.members?.length || query.members.includes(m.name)));
381
+ const covered = (m: { name: string }) => !query.members?.length || query.members.includes(m.name);
382
+ const nested = members.filter((m) => (m.kind === "workspace" || (m.kind === REPO_KIND && m.repo !== null)) && covered(m));
378
383
  if (nested.length === 0) return false;
379
384
  const { foldNested, readNested } = await import("./nested-graph");
385
+ const { fetchRepoAt } = await import("./repo-ref");
380
386
  const reads = await Promise.all(
381
387
  nested.map(async (m) => {
388
+ if (m.repo) {
389
+ // The other repository at its pinned revision, whatever revision the outer read is at.
390
+ const fetched = fetchRepoAt(m.repo, rootOnDisk);
391
+ if (!fetched.ok) return { m, read: null, missing: fetched.message };
392
+ if (!existsSync(fetched.dir)) return { m, read: null, missing: `${m.repo.url} at ${m.repo.rev} has no directory ${m.repo.workspace}` };
393
+ return { m, read: await readNested(fetched.dir, query.args ?? {}, m.repo.rev, GRAPH_OUTPUT_SCHEMA_ID, GRAPH_CONTRACT_VERSION, query.reader), missing: null };
394
+ }
382
395
  const abs = join(rootOnDisk, ...m.dir.split("/"));
383
- if (!existsSync(abs)) return { m, read: null };
384
- return { m, read: await readNested(abs, query.args ?? {}, at, GRAPH_OUTPUT_SCHEMA_ID, GRAPH_CONTRACT_VERSION, query.reader) };
396
+ if (!existsSync(abs)) return { m, read: null, missing: null };
397
+ return { m, read: await readNested(abs, query.args ?? {}, at, GRAPH_OUTPUT_SCHEMA_ID, GRAPH_CONTRACT_VERSION, query.reader), missing: null };
385
398
  }),
386
399
  );
387
400
  let failed = false;
388
- for (const { m, read } of reads) {
401
+ for (const { m, read, missing } of reads) {
389
402
  const member = graph.members.find((x) => x.name === m.name);
390
403
  if (!member) continue;
404
+ if (m.repo) member.repo = { ...m.repo, read: read?.ok ? (read.doc.workspace.repo ?? null) : null };
391
405
  if (!read) {
392
406
  failed = true;
393
407
  member.status = "failed";
394
- member.reason = { code: "dir-missing", message: `the nested workspace's directory ${m.dir} does not exist` };
408
+ // A repo that can't be fetched is a read that failed; the contract's codes stay as they are.
409
+ member.reason = missing !== null ? { code: "command-failed", message: missing } : { code: "dir-missing", message: `the nested workspace's directory ${m.dir} does not exist` };
395
410
  continue;
396
411
  }
397
412
  if (read.stderr.trim() && query.onStderr) query.onStderr(read.stderr.endsWith("\n") ? read.stderr : `${read.stderr}\n`);
@@ -230,7 +230,9 @@ describe("chant workspace graph on the chant repo (#2557)", () => {
230
230
  const g = result(doc);
231
231
  expectValid(g);
232
232
  expect(failed).toBe(false);
233
- expect(g.workspace).toEqual({ name: "chant", root: "." });
233
+ expect(g.workspace).toMatchObject({ name: "chant", root: "." });
234
+ // The repo identity (#3168) is this checkout's origin, whatever it is here.
235
+ expect(g.workspace.repo === null || typeof g.workspace.repo === "string").toBe(true);
234
236
  expect(g.at).toBeNull();
235
237
  expect(g.members.every((m) => m.status === "skipped" && m.reason?.code === "kind-not-run")).toBe(true);
236
238
  });
@@ -294,7 +296,7 @@ describe("chant workspace graph on built workspaces", () => {
294
296
  for (const at of [undefined, sha]) {
295
297
  const g = result((await workspaceGraph({ cwd: join(top, "infra", "api"), at })).doc);
296
298
  expectValid(g);
297
- expect(g.workspace).toEqual({ name: "acme", root: "infra" });
299
+ expect(g.workspace).toEqual({ name: "acme", root: "infra", repo: null });
298
300
  expect(g.nodes.map((n) => n.id)).toEqual(["api/Queue"]);
299
301
  }
300
302
  });
@@ -67,6 +67,13 @@
67
67
  "type": "string",
68
68
  "description": "The declared name. With `at`, it identifies the workspace."
69
69
  },
70
+ "repo": {
71
+ "type": [
72
+ "string",
73
+ "null"
74
+ ],
75
+ "description": "Added in contract 1 by #3168. The repository the document came from: the URL of the remote origin of the git repository that holds the workspace, with any user name or password removed, or null when there is no origin or the workspace is outside git. With name, root and the revision, it says which repository's workspace a document describes, so a reader that gathers several repositories' documents, such as an org workspace's repo members, tells them apart."
76
+ },
70
77
  "root": {
71
78
  "type": "string",
72
79
  "description": "The workspace root relative to the git root, with / separators, \".\" for the git root itself. An absolute path outside a git repository."
@@ -287,6 +294,33 @@
287
294
  "type": "string",
288
295
  "description": "Relative to the workspace root, with / separators; \".\" is the root member."
289
296
  },
297
+ "repo": {
298
+ "type": "object",
299
+ "description": "Added in contract 1 by #3168. For a member of kind repo, the other repository's workspace it names: url, the pinned rev and the workspace root inside that repository. Absent for every other kind. A repo member's dir is @repo/<name>, a marker that names no directory of this repository.",
300
+ "required": [
301
+ "url",
302
+ "rev",
303
+ "workspace"
304
+ ],
305
+ "properties": {
306
+ "url": {
307
+ "type": "string"
308
+ },
309
+ "rev": {
310
+ "type": "string"
311
+ },
312
+ "workspace": {
313
+ "type": "string"
314
+ },
315
+ "read": {
316
+ "type": [
317
+ "string",
318
+ "null"
319
+ ],
320
+ "description": "The repo identity the other repository's own graph document gave (its workspace.repo), or null when it gave none or the read failed."
321
+ }
322
+ }
323
+ },
290
324
  "kind": {
291
325
  "type": "string"
292
326
  },
@@ -17,6 +17,7 @@ import { emptyPolicy, type TrustPolicy } from "./trust/policy";
17
17
  import { sealGateApproval } from "./trust/seal";
18
18
  import { hasSshKeygen, TestRepo, type Key } from "./trust/test-repo";
19
19
  import { evaluateGate, memoryGateLedgerPort } from "../op/gate";
20
+ import { gatePolicyVersion } from "../op/gate-approval";
20
21
  import type { GateResolutionRecord, PendingGateRecord } from "../lifecycle/gate-ledger";
21
22
 
22
23
  const decl = (identity: unknown): Declaration =>
@@ -71,14 +72,40 @@ describe("identity.attribution", () => {
71
72
  expect(decl({ attribution: "identified", gates: { ship: { class: "operator" }, rollback: {} } }).identity).toEqual({
72
73
  attribution: "identified",
73
74
  gates: {
74
- ship: { gate: "ship", class: "operator", pointer: "/identity/gates/ship" },
75
- rollback: { gate: "rollback", class: null, pointer: "/identity/gates/rollback" },
75
+ ship: { gate: "ship", class: "operator", quorum: null, policy: null, mode: null, context: null, pointer: "/identity/gates/ship" },
76
+ rollback: { gate: "rollback", class: null, quorum: null, policy: null, mode: null, context: null, pointer: "/identity/gates/rollback" },
76
77
  },
77
78
  });
78
79
  expect(() => decl({ attribution: "verified" })).toThrow();
79
80
  expect(() => decl({ gates: { ship: { by: "x" } } })).toThrow();
80
81
  });
81
82
 
83
+ test("a gate entry takes a quorum beside its class (#4324)", () => {
84
+ const rules = [{ targets: ["prod-*"], count: 2, roles: ["platform"] }, { count: 1 }];
85
+ const gates = decl({ gates: { ship: { class: "operator", quorum: { count: 2 } }, deploy: { quorum: rules } } }).identity!.gates;
86
+ expect(gates.ship).toEqual({ gate: "ship", class: "operator", quorum: { count: 2 }, policy: null, mode: null, context: null, pointer: "/identity/gates/ship" });
87
+ expect(gates.deploy!.quorum).toEqual(rules);
88
+ // The schema refuses a malformed rule, and the reader refuses notAuthor, which an Op gate can't judge.
89
+ expect(() => decl({ gates: { ship: { quorum: { count: 0 } } } })).toThrow();
90
+ expect(() => decl({ gates: { ship: { quorum: [] } } })).toThrow();
91
+ expect(() => decl({ gates: { ship: { quorum: { count: 1, by: "x" } } } })).toThrow();
92
+ expect(() => decl({ gates: { ship: { quorum: { count: 1, notAuthor: true } } } })).toThrow(/identity\.gates\.ship\.quorum\.notAuthor is not read here/);
93
+ });
94
+
95
+ test("a gate entry takes a policy, a mode and a context beside its quorum (#4404)", () => {
96
+ const text = "permit (principal, action, resource);";
97
+ const policy = { kind: "gate-policy", lexicon: "cedar", name: "ship", version: gatePolicyVersion(text), text };
98
+ const gates = decl({ gates: { ship: { policy, mode: "enforce", context: { risk: "high", paths: ["a", "b"], reviewed: true, n: 2 } } } }).identity!.gates;
99
+ expect(gates.ship).toMatchObject({ policy, mode: "enforce", context: { risk: "high", paths: ["a", "b"], reviewed: true, n: 2 } });
100
+ // Each field stands alone: enforce with no base policy leaves the Op's policy to enforce.
101
+ expect(decl({ gates: { ship: { mode: "enforce" } } }).identity!.gates.ship!.policy).toBeNull();
102
+ expect(() => decl({ gates: { ship: { mode: "block" } } })).toThrow();
103
+ expect(() => decl({ gates: { ship: { policy: { ...policy, version: "sha256:other" } } } })).toThrow(/not the digest of its text/);
104
+ expect(() => decl({ gates: { ship: { policy: { kind: "gate-policy" } } } })).toThrow();
105
+ expect(() => decl({ gates: { ship: { context: { risk: { nested: 1 } } } } })).toThrow();
106
+ expect(() => decl({ gates: { ship: { context: { plan: "x" } } } })).toThrow(/identity\.gates\.ship\.context\.plan is reserved/);
107
+ });
108
+
82
109
  test("identified refuses a bare name and takes a forge identity or a signer", () => {
83
110
  const source = { declaration: decl({ attribution: "identified" }), policy: policyWith([{ principal: "dana@example.test", key: "ssh-ed25519 AAAA" }]) };
84
111
  expect(() => refuseUnidentified(source, ["github:alice", "dana@example.test"], "--by")).not.toThrow();