@intentius/chant 0.124.45 → 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 (772) 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/capabilities/aws.paths +148 -0
  47. package/dist/capabilities/base-mappings.json +122 -0
  48. package/dist/capabilities/catalog.d.ts +89 -0
  49. package/dist/capabilities/catalog.d.ts.map +1 -0
  50. package/dist/capabilities/catalog.js +204 -0
  51. package/dist/capabilities/language.d.ts +103 -0
  52. package/dist/capabilities/language.d.ts.map +1 -0
  53. package/dist/capabilities/language.js +335 -0
  54. package/dist/capabilities/matcher.d.ts +65 -0
  55. package/dist/capabilities/matcher.d.ts.map +1 -0
  56. package/dist/capabilities/matcher.js +299 -0
  57. package/dist/capabilities/view.d.ts +207 -0
  58. package/dist/capabilities/view.d.ts.map +1 -0
  59. package/dist/capabilities/view.js +484 -0
  60. package/dist/cli/commands/carve.d.ts +3 -0
  61. package/dist/cli/commands/carve.d.ts.map +1 -1
  62. package/dist/cli/commands/carve.js +36 -7
  63. package/dist/cli/commands/import-report.d.ts +26 -0
  64. package/dist/cli/commands/import-report.d.ts.map +1 -0
  65. package/dist/cli/commands/import-report.js +65 -0
  66. package/dist/cli/commands/import.d.ts +11 -1
  67. package/dist/cli/commands/import.d.ts.map +1 -1
  68. package/dist/cli/commands/import.js +22 -3
  69. package/dist/cli/commands/lint.d.ts.map +1 -1
  70. package/dist/cli/commands/lint.js +1 -0
  71. package/dist/cli/commands/migrate.d.ts +13 -1
  72. package/dist/cli/commands/migrate.d.ts.map +1 -1
  73. package/dist/cli/commands/migrate.js +134 -10
  74. package/dist/cli/handlers/approve.d.ts.map +1 -1
  75. package/dist/cli/handlers/approve.js +21 -2
  76. package/dist/cli/handlers/build.d.ts.map +1 -1
  77. package/dist/cli/handlers/build.js +6 -1
  78. package/dist/cli/handlers/declared-graph.d.ts +44 -0
  79. package/dist/cli/handlers/declared-graph.d.ts.map +1 -0
  80. package/dist/cli/handlers/declared-graph.js +87 -0
  81. package/dist/cli/handlers/graph.d.ts +25 -0
  82. package/dist/cli/handlers/graph.d.ts.map +1 -1
  83. package/dist/cli/handlers/graph.js +93 -87
  84. package/dist/cli/handlers/ledger.d.ts +3 -0
  85. package/dist/cli/handlers/ledger.d.ts.map +1 -0
  86. package/dist/cli/handlers/ledger.js +55 -0
  87. package/dist/cli/handlers/migrate.d.ts.map +1 -1
  88. package/dist/cli/handlers/migrate.js +18 -11
  89. package/dist/cli/handlers/misc.d.ts.map +1 -1
  90. package/dist/cli/handlers/misc.js +12 -1
  91. package/dist/cli/handlers/pr-env.d.ts.map +1 -1
  92. package/dist/cli/handlers/pr-env.js +4 -3
  93. package/dist/cli/handlers/pr.d.ts +15 -1
  94. package/dist/cli/handlers/pr.d.ts.map +1 -1
  95. package/dist/cli/handlers/pr.js +143 -39
  96. package/dist/cli/handlers/query.d.ts +7 -2
  97. package/dist/cli/handlers/query.d.ts.map +1 -1
  98. package/dist/cli/handlers/query.js +16 -4
  99. package/dist/cli/handlers/release.d.ts.map +1 -1
  100. package/dist/cli/handlers/release.js +6 -3
  101. package/dist/cli/handlers/search-count.d.ts +89 -0
  102. package/dist/cli/handlers/search-count.d.ts.map +1 -0
  103. package/dist/cli/handlers/search-count.js +132 -0
  104. package/dist/cli/handlers/search.d.ts +356 -6
  105. package/dist/cli/handlers/search.d.ts.map +1 -1
  106. package/dist/cli/handlers/search.js +1039 -155
  107. package/dist/cli/main.d.ts.map +1 -1
  108. package/dist/cli/main.js +88 -17
  109. package/dist/cli/mcp/server.d.ts.map +1 -1
  110. package/dist/cli/mcp/server.js +3 -0
  111. package/dist/cli/mcp/tools/capabilities.d.ts +7 -0
  112. package/dist/cli/mcp/tools/capabilities.d.ts.map +1 -0
  113. package/dist/cli/mcp/tools/capabilities.js +27 -0
  114. package/dist/cli/mcp/workspace-tools.d.ts +3 -1
  115. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  116. package/dist/cli/mcp/workspace-tools.js +47 -2
  117. package/dist/cli/parse-args.d.ts.map +1 -1
  118. package/dist/cli/parse-args.js +100 -19
  119. package/dist/cli/registry.d.ts +36 -4
  120. package/dist/cli/registry.d.ts.map +1 -1
  121. package/dist/components/builders.d.ts +5 -1
  122. package/dist/components/builders.d.ts.map +1 -1
  123. package/dist/components/builders.js +5 -0
  124. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  125. package/dist/components/capability-plugin-loader.js +16 -1
  126. package/dist/components/capability-plugin.d.ts +14 -0
  127. package/dist/components/capability-plugin.d.ts.map +1 -1
  128. package/dist/components/pr-env-ports.d.ts +29 -0
  129. package/dist/components/pr-env-ports.d.ts.map +1 -0
  130. package/dist/components/pr-env-ports.js +77 -0
  131. package/dist/components/pr-env-run.d.ts +5 -21
  132. package/dist/components/pr-env-run.d.ts.map +1 -1
  133. package/dist/components/pr-env-run.js +6 -70
  134. package/dist/components/pr-environment.d.ts +2 -1
  135. package/dist/components/pr-environment.d.ts.map +1 -1
  136. package/dist/components/pr-environment.js +1 -1
  137. package/dist/components/pr-hooks.d.ts +21 -1
  138. package/dist/components/pr-hooks.d.ts.map +1 -1
  139. package/dist/components/pr-hooks.js +72 -4
  140. package/dist/components/pr-pipeline.d.ts +45 -10
  141. package/dist/components/pr-pipeline.d.ts.map +1 -1
  142. package/dist/components/pr-pipeline.js +47 -14
  143. package/dist/components/pr-policy.d.ts +36 -0
  144. package/dist/components/pr-policy.d.ts.map +1 -1
  145. package/dist/components/pr-policy.js +36 -0
  146. package/dist/components/starter-plugin.d.ts +1 -0
  147. package/dist/components/starter-plugin.d.ts.map +1 -1
  148. package/dist/components/starter-plugin.js +5 -1
  149. package/dist/components/verbs/index.d.ts +2 -0
  150. package/dist/components/verbs/index.d.ts.map +1 -1
  151. package/dist/components/verbs/index.js +2 -0
  152. package/dist/components/verbs/publish-artifact.d.ts +65 -0
  153. package/dist/components/verbs/publish-artifact.d.ts.map +1 -0
  154. package/dist/components/verbs/publish-artifact.js +74 -0
  155. package/dist/components/verbs/publish-image.d.ts +129 -0
  156. package/dist/components/verbs/publish-image.d.ts.map +1 -0
  157. package/dist/components/verbs/publish-image.js +145 -0
  158. package/dist/config-environments.d.ts +112 -0
  159. package/dist/config-environments.d.ts.map +1 -0
  160. package/dist/config-environments.js +114 -0
  161. package/dist/config.d.ts +32 -84
  162. package/dist/config.d.ts.map +1 -1
  163. package/dist/config.js +13 -96
  164. package/dist/gate-decision.d.ts +11 -2
  165. package/dist/gate-decision.d.ts.map +1 -1
  166. package/dist/gate-decision.js +15 -3
  167. package/dist/graph-detail.d.ts.map +1 -1
  168. package/dist/graph-detail.js +15 -3
  169. package/dist/graph-effective.d.ts +33 -2
  170. package/dist/graph-effective.d.ts.map +1 -1
  171. package/dist/graph-effective.js +215 -68
  172. package/dist/graph-ir.d.ts +48 -0
  173. package/dist/graph-ir.d.ts.map +1 -1
  174. package/dist/graph-ir.js +130 -0
  175. package/dist/graph-sub-template.d.ts +11 -0
  176. package/dist/graph-sub-template.d.ts.map +1 -0
  177. package/dist/graph-sub-template.js +19 -0
  178. package/dist/graph-walk.d.ts +66 -0
  179. package/dist/graph-walk.d.ts.map +1 -0
  180. package/dist/graph-walk.js +70 -0
  181. package/dist/import/parser.d.ts +19 -0
  182. package/dist/import/parser.d.ts.map +1 -1
  183. package/dist/lexicon.d.ts +80 -6
  184. package/dist/lexicon.d.ts.map +1 -1
  185. package/dist/lifecycle/gate-ledger.d.ts +11 -0
  186. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  187. package/dist/lifecycle/git.d.ts +2 -5
  188. package/dist/lifecycle/git.d.ts.map +1 -1
  189. package/dist/lifecycle/ledger-read.d.ts +65 -0
  190. package/dist/lifecycle/ledger-read.d.ts.map +1 -0
  191. package/dist/lifecycle/ledger-read.js +160 -0
  192. package/dist/lifecycle/observe.d.ts +28 -0
  193. package/dist/lifecycle/observe.d.ts.map +1 -1
  194. package/dist/lifecycle/observe.js +23 -1
  195. package/dist/lifecycle/release-workstation.d.ts +7 -0
  196. package/dist/lifecycle/release-workstation.d.ts.map +1 -1
  197. package/dist/lifecycle/release-workstation.js +25 -0
  198. package/dist/lifecycle/run-ledger.d.ts +4 -3
  199. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  200. package/dist/lifecycle/run-ledger.js +1 -0
  201. package/dist/lint/discover.js +3 -1
  202. package/dist/lint/post-synth.d.ts +127 -1
  203. package/dist/lint/post-synth.d.ts.map +1 -1
  204. package/dist/lint/post-synth.js +84 -2
  205. package/dist/lint/rule.d.ts +6 -0
  206. package/dist/lint/rule.d.ts.map +1 -1
  207. package/dist/lint/rules/op/index.d.ts +1 -0
  208. package/dist/lint/rules/op/index.d.ts.map +1 -1
  209. package/dist/lint/rules/op/index.js +2 -0
  210. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts +32 -0
  211. package/dist/lint/rules/op/ops019-workspace-op-target.d.ts.map +1 -0
  212. package/dist/lint/rules/op/ops019-workspace-op-target.js +63 -0
  213. package/dist/live-endpoint.d.ts +1 -1
  214. package/dist/live-endpoint.d.ts.map +1 -1
  215. package/dist/live-endpoint.js +1 -1
  216. package/dist/observation.d.ts +48 -1
  217. package/dist/observation.d.ts.map +1 -1
  218. package/dist/observation.js +21 -5
  219. package/dist/op/activities/activity-contracts.d.ts +9 -6
  220. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  221. package/dist/op/activities/activity-contracts.js +5 -2
  222. package/dist/op/activities/index.d.ts +2 -2
  223. package/dist/op/activities/index.d.ts.map +1 -1
  224. package/dist/op/activities/index.js +1 -1
  225. package/dist/op/activities/review-agent.d.ts +90 -4
  226. package/dist/op/activities/review-agent.d.ts.map +1 -1
  227. package/dist/op/activities/review-agent.js +217 -19
  228. package/dist/op/activities/work-evidence.d.ts +8 -1
  229. package/dist/op/activities/work-evidence.d.ts.map +1 -1
  230. package/dist/op/activities/work-evidence.js +1 -0
  231. package/dist/op/builders.d.ts +9 -5
  232. package/dist/op/builders.d.ts.map +1 -1
  233. package/dist/op/builders.js +9 -5
  234. package/dist/op/composites/apply-op.d.ts +29 -2
  235. package/dist/op/composites/apply-op.d.ts.map +1 -1
  236. package/dist/op/composites/apply-op.js +20 -1
  237. package/dist/op/gate-approval.d.ts +11 -5
  238. package/dist/op/gate-approval.d.ts.map +1 -1
  239. package/dist/op/gate-approval.js +11 -6
  240. package/dist/op/gate-quorum.d.ts +100 -3
  241. package/dist/op/gate-quorum.d.ts.map +1 -1
  242. package/dist/op/gate-quorum.js +108 -6
  243. package/dist/op/gate-review.d.ts +9 -0
  244. package/dist/op/gate-review.d.ts.map +1 -1
  245. package/dist/op/gate-review.js +13 -0
  246. package/dist/op/gate.d.ts +44 -3
  247. package/dist/op/gate.d.ts.map +1 -1
  248. package/dist/op/gate.js +25 -4
  249. package/dist/op/index.d.ts +3 -3
  250. package/dist/op/index.d.ts.map +1 -1
  251. package/dist/op/index.js +1 -1
  252. package/dist/op/local-executor.d.ts +38 -3
  253. package/dist/op/local-executor.d.ts.map +1 -1
  254. package/dist/op/local-executor.js +48 -6
  255. package/dist/op/op-waves-run.d.ts +26 -1
  256. package/dist/op/op-waves-run.d.ts.map +1 -1
  257. package/dist/op/op-waves-run.js +259 -13
  258. package/dist/op/op-waves.d.ts +72 -5
  259. package/dist/op/op-waves.d.ts.map +1 -1
  260. package/dist/op/op-waves.js +110 -15
  261. package/dist/op/runtime.d.ts +11 -0
  262. package/dist/op/runtime.d.ts.map +1 -1
  263. package/dist/op/schedule-overdue.d.ts +16 -0
  264. package/dist/op/schedule-overdue.d.ts.map +1 -1
  265. package/dist/op/schedule-overdue.js +36 -0
  266. package/dist/op/stage-hooks.d.ts +9 -2
  267. package/dist/op/stage-hooks.d.ts.map +1 -1
  268. package/dist/op/stage-hooks.js +7 -5
  269. package/dist/op/workspace-run-env.d.ts +22 -0
  270. package/dist/op/workspace-run-env.d.ts.map +1 -0
  271. package/dist/op/workspace-run-env.js +21 -0
  272. package/dist/pr-forge.d.ts +24 -0
  273. package/dist/pr-forge.d.ts.map +1 -1
  274. package/dist/pr-forge.js +47 -0
  275. package/dist/pr-post.d.ts +42 -0
  276. package/dist/pr-post.d.ts.map +1 -1
  277. package/dist/pr-post.js +67 -1
  278. package/dist/read-filter.d.ts +65 -0
  279. package/dist/read-filter.d.ts.map +1 -0
  280. package/dist/read-filter.js +179 -0
  281. package/dist/run-event.d.ts +1 -1
  282. package/dist/run-event.d.ts.map +1 -1
  283. package/dist/terraform/carve.d.ts +24 -3
  284. package/dist/terraform/carve.d.ts.map +1 -1
  285. package/dist/terraform/carve.js +32 -14
  286. package/dist/terraform/cuts.d.ts +97 -0
  287. package/dist/terraform/cuts.d.ts.map +1 -0
  288. package/dist/terraform/cuts.js +138 -0
  289. package/dist/terraform/folds.d.ts +26 -0
  290. package/dist/terraform/folds.d.ts.map +1 -0
  291. package/dist/terraform/folds.js +38 -0
  292. package/dist/terraform/graph.d.ts.map +1 -1
  293. package/dist/terraform/graph.js +10 -0
  294. package/dist/terraform/reach.d.ts +140 -0
  295. package/dist/terraform/reach.d.ts.map +1 -0
  296. package/dist/terraform/reach.js +170 -0
  297. package/dist/terraform/score.d.ts +14 -0
  298. package/dist/terraform/score.d.ts.map +1 -1
  299. package/dist/terraform/score.js +14 -31
  300. package/dist/terraform/state.d.ts.map +1 -1
  301. package/dist/terraform/state.js +4 -2
  302. package/dist/terraform/types.d.ts +8 -0
  303. package/dist/terraform/types.d.ts.map +1 -1
  304. package/dist/wave-blocked.d.ts +10 -1
  305. package/dist/wave-blocked.d.ts.map +1 -1
  306. package/dist/wave-blocked.js +31 -3
  307. package/dist/wave-runtime.mjs +266 -256
  308. package/dist/workspace/capabilities-contract.d.ts +66 -0
  309. package/dist/workspace/capabilities-contract.d.ts.map +1 -0
  310. package/dist/workspace/capabilities-contract.js +107 -0
  311. package/dist/workspace/capabilities.schema.json +229 -0
  312. package/dist/workspace/check.schema.json +11 -2
  313. package/dist/workspace/checks/ci-branch.d.ts +44 -0
  314. package/dist/workspace/checks/ci-branch.d.ts.map +1 -0
  315. package/dist/workspace/checks/ci-branch.js +133 -0
  316. package/dist/workspace/checks/links.d.ts +3 -0
  317. package/dist/workspace/checks/links.d.ts.map +1 -1
  318. package/dist/workspace/checks/links.js +12 -1
  319. package/dist/workspace/checks/org.d.ts +48 -0
  320. package/dist/workspace/checks/org.d.ts.map +1 -0
  321. package/dist/workspace/checks/org.js +156 -0
  322. package/dist/workspace/checks.d.ts +11 -1
  323. package/dist/workspace/checks.d.ts.map +1 -1
  324. package/dist/workspace/checks.js +59 -7
  325. package/dist/workspace/ci-branches.d.ts +16 -0
  326. package/dist/workspace/ci-branches.d.ts.map +1 -1
  327. package/dist/workspace/ci-branches.js +27 -0
  328. package/dist/workspace/ci-green-cli.js +7 -7
  329. package/dist/workspace/ci-green-workflow.d.ts +1 -1
  330. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  331. package/dist/workspace/ci-green-workflow.js +13 -8
  332. package/dist/workspace/ci-green.d.ts +14 -0
  333. package/dist/workspace/ci-green.d.ts.map +1 -1
  334. package/dist/workspace/ci-green.js +44 -11
  335. package/dist/workspace/ci-run.d.ts +9 -1
  336. package/dist/workspace/ci-run.d.ts.map +1 -1
  337. package/dist/workspace/ci-run.js +67 -6
  338. package/dist/workspace/ci-serve.d.ts +9 -0
  339. package/dist/workspace/ci-serve.d.ts.map +1 -1
  340. package/dist/workspace/ci-serve.js +114 -76
  341. package/dist/workspace/compose-graph.d.ts +23 -0
  342. package/dist/workspace/compose-graph.d.ts.map +1 -1
  343. package/dist/workspace/compose-graph.js +3 -1
  344. package/dist/workspace/composites.d.ts +4 -1
  345. package/dist/workspace/composites.d.ts.map +1 -1
  346. package/dist/workspace/composites.js +5 -2
  347. package/dist/workspace/composites.schema.json +22 -0
  348. package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  349. package/dist/workspace/conformance/index.d.ts +1 -1
  350. package/dist/workspace/conformance/index.d.ts.map +1 -1
  351. package/dist/workspace/conformance/index.js +39 -2
  352. package/dist/workspace/cross-root-lint.d.ts +56 -0
  353. package/dist/workspace/cross-root-lint.d.ts.map +1 -0
  354. package/dist/workspace/cross-root-lint.js +111 -0
  355. package/dist/workspace/dbt-models-reader.d.ts +44 -0
  356. package/dist/workspace/dbt-models-reader.d.ts.map +1 -0
  357. package/dist/workspace/dbt-models-reader.js +356 -0
  358. package/dist/workspace/dbt-project-common.d.ts +40 -0
  359. package/dist/workspace/dbt-project-common.d.ts.map +1 -0
  360. package/dist/workspace/dbt-project-common.js +106 -0
  361. package/dist/workspace/dbt-project-reader.d.ts +4 -3
  362. package/dist/workspace/dbt-project-reader.d.ts.map +1 -1
  363. package/dist/workspace/dbt-project-reader.js +14 -108
  364. package/dist/workspace/decision-rank.d.ts +26 -0
  365. package/dist/workspace/decision-rank.d.ts.map +1 -0
  366. package/dist/workspace/decision-rank.js +16 -0
  367. package/dist/workspace/declaration.d.ts +43 -2
  368. package/dist/workspace/declaration.d.ts.map +1 -1
  369. package/dist/workspace/declaration.js +45 -11
  370. package/dist/workspace/declaration.schema.json +133 -24
  371. package/dist/workspace/environments.d.ts +26 -2
  372. package/dist/workspace/environments.d.ts.map +1 -1
  373. package/dist/workspace/environments.js +26 -2
  374. package/dist/workspace/flyway-dir.d.ts +39 -0
  375. package/dist/workspace/flyway-dir.d.ts.map +1 -0
  376. package/dist/workspace/flyway-dir.js +104 -0
  377. package/dist/workspace/flyway-layout.d.ts +96 -0
  378. package/dist/workspace/flyway-layout.d.ts.map +1 -0
  379. package/dist/workspace/flyway-layout.js +308 -0
  380. package/dist/workspace/graph-cache.d.ts +8 -0
  381. package/dist/workspace/graph-cache.d.ts.map +1 -1
  382. package/dist/workspace/graph-cache.js +27 -3
  383. package/dist/workspace/graph-cli.d.ts +39 -2
  384. package/dist/workspace/graph-cli.d.ts.map +1 -1
  385. package/dist/workspace/graph-cli.js +92 -32
  386. package/dist/workspace/graph.schema.json +182 -0
  387. package/dist/workspace/intent.d.ts +2 -3
  388. package/dist/workspace/intent.d.ts.map +1 -1
  389. package/dist/workspace/intent.js +5 -9
  390. package/dist/workspace/kind-link-readers.d.ts +59 -2
  391. package/dist/workspace/kind-link-readers.d.ts.map +1 -1
  392. package/dist/workspace/kind-link-readers.js +17 -1
  393. package/dist/workspace/kind-readers.d.ts +3 -0
  394. package/dist/workspace/kind-readers.d.ts.map +1 -1
  395. package/dist/workspace/kind-readers.js +7 -1
  396. package/dist/workspace/kinds.d.ts +66 -2
  397. package/dist/workspace/kinds.d.ts.map +1 -1
  398. package/dist/workspace/kinds.js +98 -4
  399. package/dist/workspace/ledger.schema.json +158 -0
  400. package/dist/workspace/lexicon-outputs.d.ts +20 -1
  401. package/dist/workspace/lexicon-outputs.d.ts.map +1 -1
  402. package/dist/workspace/lexicon-outputs.js +102 -10
  403. package/dist/workspace/lineage-check.d.ts +3 -0
  404. package/dist/workspace/lineage-check.d.ts.map +1 -1
  405. package/dist/workspace/lineage-check.js +23 -4
  406. package/dist/workspace/links.d.ts +8 -1
  407. package/dist/workspace/links.d.ts.map +1 -1
  408. package/dist/workspace/links.js +25 -1
  409. package/dist/workspace/ls.d.ts +8 -0
  410. package/dist/workspace/ls.d.ts.map +1 -1
  411. package/dist/workspace/ls.js +7 -1
  412. package/dist/workspace/ls.schema.json +7 -0
  413. package/dist/workspace/member-commands.d.ts +10 -1
  414. package/dist/workspace/member-commands.d.ts.map +1 -1
  415. package/dist/workspace/member-commands.js +71 -6
  416. package/dist/workspace/migrate-links.d.ts +45 -0
  417. package/dist/workspace/migrate-links.d.ts.map +1 -0
  418. package/dist/workspace/migrate-links.js +100 -0
  419. package/dist/workspace/migration-dirs.d.ts +74 -0
  420. package/dist/workspace/migration-dirs.d.ts.map +1 -0
  421. package/dist/workspace/migration-dirs.js +254 -0
  422. package/dist/workspace/nested-graph.d.ts +2 -0
  423. package/dist/workspace/nested-graph.d.ts.map +1 -1
  424. package/dist/workspace/nested-graph.js +32 -1
  425. package/dist/workspace/reason-codes.d.ts +18 -0
  426. package/dist/workspace/reason-codes.d.ts.map +1 -1
  427. package/dist/workspace/reason-codes.js +23 -0
  428. package/dist/workspace/records-cli.d.ts +11 -0
  429. package/dist/workspace/records-cli.d.ts.map +1 -1
  430. package/dist/workspace/records-cli.js +57 -5
  431. package/dist/workspace/records.schema.json +5 -0
  432. package/dist/workspace/release-candidates.d.ts +2 -0
  433. package/dist/workspace/release-candidates.d.ts.map +1 -1
  434. package/dist/workspace/release-candidates.js +5 -2
  435. package/dist/workspace/release-train.d.ts +53 -0
  436. package/dist/workspace/release-train.d.ts.map +1 -0
  437. package/dist/workspace/release-train.js +134 -0
  438. package/dist/workspace/release-workflow.d.ts.map +1 -1
  439. package/dist/workspace/release-workflow.js +4 -3
  440. package/dist/workspace/repo-ref.d.ts +45 -0
  441. package/dist/workspace/repo-ref.d.ts.map +1 -0
  442. package/dist/workspace/repo-ref.js +114 -0
  443. package/dist/workspace/run-event.schema.json +2 -2
  444. package/dist/workspace/runs-cli.d.ts +9 -0
  445. package/dist/workspace/runs-cli.d.ts.map +1 -1
  446. package/dist/workspace/runs-cli.js +48 -3
  447. package/dist/workspace/runs.schema.json +7 -0
  448. package/dist/workspace/search-cli.d.ts +204 -0
  449. package/dist/workspace/search-cli.d.ts.map +1 -0
  450. package/dist/workspace/search-cli.js +466 -0
  451. package/dist/workspace/search-contract.d.ts +56 -0
  452. package/dist/workspace/search-contract.d.ts.map +1 -0
  453. package/dist/workspace/search-contract.js +70 -0
  454. package/dist/workspace/search.schema.json +822 -0
  455. package/dist/workspace/status-decisions.d.ts +37 -0
  456. package/dist/workspace/status-decisions.d.ts.map +1 -0
  457. package/dist/workspace/status-decisions.js +147 -0
  458. package/dist/workspace/status-gates.d.ts +9 -0
  459. package/dist/workspace/status-gates.d.ts.map +1 -1
  460. package/dist/workspace/status.d.ts +4 -0
  461. package/dist/workspace/status.d.ts.map +1 -1
  462. package/dist/workspace/status.js +14 -2
  463. package/dist/workspace/status.schema.json +6 -0
  464. package/dist/workspace/work-cli.d.ts.map +1 -1
  465. package/dist/workspace/work-cli.js +5 -3
  466. package/dist/workspace/work-evidence-ops.d.ts +83 -0
  467. package/dist/workspace/work-evidence-ops.d.ts.map +1 -0
  468. package/dist/workspace/work-evidence-ops.js +169 -0
  469. package/dist/workspace/work-evidence.d.ts +12 -2
  470. package/dist/workspace/work-evidence.d.ts.map +1 -1
  471. package/dist/workspace/work-evidence.js +21 -3
  472. package/dist/workspace/work-evidence.schema.json +49 -2
  473. package/dist/workspace/workspace-gates.d.ts +110 -0
  474. package/dist/workspace/workspace-gates.d.ts.map +1 -0
  475. package/dist/workspace/workspace-gates.js +140 -0
  476. package/dist/workspace/workspace-kinds.schema.json +58 -3
  477. package/dist/workspace/workspace-ledger.d.ts +76 -0
  478. package/dist/workspace/workspace-ledger.d.ts.map +1 -0
  479. package/dist/workspace/workspace-ledger.js +91 -0
  480. package/dist/workspace/workspace-run.d.ts +112 -0
  481. package/dist/workspace/workspace-run.d.ts.map +1 -0
  482. package/dist/workspace/workspace-run.js +432 -0
  483. package/package.json +1 -1
  484. package/src/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
  485. package/src/__golden__/wave-loop/op-by-apply-hold.json +10 -0
  486. package/src/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
  487. package/src/__golden__/wave-loop/op-by-apply-pass.json +10 -0
  488. package/src/__golden__/wave-loop/op-by-apply-skip.json +10 -0
  489. package/src/__golden__/wave-loop/op-cost-member-clash.json +6 -0
  490. package/src/__golden__/wave-loop/op-cost-member.json +13 -0
  491. package/src/__golden__/wave-loop/op-denied-source.json +16 -0
  492. package/src/__golden__/wave-loop/op-denied.json +16 -0
  493. package/src/__golden__/wave-loop/op-discover-rest-short.json +6 -0
  494. package/src/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
  495. package/src/__golden__/wave-loop/op-discover-rest.json +26 -0
  496. package/src/__golden__/wave-loop/op-discover-wide.json +26 -0
  497. package/src/__golden__/wave-loop/op-held-on-again.json +16 -0
  498. package/src/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
  499. package/src/__golden__/wave-loop/op-held-on.json +16 -0
  500. package/src/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
  501. package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
  502. package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
  503. package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
  504. package/src/__golden__/wave-loop/op-hooks-held.json +13 -0
  505. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
  506. package/src/__golden__/wave-loop/op-hooks-pass.json +13 -0
  507. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
  508. package/src/__golden__/wave-loop/op-hooks-share.json +33 -0
  509. package/src/__golden__/wave-loop/op-outcome-held.json +10 -0
  510. package/src/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
  511. package/src/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
  512. package/src/__golden__/wave-loop/op-outcome-replan.json +10 -0
  513. package/src/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
  514. package/src/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
  515. package/src/__golden__/wave-loop/op-overridden.json +16 -0
  516. package/src/__golden__/wave-loop/op-per-run.json +20 -0
  517. package/src/__golden__/wave-loop/op-point.json +13 -0
  518. package/src/__golden__/wave-loop/op-pr-review.json +10 -0
  519. package/src/__golden__/wave-loop/op-sealed.json +10 -0
  520. package/src/__golden__/wave-loop/op-spend-change-only.json +10 -0
  521. package/src/__golden__/wave-loop/op-spend.json +10 -0
  522. package/src/__golden__/wave-loop/op-wave-commands.json +55 -0
  523. package/src/__golden__/wave-loop/op-wide-refused.json +26 -0
  524. package/src/__golden__/wave-loop/op-wide-waiting.json +29 -0
  525. package/src/__golden__/wave-loop/op-wide.json +39 -0
  526. package/src/__snapshots__/okf.test.ts.snap +10 -0
  527. package/src/action-pins.test.ts +78 -0
  528. package/src/action-pins.ts +92 -0
  529. package/src/capabilities/aws.paths +148 -0
  530. package/src/capabilities/base-mappings.json +122 -0
  531. package/src/capabilities/catalog.ts +230 -0
  532. package/src/capabilities/language.test.ts +191 -0
  533. package/src/capabilities/language.ts +373 -0
  534. package/src/capabilities/matcher.ts +315 -0
  535. package/src/capabilities/view-compose.test.ts +76 -0
  536. package/src/capabilities/view.ts +631 -0
  537. package/src/cli/commands/carve-score-once.test.ts +99 -0
  538. package/src/cli/commands/carve.ts +37 -6
  539. package/src/cli/commands/check-lexicon.test.ts +2 -2
  540. package/src/cli/commands/import-report.ts +71 -0
  541. package/src/cli/commands/import.ts +33 -3
  542. package/src/cli/commands/lint.ts +1 -0
  543. package/src/cli/commands/migrate-dialect.test.ts +145 -0
  544. package/src/cli/commands/migrate.ts +132 -11
  545. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +4 -4
  546. package/src/cli/handlers/approve.ts +23 -2
  547. package/src/cli/handlers/build.ts +7 -1
  548. package/src/cli/handlers/declared-graph.ts +99 -0
  549. package/src/cli/handlers/graph.test.ts +94 -3
  550. package/src/cli/handlers/graph.ts +123 -96
  551. package/src/cli/handlers/ledger.ts +54 -0
  552. package/src/cli/handlers/migrate.ts +21 -12
  553. package/src/cli/handlers/misc.ts +10 -1
  554. package/src/cli/handlers/pr-env.ts +3 -4
  555. package/src/cli/handlers/pr.ts +138 -36
  556. package/src/cli/handlers/query.ts +16 -4
  557. package/src/cli/handlers/release.ts +6 -3
  558. package/src/cli/handlers/search-build-roots.test.ts +144 -0
  559. package/src/cli/handlers/search-count.test.ts +191 -0
  560. package/src/cli/handlers/search-count.ts +165 -0
  561. package/src/cli/handlers/search-json.test.ts +333 -0
  562. package/src/cli/handlers/search-live.test.ts +387 -0
  563. package/src/cli/handlers/search-paths.test.ts +235 -0
  564. package/src/cli/handlers/search.ts +1291 -166
  565. package/src/cli/main.test.ts +43 -3
  566. package/src/cli/main.ts +88 -17
  567. package/src/cli/mcp/server.test.ts +3 -3
  568. package/src/cli/mcp/server.ts +3 -0
  569. package/src/cli/mcp/tools/capabilities.ts +32 -0
  570. package/src/cli/mcp/workspace-tools.e2e.test.ts +21 -1
  571. package/src/cli/mcp/workspace-tools.ts +47 -2
  572. package/src/cli/parse-args.ts +79 -15
  573. package/src/cli/registry.ts +36 -4
  574. package/src/components/builders.ts +8 -0
  575. package/src/components/capability-plugin-loader.ts +13 -1
  576. package/src/components/capability-plugin.ts +16 -0
  577. package/src/components/pr-env-ports.ts +89 -0
  578. package/src/components/pr-env-run-bundle.test.ts +54 -0
  579. package/src/components/pr-env-run.ts +5 -76
  580. package/src/components/pr-environment.ts +2 -1
  581. package/src/components/pr-hooks.test.ts +73 -5
  582. package/src/components/pr-hooks.ts +95 -5
  583. package/src/components/pr-pipeline.ts +64 -16
  584. package/src/components/pr-policy.test.ts +64 -1
  585. package/src/components/pr-policy.ts +53 -0
  586. package/src/components/registry.test.ts +1 -1
  587. package/src/components/starter-plugin.ts +6 -0
  588. package/src/components/verbs/index.ts +2 -0
  589. package/src/components/verbs/publish-artifact.test.ts +60 -0
  590. package/src/components/verbs/publish-artifact.ts +122 -0
  591. package/src/components/verbs/publish-image.test.ts +101 -0
  592. package/src/components/verbs/publish-image.ts +265 -0
  593. package/src/config-environments.ts +160 -0
  594. package/src/config.ts +44 -124
  595. package/src/gate-decision-bundle.test.ts +1 -0
  596. package/src/gate-decision-identity.test.ts +9 -0
  597. package/src/gate-decision.ts +26 -3
  598. package/src/graph-detail.ts +15 -3
  599. package/src/graph-effective.ts +226 -75
  600. package/src/graph-ir.test.ts +163 -0
  601. package/src/graph-ir.ts +174 -0
  602. package/src/graph-sub-template.ts +22 -0
  603. package/src/graph-walk.test.ts +44 -0
  604. package/src/graph-walk.ts +102 -0
  605. package/src/import/parser.ts +20 -0
  606. package/src/lexicon.ts +84 -6
  607. package/src/lifecycle/gate-ledger.ts +8 -0
  608. package/src/lifecycle/git.ts +2 -2
  609. package/src/lifecycle/ledger-read.test.ts +107 -0
  610. package/src/lifecycle/ledger-read.ts +195 -0
  611. package/src/lifecycle/observe.test.ts +27 -1
  612. package/src/lifecycle/observe.ts +43 -1
  613. package/src/lifecycle/release-workstation.ts +24 -0
  614. package/src/lifecycle/run-ledger.ts +5 -2
  615. package/src/lint/cross-root.test.ts +122 -0
  616. package/src/lint/discover.ts +3 -1
  617. package/src/lint/post-synth.ts +212 -2
  618. package/src/lint/rule.ts +6 -0
  619. package/src/lint/rules/op/index.ts +2 -0
  620. package/src/lint/rules/op/ops019-workspace-op-target.ts +89 -0
  621. package/src/live-endpoint.ts +1 -1
  622. package/src/observation.test.ts +25 -0
  623. package/src/observation.ts +69 -5
  624. package/src/op/activities/activity-contracts.ts +5 -2
  625. package/src/op/activities/index.ts +2 -2
  626. package/src/op/activities/review-agent.test.ts +221 -4
  627. package/src/op/activities/review-agent.ts +265 -22
  628. package/src/op/activities/work-evidence.ts +9 -1
  629. package/src/op/builders.ts +9 -5
  630. package/src/op/composites/apply-op.test.ts +108 -0
  631. package/src/op/composites/apply-op.ts +37 -2
  632. package/src/op/gate-approval.test.ts +6 -1
  633. package/src/op/gate-approval.ts +12 -6
  634. package/src/op/gate-quorum-at-base.test.ts +211 -0
  635. package/src/op/gate-quorum.ts +156 -8
  636. package/src/op/gate-review.ts +14 -0
  637. package/src/op/gate.ts +67 -8
  638. package/src/op/index.ts +3 -2
  639. package/src/op/local-executor.ts +69 -9
  640. package/src/op/op-waves-depends-on.test.ts +81 -0
  641. package/src/op/op-waves-hook-targets.test.ts +4 -2
  642. package/src/op/op-waves-quorum-product.test.ts +158 -0
  643. package/src/op/op-waves-run.ts +288 -14
  644. package/src/op/op-waves.ts +143 -14
  645. package/src/op/runtime.ts +8 -0
  646. package/src/op/schedule-overdue.ts +33 -0
  647. package/src/op/stage-hooks.ts +13 -5
  648. package/src/op/workspace-run-env.ts +29 -0
  649. package/src/pr-forge-bundle.test.ts +2 -2
  650. package/src/pr-forge.test.ts +47 -0
  651. package/src/pr-forge.ts +68 -0
  652. package/src/pr-post-open.e2e.test.ts +36 -0
  653. package/src/pr-post.test.ts +102 -3
  654. package/src/pr-post.ts +89 -1
  655. package/src/read-filter.test.ts +54 -0
  656. package/src/read-filter.ts +193 -0
  657. package/src/run-event.ts +1 -1
  658. package/src/stage-hooks-bundle.test.ts +1 -0
  659. package/src/terraform/carve.ts +52 -15
  660. package/src/terraform/cuts.test.ts +173 -0
  661. package/src/terraform/cuts.ts +188 -0
  662. package/src/terraform/folds.ts +44 -0
  663. package/src/terraform/graph.ts +9 -0
  664. package/src/terraform/radius.test.ts +90 -0
  665. package/src/terraform/reach.test.ts +146 -0
  666. package/src/terraform/reach.ts +247 -0
  667. package/src/terraform/score.ts +27 -29
  668. package/src/terraform/state.ts +3 -1
  669. package/src/terraform/types.ts +8 -0
  670. package/src/wave-blocked.ts +28 -3
  671. package/src/wave-loop-golden.test.ts +4 -3
  672. package/src/workspace/behold-kinds.test.ts +1 -1
  673. package/src/workspace/capabilities-contract.test.ts +88 -0
  674. package/src/workspace/capabilities-contract.ts +132 -0
  675. package/src/workspace/capabilities.schema.json +229 -0
  676. package/src/workspace/check-contract.test.ts +7 -6
  677. package/src/workspace/check.schema.json +11 -2
  678. package/src/workspace/checks/ci-branch.test.ts +128 -0
  679. package/src/workspace/checks/ci-branch.ts +141 -0
  680. package/src/workspace/checks/links.ts +12 -1
  681. package/src/workspace/checks/org.ts +169 -0
  682. package/src/workspace/checks.ts +64 -7
  683. package/src/workspace/ci-branches.ts +31 -0
  684. package/src/workspace/ci-green-branches.test.ts +153 -0
  685. package/src/workspace/ci-green-cli.ts +7 -7
  686. package/src/workspace/ci-green-workflow.test.ts +4 -3
  687. package/src/workspace/ci-green-workflow.ts +13 -9
  688. package/src/workspace/ci-green.ts +49 -12
  689. package/src/workspace/ci-run.test.ts +72 -1
  690. package/src/workspace/ci-run.ts +68 -8
  691. package/src/workspace/ci-serve.test.ts +95 -0
  692. package/src/workspace/ci-serve.ts +106 -65
  693. package/src/workspace/component-environments.test.ts +25 -2
  694. package/src/workspace/compose-graph.ts +19 -3
  695. package/src/workspace/composites.schema.json +22 -0
  696. package/src/workspace/composites.test.ts +39 -8
  697. package/src/workspace/composites.ts +6 -2
  698. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
  699. package/src/workspace/conformance/conformance.e2e.test.ts +14 -1
  700. package/src/workspace/conformance/index.ts +31 -2
  701. package/src/workspace/cross-root-lint.test.ts +90 -0
  702. package/src/workspace/cross-root-lint.ts +126 -0
  703. package/src/workspace/dbt-kind.test.ts +3 -2
  704. package/src/workspace/dbt-models-reader.ts +362 -0
  705. package/src/workspace/dbt-models.test.ts +248 -0
  706. package/src/workspace/dbt-project-common.ts +113 -0
  707. package/src/workspace/dbt-project-reader.ts +29 -112
  708. package/src/workspace/decision-rank.ts +40 -0
  709. package/src/workspace/declaration.schema.json +133 -24
  710. package/src/workspace/declaration.ts +84 -15
  711. package/src/workspace/environments.ts +40 -4
  712. package/src/workspace/flyway-dir.ts +113 -0
  713. package/src/workspace/flyway-layout.ts +337 -0
  714. package/src/workspace/graph-cache.ts +27 -3
  715. package/src/workspace/graph-cli.ts +119 -32
  716. package/src/workspace/graph-contract.test.ts +4 -2
  717. package/src/workspace/graph.schema.json +182 -0
  718. package/src/workspace/identity.test.ts +14 -2
  719. package/src/workspace/intent.ts +6 -14
  720. package/src/workspace/kind-link-readers.ts +68 -3
  721. package/src/workspace/kind-readers.e2e.test.ts +28 -0
  722. package/src/workspace/kind-readers.test.ts +1 -1
  723. package/src/workspace/kind-readers.ts +7 -1
  724. package/src/workspace/kinds.test.ts +12 -7
  725. package/src/workspace/kinds.ts +132 -7
  726. package/src/workspace/ledger.schema.json +158 -0
  727. package/src/workspace/lexicon-outputs.ts +132 -13
  728. package/src/workspace/lineage-check.ts +30 -7
  729. package/src/workspace/links.ts +24 -1
  730. package/src/workspace/ls-contract.test.ts +1 -1
  731. package/src/workspace/ls.schema.json +7 -0
  732. package/src/workspace/ls.ts +10 -1
  733. package/src/workspace/member-commands.ts +82 -3
  734. package/src/workspace/member-file-schema.test.ts +124 -0
  735. package/src/workspace/migrate-links.test.ts +99 -0
  736. package/src/workspace/migrate-links.ts +108 -0
  737. package/src/workspace/migration-dirs.test.ts +272 -0
  738. package/src/workspace/migration-dirs.ts +267 -0
  739. package/src/workspace/nested-graph.ts +32 -1
  740. package/src/workspace/org-scope.test.ts +238 -0
  741. package/src/workspace/reason-codes.test.ts +13 -1
  742. package/src/workspace/reason-codes.ts +23 -0
  743. package/src/workspace/records-cli.ts +58 -5
  744. package/src/workspace/records.schema.json +5 -0
  745. package/src/workspace/release-candidates.ts +7 -2
  746. package/src/workspace/release-train.ts +157 -0
  747. package/src/workspace/release-workflow.ts +4 -3
  748. package/src/workspace/repo-ref.ts +113 -0
  749. package/src/workspace/run-event.schema.json +2 -2
  750. package/src/workspace/runs-cli.ts +48 -4
  751. package/src/workspace/runs.schema.json +7 -0
  752. package/src/workspace/search-cli.e2e.test.ts +305 -0
  753. package/src/workspace/search-cli.ts +591 -0
  754. package/src/workspace/search-contract.ts +95 -0
  755. package/src/workspace/search.schema.json +822 -0
  756. package/src/workspace/status-contract.test.ts +46 -1
  757. package/src/workspace/status-decisions.test.ts +100 -0
  758. package/src/workspace/status-decisions.ts +151 -0
  759. package/src/workspace/status-gates.ts +9 -0
  760. package/src/workspace/status.schema.json +6 -0
  761. package/src/workspace/status.ts +17 -3
  762. package/src/workspace/where-filters.test.ts +39 -0
  763. package/src/workspace/work-cli.ts +5 -3
  764. package/src/workspace/work-evidence-ops.test.ts +176 -0
  765. package/src/workspace/work-evidence-ops.ts +189 -0
  766. package/src/workspace/work-evidence.schema.json +49 -2
  767. package/src/workspace/work-evidence.ts +22 -3
  768. package/src/workspace/workspace-gates.ts +200 -0
  769. package/src/workspace/workspace-kinds.schema.json +58 -3
  770. package/src/workspace/workspace-ledger.ts +130 -0
  771. package/src/workspace/workspace-run.test.ts +322 -0
  772. package/src/workspace/workspace-run.ts +461 -0
@@ -24,13 +24,14 @@
24
24
  * (`changes-cli.ts`, #2773), which prints a document of its own.
25
25
  */
26
26
 
27
- import { realpathSync } from "node:fs";
27
+ import { readFileSync, realpathSync } from "node:fs";
28
28
  import { join, relative, resolve, sep } from "node:path";
29
29
  import { formatError, formatSuccess } from "../cli/format";
30
30
  import type { CommandContext } from "../cli/registry";
31
31
  import type { LintDiagnostic, LintRule } from "../lint/rule";
32
32
  import { findWorkspaceRoot } from "../project-root";
33
33
  import type { DeclarationCheckReport } from "./checks";
34
+ import type { ForgeRepoList } from "./checks/org";
34
35
  import type { RecordFacts } from "./checks/records";
35
36
  import { LOCK_FILE, LockError, parseLock, readLock } from "./lineage-lock";
36
37
  import type { ReasonCode } from "./reason-codes";
@@ -91,7 +92,7 @@ export function findingKey(f: CheckFinding): string {
91
92
  }
92
93
 
93
94
  const USAGE =
94
- "chant workspace check [--at <rev>] [--json] [--format stylish|json|sarif] [--generated] [--kind <kind file>] [--live --env <env>]\n chant workspace check --changes <base>..<head> [--work <id>] [--severity off|warn|fail] [--kind <kind file>...] [--json]";
95
+ "chant workspace check [--at <rev>] [--json] [--format stylish|json|sarif] [--generated] [--kind <kind file>] [--live --env <env>] [--forge-repos <file>]\n chant workspace check --changes <base>..<head> [--work <id>] [--severity off|warn|fail] [--kind <kind file>...] [--json]";
95
96
  const FORMATS = ["stylish", "json", "sarif"] as const;
96
97
 
97
98
  /** A lock finding as a lint diagnostic, for `--format json` and `--format sarif`. */
@@ -141,7 +142,7 @@ export type CheckDocument =
141
142
  chant: string;
142
143
  at: string | null;
143
144
  /** The declaration's workspace, or null when there is none; `name` is null when it can't be read. */
144
- workspace: { name: string | null; root: string } | null;
145
+ workspace: { name: string | null; root: string; repo: string | null } | null;
145
146
  declaration?: DeclarationCheckReport;
146
147
  })
147
148
  | { $schema: string; contract: number; chant: string; error: { code: CheckErrorCode; message: string; location: null } };
@@ -152,7 +153,11 @@ export type CheckDocument =
152
153
  * to `cwd`. `runGenerators` is `--generated`; with `at` it does nothing,
153
154
  * since the member facts describe the working tree, not the revision.
154
155
  */
155
- export async function runChecks(cwd: string, at?: string, options: { runGenerators?: boolean; kind?: string; live?: { env: string; onStderr?: (text: string) => void } } = {}): Promise<CheckDocument> {
156
+ export async function runChecks(
157
+ cwd: string,
158
+ at?: string,
159
+ options: { runGenerators?: boolean; kind?: string; live?: { env: string; onStderr?: (text: string) => void }; forgeRepos?: ForgeRepoList } = {},
160
+ ): Promise<CheckDocument> {
156
161
  // Loaded here, not at the top: `workspace upgrade` imports this module for checkLineage alone.
157
162
  const [{ findDeclarationDir, readDeclaration, readerVersion }, { gitTop, gitTree, resolveCommit, workingTree }] = await Promise.all([
158
163
  import("./declaration"),
@@ -190,7 +195,7 @@ export async function runChecks(cwd: string, at?: string, options: { runGenerato
190
195
  }
191
196
 
192
197
  let declaration: DeclarationCheckReport | undefined;
193
- let workspace: { name: string | null; root: string } | null = null;
198
+ let workspace: { name: string | null; root: string; repo: string | null } | null = null;
194
199
  if (found) {
195
200
  const { runDeclarationChecks } = await import("./checks");
196
201
  const { dir, tree } = found;
@@ -200,6 +205,7 @@ export async function runChecks(cwd: string, at?: string, options: { runGenerato
200
205
  ...(commit !== null ? { tree } : {}),
201
206
  ...(records ? { records } : {}),
202
207
  ...(options.live ? { live: options.live } : {}),
208
+ ...(options.forgeRepos ? { forgeRepos: options.forgeRepos } : {}),
203
209
  });
204
210
  let name: string | null = null;
205
211
  try {
@@ -207,7 +213,8 @@ export async function runChecks(cwd: string, at?: string, options: { runGenerato
207
213
  } catch {
208
214
  // The WSP001 finding already says why.
209
215
  }
210
- workspace = { name, root: found.root };
216
+ const { repoIdentity } = await import("./repo-ref");
217
+ workspace = { name, root: found.root, repo: repoIdentity(dir) };
211
218
  }
212
219
  const ok = report.ok && (declaration?.ok ?? true);
213
220
  return { ...head, at: commit, workspace, ...report, ok, ...(declaration ? { declaration } : {}) };
@@ -267,7 +274,23 @@ export async function runWorkspaceCheck(ctx: CommandContext): Promise<number> {
267
274
  const handed = await handToRootChant(root, ctx.args.at);
268
275
  if (handed !== undefined) return handed;
269
276
  }
270
- const doc = await runChecks(root, ctx.args.at, { runGenerators: ctx.args.generated === true, ...(ctx.args.kind !== undefined ? { kind: ctx.args.kind } : {}), ...(ctx.args.live && ctx.args.env ? { live: { env: ctx.args.env, onStderr: (text: string) => process.stderr.write(text) } } : {}) });
277
+ // The forge's repository list (#3168) is input a warden writes; chant never asks the forge itself.
278
+ let forgeRepos: ForgeRepoList | undefined;
279
+ if (ctx.args.forgeRepos !== undefined) {
280
+ const { parseForgeRepos } = await import("./checks/org");
281
+ try {
282
+ forgeRepos = parseForgeRepos(readFileSync(resolve(ctx.args.forgeRepos), "utf-8"), ctx.args.forgeRepos);
283
+ } catch (err) {
284
+ console.error(formatError({ message: `--forge-repos: ${(err as Error).message}`, hint: USAGE }));
285
+ return 1;
286
+ }
287
+ }
288
+ const doc = await runChecks(root, ctx.args.at, {
289
+ runGenerators: ctx.args.generated === true,
290
+ ...(ctx.args.kind !== undefined ? { kind: ctx.args.kind } : {}),
291
+ ...(ctx.args.live && ctx.args.env ? { live: { env: ctx.args.env, onStderr: (text: string) => process.stderr.write(text) } } : {}),
292
+ ...(forgeRepos ? { forgeRepos } : {}),
293
+ });
271
294
  if ("error" in doc) {
272
295
  if (ctx.args.json || format === "json") console.log(JSON.stringify(doc, null, 2));
273
296
  else console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
@@ -195,10 +195,11 @@ function nearNames(name: string, outputs: JoinHandle[]): string[] {
195
195
  */
196
196
  export function resolveLinks(
197
197
  declaration: Declaration,
198
- handles: ReadonlyMap<string, MemberHandles>,
198
+ sourceHandles: ReadonlyMap<string, MemberHandles>,
199
199
  telemetry?: TelemetryTargets,
200
200
  readLinks?: ReadonlyMap<string, KindLinkRead>,
201
201
  ): LinkTableRow[] {
202
+ const handles = withReaderOutputs(sourceHandles, readLinks);
202
203
  const rows: LinkTableRow[] = [];
203
204
  const declaredTargets = new Map<string, Set<string>>();
204
205
 
@@ -311,6 +312,28 @@ export function resolveLinks(
311
312
  return [...rows, ...inferred];
312
313
  }
313
314
 
315
+ /**
316
+ * `handles` with the outputs each member's kind reader found in its files
317
+ * (#3935) added to the member's own. The member stays complete unless a gap
318
+ * the reader flagged `outputs` left a name out. A member with no handles
319
+ * (its directory is missing, or its kind unknown) gets none.
320
+ */
321
+ export function withReaderOutputs(handles: ReadonlyMap<string, MemberHandles>, reads: ReadonlyMap<string, KindLinkRead> | undefined): ReadonlyMap<string, MemberHandles> {
322
+ if (!reads) return handles;
323
+ let out: Map<string, MemberHandles> | undefined;
324
+ for (const [member, read] of reads) {
325
+ const base = handles.get(member);
326
+ const gaps = read.gaps.filter((g) => g.outputs);
327
+ if (!base || (!read.outputs?.length && gaps.length === 0)) continue;
328
+ const have = new Set(base.outputs.map((o) => o.name));
329
+ const added = (read.outputs ?? []).filter((o) => !have.has(o.name) && have.add(o.name)).map((o) => ({ name: o.name }));
330
+ const complete = base.complete && gaps.length === 0;
331
+ const why = base.why ?? (gaps.length === 0 ? null : gaps.length === 1 ? gaps[0].message : `${gaps[0].message}, and ${gaps.length - 1} more`);
332
+ (out ??= new Map(handles)).set(member, { ...base, outputs: [...base.outputs, ...added], complete, why: complete ? base.why : why, exposesNone: false });
333
+ }
334
+ return out ?? handles;
335
+ }
336
+
314
337
  /** Match a declared or reader's output row against its producer's handles, setting its status and reason. */
315
338
  function resolveOutput(row: LinkRow, handles: ReadonlyMap<string, MemberHandles>): void {
316
339
  const producer = handles.get(row.producer);
@@ -129,7 +129,7 @@ describe("chant workspace ls on built workspaces", () => {
129
129
  ["tools", false, "kind-probe-failed"],
130
130
  ["vendor", true, null],
131
131
  ]);
132
- expect(doc.members[2].reason?.message).toMatch(/known kinds: chant, design, other, workspace/);
132
+ expect(doc.members[2].reason?.message).toMatch(/known kinds: chant, design, other, repo, workspace/);
133
133
  expect(doc.members[0].roles).toEqual([{ name: "frontend", path: "src" }]);
134
134
  expect(doc.groups[0].reason?.code).toBe("no-matches");
135
135
  expect(doc.summary).toEqual({ members: 5, unreadable: 3, groups: 1, matches: 0 });
@@ -51,6 +51,7 @@
51
51
  "required": ["name", "root", "file", "schema", "minReader", "pins"],
52
52
  "properties": {
53
53
  "name": { "type": "string", "description": "The declared name. With the revision, it identifies the workspace." },
54
+ "repo": { "type": ["string", "null"], "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." },
54
55
  "root": {
55
56
  "type": "string",
56
57
  "description": "The workspace root relative to the git root, with / separators, \".\" for the git root itself. An absolute path outside a git repository."
@@ -158,6 +159,12 @@
158
159
  "type": "array",
159
160
  "items": { "$ref": "#/$defs/generatedFile" }
160
161
  },
162
+ "repo": {
163
+ "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.",
164
+ "type": "object",
165
+ "required": ["url", "rev", "workspace"],
166
+ "properties": { "url": { "type": "string" }, "rev": { "type": "string" }, "workspace": { "type": "string" } }
167
+ },
161
168
  "agents": {
162
169
  "description": "Added in contract 1 by #3615. The names of the agent sessions the declaration binds to the member (agents[].member or agents[].members), in declaration order, read where workspace.agentsFrom says. Empty when none is. A session bound to several members is listed on each.",
163
170
  "type": "array",
@@ -39,7 +39,8 @@ import {
39
39
  } from "./declaration";
40
40
  import type { ReasonCode } from "./reason-codes";
41
41
  import { memberGenerated, type LsGenerated } from "./ls-generated";
42
- import { loadKindRegistry, probeKind, type KindRegistry } from "./kinds";
42
+ import { repoIdentity } from "./repo-ref";
43
+ import { loadKindRegistry, probeKind, REPO_KIND, type KindRegistry } from "./kinds";
43
44
  import type { WorkspaceTree } from "./tree";
44
45
  import { handToRootChant, locateWorkspace } from "./which-chant";
45
46
  import { declaredSessions } from "./write-scope";
@@ -117,6 +118,8 @@ export interface LsMember {
117
118
  generated: LsGenerated[];
118
119
  /** The agent sessions bound to the member, in declaration order (#3615); empty when none is. */
119
120
  agents: string[];
121
+ /** For a member of kind repo, the other repository's workspace it names (#3168). Absent for every other kind. */
122
+ repo?: { url: string; rev: string; workspace: string };
120
123
  }
121
124
 
122
125
  /**
@@ -168,6 +171,8 @@ export type LsDocument =
168
171
  at: string | null;
169
172
  workspace: {
170
173
  name: string;
174
+ /** The repository the document came from (#3168): its origin URL, redacted, or null. */
175
+ repo: string | null;
171
176
  /** The workspace root relative to the git root, `"."` for the git root itself. Absolute outside git. */
172
177
  root: string;
173
178
  file: string;
@@ -211,6 +216,8 @@ export interface LsQuery {
211
216
 
212
217
  /** A member's reason code, or null when its kind can read it. */
213
218
  export function memberReason(member: Member, tree: WorkspaceTree, kinds: KindRegistry): LsMember["reason"] {
219
+ // A repo member has no directory here (#3168); it is read from its own repository.
220
+ if (member.kind === REPO_KIND) return null;
214
221
  if (tree.stat(member.dir === "." ? "" : member.dir) !== "dir") {
215
222
  return { code: "dir-missing", message: `${member.dir} is not a directory${tree.label}` };
216
223
  }
@@ -283,6 +290,7 @@ function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declarati
283
290
  records: unloaded(m.records),
284
291
  generated: memberGenerated(m, tree, repoPrefix),
285
292
  agents: sessions.agents.filter((a) => a.members.includes(m.name)).map((a) => a.name),
293
+ ...(m.repo ? { repo: { ...m.repo } } : {}),
286
294
  // The member's x- keys, as declared (#3595).
287
295
  ...extensionsAt(declaration, m.pointer),
288
296
  };
@@ -311,6 +319,7 @@ function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declarati
311
319
  at,
312
320
  workspace: {
313
321
  name: declaration.name,
322
+ repo: repoIdentity(rootOnDisk),
314
323
  root: located.root,
315
324
  file: declaration.file,
316
325
  schema: declaration.schema,
@@ -12,7 +12,10 @@
12
12
  * - For `graph` only, members of a pinned package's kind whose kinds file
13
13
  * says how to read them (#2874, a `graph` block): each runs `chant graph`
14
14
  * in a reader project chant writes for it (`kind-readers.ts`). Other
15
- * package kinds are `kind-not-run`, as before.
15
+ * package kinds are `kind-not-run`, as before. A kind no built-in or
16
+ * pinned package supplies, such as `terraform` when the pinned lexicon is
17
+ * missing or at another version, is `unknown-kind` and fails the read,
18
+ * with the pin's problem in the message (#4380).
16
19
  * - For `build` and `lint` only, every project an example group matches
17
20
  * (ws-051): groups are built and linted, and have no ledger, audit or place
18
21
  * in the graph. On a large repository this is most of the run, so
@@ -155,6 +158,12 @@ export interface PlanOptions {
155
158
  reader?: Toolchain;
156
159
  /** The files to plan from, such as a revision's (`--at`); the working tree under `root` by default. */
157
160
  tree?: WorkspaceTree;
161
+ /**
162
+ * Why a pinned package's kinds couldn't be loaded (`loadKindRegistry`'s
163
+ * problems), such as a pin at one version with another installed. A graph
164
+ * read names them on a member whose kind is unknown (#4380).
165
+ */
166
+ kindProblems?: readonly string[];
158
167
  }
159
168
 
160
169
  /** Work out which projects run and under which chant. Throws a {@link WorkspaceReadError} for an unreadable declaration or an unknown `--member` name. */
@@ -218,10 +227,22 @@ export function planMembers(verb: WorkspaceVerb, root: string, options: PlanOpti
218
227
  });
219
228
  continue;
220
229
  }
230
+ // A kind no built-in or pinned package supplies is unknown, not a kind
231
+ // the read leaves out (#4380): a terraform member whose pinned lexicon is
232
+ // missing or at another version said kind-not-run before, as if the graph
233
+ // never read its kind. Say why, the pin's problem included.
234
+ if (verb === "graph" && !kinds.get(entry.kind)) {
235
+ const reason = memberReason(entry, tree, kinds) ?? { code: "unknown-kind", message: `no built-in kind or pinned package supplies kind ${entry.kind}` };
236
+ const problems = reason.code === "unknown-kind" ? (options.kindProblems ?? []) : [];
237
+ unreadable.push({ name: entry.name, dir: entry.dir, kind: entry.kind, reason: problems.length ? { ...reason, message: `${reason.message}; ${problems.join("; ")}` } : reason });
238
+ continue;
239
+ }
221
240
  if (entry.kind !== "chant") {
222
241
  const why =
223
242
  entry.kind === "workspace"
224
243
  ? "a nested workspace runs its own chant workspace commands"
244
+ : entry.kind === "repo"
245
+ ? "a repo member is another repository's workspace, which runs its own chant workspace commands; chant workspace graph reads it at its pinned revision"
225
246
  : entry.kind === "design"
226
247
  ? "a design member is data: chant reads its files for record pins and builds nothing"
227
248
  : `chant workspace ${verb} runs members of kind chant, and this one is kind ${entry.kind}`;
@@ -308,6 +329,8 @@ export function memberArgv(verb: WorkspaceVerb, unit: RunUnit, args: ParsedArgs)
308
329
  // The detail tier too, so `workspace graph --detail 1` composes collapsed
309
330
  // composites with their rolled-up status (#4228).
310
331
  if (args.detail !== undefined) argv.push("--detail", String(args.detail));
332
+ // Each member's chant maps its own capability view (#4378).
333
+ if (args.capabilityView) argv.push("--capability-view");
311
334
  }
312
335
  return argv;
313
336
  }
@@ -535,13 +558,17 @@ export async function runWorkspaceMembers(ctx: CommandContext, verb: WorkspaceVe
535
558
  }
536
559
 
537
560
  const results = await executePlan(plan, args);
538
- const anyFailed = results.some((r) => r.exitCode !== 0) || plan.unreadable.length > 0;
561
+ // #2549: the disagreement lint, over every member's graph, when asked for.
562
+ const cross = verb === "lint" && args.crossRoot ? await runCrossRoot(found.dir, args) : undefined;
563
+ const anyFailed = results.some((r) => r.exitCode !== 0) || plan.unreadable.length > 0 || cross?.failed === true;
539
564
  const workspace = { name: plan.workspace.name, root: plan.workspace.root };
540
565
 
541
566
  if (verb === "lint" && args.format === "sarif") {
542
567
  for (const r of results) if (r.stderr.trim()) process.stderr.write(r.stderr.endsWith("\n") ? r.stderr : `${r.stderr}\n`);
543
568
  const unreadable: MemberOutput[] = plan.unreadable.map((s) => ({ id: s.name, member: s.name, dir: s.dir ?? ".", exitCode: 1, stdout: "", stderr: `${s.reason.code}: ${s.reason.message}` }));
544
- emitDocument(mergeSarif([...results, ...unreadable], plan.workspace.root), args.output);
569
+ const sarif = mergeSarif([...results, ...unreadable], plan.workspace.root) as { runs?: unknown[] };
570
+ if (cross) (sarif.runs ??= []).push(crossRootSarifRun(cross));
571
+ emitDocument(sarif, args.output);
545
572
  console.error(summaryLine(plan, results));
546
573
  return anyFailed ? 1 : 0;
547
574
  }
@@ -577,6 +604,7 @@ export async function runWorkspaceMembers(ctx: CommandContext, verb: WorkspaceVe
577
604
  ...plan.unreadable.map((s) => ({ member: s.name, dir: s.dir, exitCode: 1, diagnostics: null, reason: s.reason })),
578
605
  ],
579
606
  skipped: plan.skipped,
607
+ ...(cross ? { crossRoot: crossRootJson(cross) } : {}),
580
608
  };
581
609
  }
582
610
  emitDocument(doc, args.output);
@@ -587,5 +615,56 @@ export async function runWorkspaceMembers(ctx: CommandContext, verb: WorkspaceVe
587
615
  // Text: each member's own output under a heading. A build without -o prints
588
616
  // no templates, since several members' templates can't share one stdout.
589
617
  printSections(plan, results, verb !== "build" || args.output !== undefined);
618
+ if (cross) printCrossRoot(cross);
590
619
  return anyFailed ? 1 : 0;
591
620
  }
621
+
622
+ // ── The disagreement lint (#2549) ────────────────────────────────────────────
623
+
624
+ interface CrossRootRun {
625
+ result?: import("./cross-root-lint").CrossRootLintResult;
626
+ /** Why the members' graphs could not be read. */
627
+ error?: string;
628
+ /** An error finding, or a graph that could not be read. */
629
+ failed: boolean;
630
+ }
631
+
632
+ async function runCrossRoot(root: string, args: ParsedArgs): Promise<CrossRootRun> {
633
+ const { workspaceGraph } = await import("./graph-cli");
634
+ const { crossRootLint } = await import("./cross-root-lint");
635
+ const { doc, failed } = await workspaceGraph({ cwd: root, ...(args.members?.length ? { members: args.members } : {}), onStderr: (t) => process.stderr.write(t) });
636
+ if ("error" in doc) return { error: `${doc.error.code}: ${doc.error.message}`, failed: true };
637
+ const result = await crossRootLint(doc, args.env ? { env: args.env } : {});
638
+ return { result, failed: failed || result.findings.some((f) => f.severity === "error") };
639
+ }
640
+
641
+ function crossRootJson(cross: CrossRootRun): unknown {
642
+ return cross.result ? { findings: cross.result.findings, checks: cross.result.checks, unloaded: cross.result.unloaded } : { error: cross.error };
643
+ }
644
+
645
+ function crossRootSarifRun(cross: CrossRootRun): unknown {
646
+ const findings = cross.result?.findings ?? [];
647
+ return {
648
+ tool: { driver: { name: "chant", rules: [...new Set(findings.map((f) => f.checkId))].sort().map((id) => ({ id })) } },
649
+ automationDetails: { id: "cross-root/" },
650
+ properties: { crossRoot: true, checks: cross.result?.checks ?? [], unloaded: cross.result?.unloaded ?? [] },
651
+ results: findings.map((f) => ({
652
+ ruleId: f.checkId,
653
+ level: f.severity === "error" ? "error" : f.severity === "warning" ? "warning" : "note",
654
+ message: { text: f.message },
655
+ ...(f.root ? { properties: { member: f.root } } : {}),
656
+ })),
657
+ ...(cross.error ? { invocations: [{ executionSuccessful: false, toolExecutionNotifications: [{ message: { text: cross.error } }] }] } : {}),
658
+ };
659
+ }
660
+
661
+ function printCrossRoot(cross: CrossRootRun): void {
662
+ if (!cross.result) {
663
+ console.log(`── cross-root (the members' graphs could not be read: ${cross.error})`);
664
+ return;
665
+ }
666
+ const { findings, checks, unloaded } = cross.result;
667
+ console.log(`── cross-root (${checks.join(", ")}; ${findings.length} finding${findings.length === 1 ? "" : "s"})`);
668
+ for (const f of findings) console.log(` ${f.severity.padEnd(7)} ${f.checkId}${f.root ? ` [${f.root}]` : ""} ${f.message}`);
669
+ for (const u of unloaded) console.log(` (lexicon ${u.lexicon}: its cross-root checks could not be loaded: ${u.reason})`);
670
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * A member kind names a JSON Schema its member's file must pass (#3672): the
3
+ * kinds file's memberFile block, the schema read as data from the package,
4
+ * and WSP012 from `chant workspace check`.
5
+ */
6
+ import { execFileSync } from "node:child_process";
7
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { dirname, join } from "node:path";
10
+ import { afterAll, describe, expect, test } from "vitest";
11
+ import { runDeclarationChecks } from "./checks";
12
+ import { checkMemberFile, parseKindData, readPackageKinds } from "./kinds";
13
+
14
+ const scratch: string[] = [];
15
+ afterAll(() => {
16
+ for (const d of scratch) rmSync(d, { recursive: true, force: true });
17
+ });
18
+ function repo(files: Record<string, string>): string {
19
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "chant-3672-")));
20
+ scratch.push(root);
21
+ execFileSync("git", ["init", "-q"], { cwd: root });
22
+ for (const [path, text] of Object.entries(files)) {
23
+ mkdirSync(dirname(join(root, path)), { recursive: true });
24
+ writeFileSync(join(root, path), text);
25
+ }
26
+ return root;
27
+ }
28
+
29
+ const controlSchema = {
30
+ $schema: "https://json-schema.org/draft/2020-12/schema",
31
+ type: "object",
32
+ required: ["estates"],
33
+ properties: { estates: { type: "array", items: { type: "string" } } },
34
+ additionalProperties: false,
35
+ };
36
+
37
+ const controlKind = (memberFile: unknown = { path: "terragucci.yml", schema: "schemas/control.schema.json" }) => ({
38
+ name: "control",
39
+ description: "a terragucci control repo, with a terragucci.yml",
40
+ precedence: 300,
41
+ probe: { anyFile: ["terragucci.yml"] },
42
+ memberFile,
43
+ });
44
+
45
+ /** A kinds-only package at kinds/control, and a workspace that pins it. */
46
+ function workspace(files: Record<string, string>, opts: { schema?: string | null; kind?: unknown } = {}): string {
47
+ const schemaText = opts.schema === undefined ? JSON.stringify(controlSchema) : opts.schema;
48
+ return repo({
49
+ "chant.workspace.json": JSON.stringify({ name: "acme", schema: 1, pins: [{ path: "kinds/control" }], members: [{ name: "ctl", dir: "ctl", kind: "control" }] }),
50
+ "kinds/control/package.json": JSON.stringify({ name: "control-kinds", exports: { "./workspace-kinds": "./workspace-kinds.json" } }),
51
+ "kinds/control/workspace-kinds.json": JSON.stringify({ schema: 1, kinds: [opts.kind ?? controlKind()] }),
52
+ ...(schemaText === null ? {} : { "kinds/control/schemas/control.schema.json": schemaText }),
53
+ ...files,
54
+ });
55
+ }
56
+
57
+ const wsp012 = async (root: string) =>
58
+ (await runDeclarationChecks(root)).diagnostics.filter((d) => d.ruleId === "WSP012" || d.ruleId === "WSP002").map((d) => `${d.ruleId}: ${d.message}`);
59
+
60
+ describe("the kinds file's memberFile block", () => {
61
+ test("is kind data: a path in the member and a .json schema in the package", () => {
62
+ const ok = parseKindData(JSON.stringify({ schema: 1, kinds: [controlKind()] }), "p");
63
+ expect(ok.problems).toEqual([]);
64
+ expect(ok.kinds[0].memberFile).toEqual({ path: "terragucci.yml", schema: "schemas/control.schema.json" });
65
+ for (const bad of [{ path: "../x.yml", schema: "s.json" }, { path: "x.yml", schema: "s.yaml" }, { path: "x.yml", schema: "../s.json" }, { path: "x.yml" }]) {
66
+ expect(parseKindData(JSON.stringify({ schema: 1, kinds: [controlKind(bad)] }), "p").problems).not.toEqual([]);
67
+ }
68
+ });
69
+
70
+ test("reading the package compiles the schema as data, and reports one that is missing or not a schema", () => {
71
+ const good = workspace({});
72
+ const read = readPackageKinds(join(good, "kinds/control"));
73
+ expect(read.problems).toEqual([]);
74
+ expect(typeof read.kinds[0].memberFile?.validate).toBe("function");
75
+ expect(readPackageKinds(join(workspace({}, { schema: null }), "kinds/control")).problems[0]).toMatch(/does not exist/);
76
+ expect(readPackageKinds(join(workspace({}, { schema: "{ nope" }), "kinds/control")).problems[0]).toMatch(/is not JSON/);
77
+ expect(readPackageKinds(join(workspace({}, { schema: JSON.stringify({ type: 12 }) }), "kinds/control")).problems[0]).toMatch(/does not compile/);
78
+ });
79
+ });
80
+
81
+ describe("checkMemberFile", () => {
82
+ const mf = () => readPackageKinds(join(workspace({}), "kinds/control")).kinds[0].memberFile!;
83
+ test("parses YAML by extension and JSON otherwise, and names each failing place", () => {
84
+ expect(checkMemberFile(mf(), "terragucci.yml", "estates: [a, b]\n")).toEqual([]);
85
+ expect(checkMemberFile(mf(), "t.json", '{"estates":["a"]}')).toEqual([]);
86
+ expect(checkMemberFile(mf(), "terragucci.yml", "estates: [1]\nextra: true\n").map((p) => p.message).sort()).toEqual(["/estates/0 must be string", "the file must NOT have additional properties (extra)"]);
87
+ expect(checkMemberFile(mf(), "t.json", "estates: []")[0].message).toMatch(/^is not JSON/);
88
+ });
89
+ });
90
+
91
+ describe("WSP012: a member's file passes its kind's schema", () => {
92
+ test("a member whose file passes has no finding", async () => {
93
+ expect(await wsp012(workspace({ "ctl/terragucci.yml": "estates: [prod]\n" }))).toEqual([]);
94
+ });
95
+
96
+ test("a file that fails the schema fails the check, once per reason", async () => {
97
+ const report = await runDeclarationChecks(workspace({ "ctl/terragucci.yml": "estates: prod\n" }));
98
+ expect(report.ok).toBe(false);
99
+ const found = report.diagnostics.filter((d) => d.ruleId === "WSP012");
100
+ expect(found.map((d) => d.message)).toEqual(["member ctl's terragucci.yml fails kind control's schema schemas/control.schema.json: /estates must be array"]);
101
+ expect(found[0].file).toMatch(/ctl\/terragucci\.yml$/);
102
+ });
103
+
104
+ test("a member without the file fails it", async () => {
105
+ // The probe also wants the file; the declared kind still names the schema, so WSP012 says what is missing.
106
+ expect(await wsp012(workspace({ "ctl/README.md": "" }))).toEqual(["WSP012: member ctl (ctl) has no terragucci.yml, which kind control requires"]);
107
+ });
108
+
109
+ test("a schema that doesn't load is WSP002, and its members can't pass WSP012", async () => {
110
+ const found = await wsp012(workspace({ "ctl/terragucci.yml": "estates: []\n" }, { schema: null }));
111
+ expect(found).toHaveLength(2);
112
+ expect(found[0]).toMatch(/^WSP002: .*kind control names member-file schema schemas\/control.schema.json, which does not exist/);
113
+ expect(found[1]).toMatch(/^WSP012: .*did not load/);
114
+ });
115
+
116
+ test("the declaration can turn it down", async () => {
117
+ const root = workspace({ "ctl/terragucci.yml": "nope: 1\n" });
118
+ const decl = JSON.parse(execFileSync("cat", [join(root, "chant.workspace.json")], { encoding: "utf-8" }));
119
+ writeFileSync(join(root, "chant.workspace.json"), JSON.stringify({ ...decl, checks: { WSP012: "warning" } }));
120
+ const report = await runDeclarationChecks(root);
121
+ expect(report.ok).toBe(true);
122
+ expect(report.diagnostics.filter((d) => d.ruleId === "WSP012").every((d) => d.severity === "warning")).toBe(true);
123
+ });
124
+ });
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The links `chant migrate` writes into a workspace declaration (#4399):
3
+ * which members are the producer and the consumer, that the edit touches only
4
+ * the consumer's `links`, that a second run adds nothing, and that every case
5
+ * with nothing to link says why.
6
+ */
7
+ import { afterAll, describe, expect, test } from "vitest";
8
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { dirname, join } from "node:path";
11
+ import { parseDeclaration } from "./declaration";
12
+ import { planMigrateLinks } from "./migrate-links";
13
+
14
+ const scratch: string[] = [];
15
+ afterAll(() => {
16
+ for (const d of scratch) rmSync(d, { recursive: true, force: true });
17
+ });
18
+
19
+ function repo(declaration: string, file = "chant.workspace.json"): string {
20
+ const root = mkdtempSync(join(tmpdir(), "chant-migrate-links-"));
21
+ scratch.push(root);
22
+ for (const [path, text] of Object.entries({ [file]: declaration, "db/schema.sql": "", "mirror/mirrors.ts": "", "other/x.ts": "" })) {
23
+ mkdirSync(dirname(join(root, path)), { recursive: true });
24
+ writeFileSync(join(root, path), text);
25
+ }
26
+ return root;
27
+ }
28
+
29
+ const decl = (members: unknown[]) => `${JSON.stringify({ name: "shop", schema: 1, members }, null, 2)}\n`;
30
+ const MEMBERS = [
31
+ { name: "db", dir: "db", kind: "chant" },
32
+ { name: "mirror", dir: "mirror", kind: "chant" },
33
+ ];
34
+ const outputs = ["public.users.id", "public.users.email"];
35
+
36
+ describe("planMigrateLinks", () => {
37
+ test("adds one link per output to the member holding the output file, and changes nothing else", () => {
38
+ const root = repo(decl(MEMBERS));
39
+ const r = planMigrateLinks({ sourcePath: join(root, "db/schema.sql"), outputPath: join(root, "mirror/mirrors.ts"), outputs });
40
+ if (!r || !("plan" in r)) throw new Error(JSON.stringify(r));
41
+ expect(r.plan).toMatchObject({ producer: "db", consumer: "mirror", added: outputs });
42
+ const members = parseDeclaration(r.plan.text, "chant.workspace.json").members;
43
+ expect(members.find((m) => m.name === "mirror")!.links.map((l) => `${l.member} ${l.output}`)).toEqual(outputs.map((o) => `db ${o}`));
44
+ expect(members.find((m) => m.name === "db")!.links).toEqual([]);
45
+ expect(r.plan.text.startsWith('{\n "name": "shop"')).toBe(true);
46
+ });
47
+
48
+ test("keeps the links already there and adds only the new ones; a second plan adds nothing", () => {
49
+ const root = repo(decl([MEMBERS[0], { ...MEMBERS[1], links: [{ member: "db", output: "public.users.id" }, { member: "other", output: "x" }] }, { name: "other", dir: "other", kind: "chant" }]));
50
+ const r = planMigrateLinks({ sourcePath: join(root, "db"), outputPath: join(root, "mirror/mirrors.ts"), outputs });
51
+ if (!r || !("plan" in r)) throw new Error(JSON.stringify(r));
52
+ expect(r.plan.added).toEqual(["public.users.email"]);
53
+ const links = parseDeclaration(r.plan.text, "x.json").members.find((m) => m.name === "mirror")!.links;
54
+ expect(links.map((l) => `${l.member} ${l.output}`)).toEqual(["db public.users.id", "other x", "db public.users.email"]);
55
+ writeFileSync(join(root, "chant.workspace.json"), r.plan.text);
56
+ const again = planMigrateLinks({ sourcePath: join(root, "db"), outputPath: join(root, "mirror/mirrors.ts"), outputs });
57
+ expect(again).toMatchObject({ plan: { added: [] } });
58
+ });
59
+
60
+ test("reads a jsonc declaration and keeps its comments", () => {
61
+ const text = `{\n // the shop\n "name": "shop", "schema": 1,\n "members": [\n { "name": "db", "dir": "db", "kind": "chant" },\n { "name": "mirror", "dir": "mirror", "kind": "chant" } // the mirror\n ]\n}\n`;
62
+ const root = repo(text, "chant.workspace.jsonc");
63
+ const r = planMigrateLinks({ sourcePath: join(root, "db/schema.sql"), outputPath: join(root, "mirror/mirrors.ts"), outputs });
64
+ if (!r || !("plan" in r)) throw new Error(JSON.stringify(r));
65
+ expect(r.plan.text).toContain("// the shop");
66
+ expect(r.plan.text).toContain("// the mirror");
67
+ expect(r.plan.added).toEqual(outputs);
68
+ });
69
+
70
+ test("the deepest member holds a path: a nested member is not its parent's", () => {
71
+ const root = repo(decl([{ name: "all", dir: ".", kind: "chant" }, { name: "db", dir: "db", kind: "chant" }, { name: "mirror", dir: "mirror", kind: "chant" }]));
72
+ const r = planMigrateLinks({ sourcePath: join(root, "db/schema.sql"), outputPath: join(root, "mirror/mirrors.ts"), outputs });
73
+ expect(r).toMatchObject({ plan: { producer: "db", consumer: "mirror" } });
74
+ });
75
+
76
+ test("no workspace above the source is no outcome at all", () => {
77
+ const root = mkdtempSync(join(tmpdir(), "chant-no-workspace-"));
78
+ scratch.push(root);
79
+ writeFileSync(join(root, "schema.sql"), "");
80
+ expect(planMigrateLinks({ sourcePath: join(root, "schema.sql"), outputPath: join(root, "out.ts"), outputs })).toBeUndefined();
81
+ });
82
+
83
+ test.each([
84
+ ["output to stdout", (root: string) => ({ sourcePath: join(root, "db/schema.sql"), outputPath: undefined }), "pass --output inside the ClickHouse member"],
85
+ ["a source in no member", (root: string) => ({ sourcePath: join(root, "elsewhere/schema.sql"), outputPath: join(root, "mirror/mirrors.ts") }), "is in no member"],
86
+ ["an output in no member", (root: string) => ({ sourcePath: join(root, "db/schema.sql"), outputPath: join(root, "elsewhere/out.ts") }), "is in no member"],
87
+ ["a source and output in one member", (root: string) => ({ sourcePath: join(root, "db/schema.sql"), outputPath: join(root, "db/out.ts") }), "does not link to itself"],
88
+ ])("%s is skipped with a reason", (_name, paths, reason) => {
89
+ const root = repo(decl(MEMBERS));
90
+ const r = planMigrateLinks({ ...paths(root), outputs });
91
+ expect(r && "skipped" in r ? r.skipped : r).toContain(reason);
92
+ });
93
+
94
+ test("a declaration that does not read is skipped with its error, never rewritten", () => {
95
+ const root = repo("{ not json");
96
+ const r = planMigrateLinks({ sourcePath: join(root, "db/schema.sql"), outputPath: join(root, "mirror/mirrors.ts"), outputs });
97
+ expect(r && "skipped" in r ? r.skipped : r).toContain("does not read, so no link was written");
98
+ });
99
+ });