@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
@@ -17,6 +17,7 @@ import { MEMBER_RUN_REASON_CODES } from "./compose-graph";
17
17
  import {
18
18
  COMPOSITES_CONTRACT_VERSION,
19
19
  COMPOSITES_ENVIRONMENT_REASON_CODES,
20
+ COMPOSITES_EXECUTOR_REASON_CODES,
20
21
  COMPOSITES_ERROR_CODES,
21
22
  COMPOSITES_OUTPUT_SCHEMA_ID,
22
23
  COMPOSITES_REASON_CODES,
@@ -30,6 +31,8 @@ import {
30
31
  import { GRAPH_ERROR_CODES } from "./graph-cli";
31
32
  import schema from "./composites.schema.json";
32
33
 
34
+ const NO_EXECUTOR = { forge: null, workflow: null, reason: { code: "executor-none-declared", message: expect.any(String) } };
35
+
33
36
  afterAll(cleanScratch);
34
37
 
35
38
  const { expectValid } = contract(schema);
@@ -61,7 +64,7 @@ const componentIr = (components: { name: string; composites?: string[]; archetyp
61
64
  const local = (name: string) => ({ name: "local", lexicon: null, default: true, command: `chant run --components ${name}` });
62
65
 
63
66
  /** The default environment, named by nothing but chant itself. */
64
- const localEnv = (name: string) => ({ name: "local", default: true, source: "builtin", site: null, command: `chant run --components ${name}` });
67
+ const localEnv = (name: string) => ({ name: "local", default: true, source: "builtin", site: null, executor: NO_EXECUTOR, command: `chant run --components ${name}` });
65
68
 
66
69
  /**
67
70
  * app declares three instances and exports ImageUri; delivery links to it and
@@ -117,6 +120,7 @@ describe("composites output schema", () => {
117
120
  expect(schema.$defs.member.properties.reason.oneOf[1].properties!.code.enum).toEqual([...MEMBER_RUN_REASON_CODES]);
118
121
  expect(schema.$defs.member.properties.runtimeReasons.items.properties.code.enum).toEqual([...COMPOSITES_RUNTIME_REASON_CODES]);
119
122
  expect(schema.$defs.member.properties.environmentReasons.items.properties.code.enum).toEqual([...COMPOSITES_ENVIRONMENT_REASON_CODES]);
123
+ expect(schema.$defs.environment.properties.executor.properties.reason.oneOf[1].properties!.code.enum).toEqual([...COMPOSITES_EXECUTOR_REASON_CODES]);
120
124
  });
121
125
  });
122
126
 
@@ -381,11 +385,11 @@ describe("the environments each component may deploy to (#2695)", () => {
381
385
  const g = result((await workspaceComposites({ cwd: declared() })).doc);
382
386
  expectValid(g);
383
387
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments).toEqual([
384
- { name: "local", default: true, source: "builtin", site: null, command: "chant run --components queue-runner" },
385
- { name: "staging", default: false, source: "config", site: null, command: "chant run --components queue-runner --env staging" },
386
- { name: "prod", default: false, source: "config", site: null, command: "chant run --components queue-runner --env prod" },
388
+ { name: "local", default: true, source: "builtin", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner" },
389
+ { name: "staging", default: false, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env staging" },
390
+ { name: "prod", default: false, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env prod" },
387
391
  // The pattern pr-* is no environment by itself, and it covers the ledger's pr-42.
388
- { name: "pr-42", default: false, source: "ledger", site: null, command: "chant run --components queue-runner --env pr-42" },
392
+ { name: "pr-42", default: false, source: "ledger", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env pr-42" },
389
393
  ]);
390
394
  const jobs = g.members.find((m) => m.name === "jobs")!;
391
395
  expect(jobs.environmentReasons).toEqual([{ code: "environments-ledger-undeclared", message: expect.stringContaining("retired") }]);
@@ -397,7 +401,7 @@ describe("the environments each component may deploy to (#2695)", () => {
397
401
  // delivery has no _members/delivery, so the flat staging ledger is its; its config declares nothing, so anything goes.
398
402
  expect(g.components.find((c) => c.id === "delivery/edge")!.environments).toEqual([
399
403
  localEnv("edge"),
400
- { name: "staging", default: false, source: "ledger", site: null, command: "chant run --components edge --env staging" },
404
+ { name: "staging", default: false, source: "ledger", site: null, executor: NO_EXECUTOR, command: "chant run --components edge --env staging" },
401
405
  ]);
402
406
  expect(g.members.find((m) => m.name === "delivery")!.environmentReasons.map((r) => r.code)).toEqual(["environments-none-declared"]);
403
407
  expect(g.members.find((m) => m.name === "docs")!.environmentReasons).toEqual([]);
@@ -409,8 +413,8 @@ describe("the environments each component may deploy to (#2695)", () => {
409
413
  const g = result((await workspaceComposites({ cwd: root })).doc);
410
414
  expectValid(g);
411
415
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments).toEqual([
412
- { name: "local", default: true, source: "config", site: null, command: "chant run --components queue-runner" },
413
- { name: "prod", default: false, source: "config", site: null, command: "chant run --components queue-runner --env prod" },
416
+ { name: "local", default: true, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner" },
417
+ { name: "prod", default: false, source: "config", site: null, executor: NO_EXECUTOR, command: "chant run --components queue-runner --env prod" },
414
418
  ]);
415
419
  expect(g.members.find((m) => m.name === "jobs")!.environmentReasons).toEqual([]);
416
420
  });
@@ -445,4 +449,31 @@ describe("the environments each component may deploy to (#2695)", () => {
445
449
  expect(g.members.find((m) => m.name === "jobs")!.environmentReasons).toEqual([{ code: "environments-ledger-unreadable", message: "boom" }]);
446
450
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments.map((e) => e.name)).toEqual(["local", "prod"]);
447
451
  });
452
+
453
+ test("each environment names the forge workflow the config declares for it, a literal before a pattern (#4402)", async () => {
454
+ const root = declared();
455
+ writeFileSync(
456
+ join(root, "jobs", "chant.config.ts"),
457
+ 'export default { environments: ["staging", { name: "prod", executor: { forge: "github", workflow: "deploy-prod.yml" } }, { name: "pr-*", executor: { forge: "forgejo", workflow: "preview.yml" } }] };\n',
458
+ );
459
+ const g = result((await workspaceComposites({ cwd: root })).doc);
460
+ expectValid(g);
461
+ const executors = Object.fromEntries(g.components.find((c) => c.id === "jobs/queue-runner")!.environments.map((e) => [e.name, e.executor]));
462
+ expect(executors).toEqual({
463
+ local: NO_EXECUTOR,
464
+ staging: NO_EXECUTOR,
465
+ prod: { forge: "github", workflow: "deploy-prod.yml", reason: null },
466
+ "pr-42": { forge: "forgejo", workflow: "preview.yml", reason: null },
467
+ });
468
+ });
469
+
470
+ test("a config that can't be read says no executor is known (#4402)", async () => {
471
+ const root = declared();
472
+ writeFileSync(join(root, "jobs", "chant.config.ts"), 'throw new Error("no config here");\nexport default {};\n');
473
+ const g = result((await workspaceComposites({ cwd: root })).doc);
474
+ expectValid(g);
475
+ for (const e of g.components.find((c) => c.id === "jobs/queue-runner")!.environments) {
476
+ expect(e.executor).toEqual({ forge: null, workflow: null, reason: { code: "executor-config-unreadable", message: expect.stringContaining(e.name) } });
477
+ }
478
+ });
448
479
  });
@@ -36,7 +36,8 @@
36
36
  * And each component lists the environments it may deploy to (#2695,
37
37
  * `environments.ts`): `local`, the environments its member's config declares,
38
38
  * and those with a release in the member's ledger on `chant/lifecycle`, with
39
- * the command line for each.
39
+ * the command line for each, and the forge workflow that deploys each one
40
+ * when the member's config names it (#4402).
40
41
  */
41
42
 
42
43
  import { formatError } from "../cli/format";
@@ -50,7 +51,7 @@ import { emitDocument, type UnitResult } from "./member-commands";
50
51
  import { readerVersion, WORKSPACE_ERROR_CODES, type ErrorLocation, type WorkspaceErrorCode } from "./declaration";
51
52
  import type { ReasonCode } from "./reason-codes";
52
53
  import type { ComponentEnvironmentDeclaration } from "../components/component";
53
- import { componentEnvironments, ENVIRONMENT_REASON_CODES, memberEnvironments, readLedgerEnvironments, type ComponentEnvironment, type EnvironmentReason, type LedgerEnvironmentReader, type MemberEnvironments } from "./environments";
54
+ import { componentEnvironments, ENVIRONMENT_REASON_CODES, EXECUTOR_REASON_CODES, memberEnvironments, readLedgerEnvironments, type ComponentEnvironment, type EnvironmentReason, type LedgerEnvironmentReader, type MemberEnvironments } from "./environments";
54
55
  import { componentRuntimes, readRuntimesIn, RUNTIME_REASON_CODES, type ComponentRuntime, type MemberRuntimes, type PluginLoader, type RuntimeReason } from "./runtimes";
55
56
 
56
57
  /** `$id` of the JSON Schema for the document, shipped beside this file. */
@@ -83,6 +84,9 @@ export const COMPOSITES_RUNTIME_REASON_CODES = RUNTIME_REASON_CODES;
83
84
  /** Why a member's environments are only `local`, or leave out one its ledger has (#2695). Closed. */
84
85
  export const COMPOSITES_ENVIRONMENT_REASON_CODES = ENVIRONMENT_REASON_CODES;
85
86
 
87
+ /** Why an environment row's executor names no forge workflow (#4402). Closed. */
88
+ export const COMPOSITES_EXECUTOR_REASON_CODES = EXECUTOR_REASON_CODES;
89
+
86
90
  export interface CompositesMember {
87
91
  name: string;
88
92
  dir: string;
@@ -79,7 +79,7 @@
79
79
  "uniqueItems": true
80
80
  },
81
81
  "evidence": {
82
- "description": "The proof of done: public links, or workspace files pinned by the hash of their bytes (chant workspace records pin <path>). May be empty while the work is open; a done item with none gets the warning work-done-unpinned. An entry that names a criterion from acceptance carries the verification it is and its result, and a manual verdict names who gave it in by (#2772).",
82
+ "description": "The proof of done: public links, workspace files pinned by the hash of their bytes (chant workspace records pin <path>), or ops results on the chant/lifecycle branch (#3171). May be empty while the work is open; a done item with none gets the warning work-done-unpinned. An entry that names a criterion from acceptance carries the verification it is and its result, and a manual verdict names who gave it in by (#2772).",
83
83
  "type": "array",
84
84
  "items": {
85
85
  "oneOf": [
@@ -88,6 +88,9 @@
88
88
  },
89
89
  {
90
90
  "$ref": "#/definitions/evidencePin"
91
+ },
92
+ {
93
+ "$ref": "#/definitions/evidenceOps"
91
94
  }
92
95
  ]
93
96
  }
@@ -305,6 +308,107 @@
305
308
  ]
306
309
  }
307
310
  },
311
+ "evidenceOps": {
312
+ "description": "An ops result on the chant/lifecycle branch (#3171): a run, a deploy, a gate answer or a release plan, named the way graph --intent's trace names a link. chant workspace work evidence and the workEvidence activity check it against the ledger before writing it, and a cited run's outcome agrees with result.",
313
+ "type": "object",
314
+ "additionalProperties": false,
315
+ "required": [
316
+ "title",
317
+ "ops"
318
+ ],
319
+ "properties": {
320
+ "title": {
321
+ "type": "string",
322
+ "minLength": 1
323
+ },
324
+ "ops": {
325
+ "type": "object",
326
+ "additionalProperties": false,
327
+ "required": [
328
+ "ledger",
329
+ "ref",
330
+ "field",
331
+ "value",
332
+ "sha256"
333
+ ],
334
+ "properties": {
335
+ "ledger": {
336
+ "description": "Which ledger: an Op run, a deploy record, a gate answer or a release plan.",
337
+ "enum": [
338
+ "run",
339
+ "deploy",
340
+ "gate",
341
+ "plan"
342
+ ]
343
+ },
344
+ "ref": {
345
+ "description": "The ledger file, as chant/lifecycle:<path>.",
346
+ "type": "string",
347
+ "pattern": "^chant/lifecycle:"
348
+ },
349
+ "field": {
350
+ "description": "The field of the record that picks it out of the file, such as id, runId, timestamp or digest.",
351
+ "type": "string",
352
+ "minLength": 1
353
+ },
354
+ "value": {
355
+ "description": "That field's value.",
356
+ "type": "string",
357
+ "minLength": 1
358
+ },
359
+ "sha256": {
360
+ "description": "Hex SHA-256 of the record as the ledger stores it: the line, or the plan file.",
361
+ "type": "string",
362
+ "pattern": "^[0-9a-f]{64}$"
363
+ },
364
+ "env": {
365
+ "description": "The environment, for a run or a deploy.",
366
+ "type": "string"
367
+ },
368
+ "outcome": {
369
+ "description": "The run's status, for a run.",
370
+ "enum": [
371
+ "ok",
372
+ "fail"
373
+ ]
374
+ }
375
+ }
376
+ },
377
+ "as_of": {
378
+ "description": "When the result was cited, as an ISO 8601 timestamp.",
379
+ "type": [
380
+ "string",
381
+ "null"
382
+ ]
383
+ },
384
+ "criterion": {
385
+ "description": "The acceptance criterion this evidence is for, by id (#2772).",
386
+ "type": "string",
387
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
388
+ },
389
+ "verification": {
390
+ "$ref": "#/definitions/verification"
391
+ },
392
+ "result": {
393
+ "description": "Whether the evidence shows the criterion met.",
394
+ "enum": [
395
+ "pass",
396
+ "fail"
397
+ ]
398
+ },
399
+ "by": {
400
+ "description": "Who produced the evidence: the lease holder for a steward's, the person for a manual verdict.",
401
+ "type": "string",
402
+ "minLength": 1
403
+ }
404
+ },
405
+ "dependencies": {
406
+ "criterion": [
407
+ "verification",
408
+ "result"
409
+ ]
410
+ }
411
+ },
308
412
  "evidencePin": {
309
413
  "description": "A file in the workspace, pinned by the hash of its bytes. chant workspace records reports a file that changed or went missing since.",
310
414
  "type": "object",
@@ -39,6 +39,8 @@ const JSON_FLAG: Record<ReadContractCommand, string[]> = {
39
39
  "graph --composites": ["--json"],
40
40
  runs: ["--json"],
41
41
  wip: ["--json"],
42
+ search: ["--json"],
43
+ "search --capabilities": ["--json"],
42
44
  };
43
45
 
44
46
  /** The smallest reader that conforms, with a hook to misbehave. */
@@ -65,6 +67,17 @@ describe("the generated conformance workspace (#2679)", () => {
65
67
  expect(report.skipped).toEqual([]);
66
68
  }, 300_000);
67
69
 
70
+ test("search answers over the chant member with workspace graph's ids, and lists the others (#4374)", async () => {
71
+ const docs: Record<string, unknown>[] = [];
72
+ const report = await runWorkspaceReaderConformance(reader((doc) => (docs.push(doc), doc)), { workspaceDir: ws.dir, commands: ["search"] });
73
+ expect(report.problems).toEqual([]);
74
+ const doc = docs[0] as { at: null; matches: { id: string; member: string }[]; members: { name: string; status: string; alone?: boolean }[] };
75
+ expect(doc.at).toBeNull();
76
+ expect(doc.matches.length).toBeGreaterThan(0);
77
+ for (const m of doc.matches) expect(m.id.startsWith(`${m.member}/`), m.id).toBe(true);
78
+ expect(doc.members.find((m) => m.name === "delivery")).toMatchObject({ status: "searched", alone: true });
79
+ }, 300_000);
80
+
68
81
  test("graph --composites joins the fixture's composite to its component", async () => {
69
82
  const docs: Record<string, unknown>[] = [];
70
83
  await runWorkspaceReaderConformance(reader((doc) => (docs.push(doc), doc)), { workspaceDir: ws.dir, commands: ["graph --composites"] });
@@ -120,7 +133,7 @@ describe("commands (#2679)", () => {
120
133
  expect(report.problems).toEqual([]);
121
134
  expect(read).toEqual(["ls", "status", "graph --composites"]);
122
135
  expect(report.checked).toEqual(["ls", "status", "graph --composites"]);
123
- expect(report.skipped).toEqual(["graph", "check", "records", "records --uncommitted", "graph --intent", "runs", "wip"]);
136
+ expect(report.skipped).toEqual(["graph", "check", "records", "records --uncommitted", "graph --intent", "runs", "wip", "search", "search --capabilities"]);
124
137
  }, 300_000);
125
138
 
126
139
  test("a name that is not a contract command, or an empty list, is refused", () => {
@@ -56,7 +56,7 @@ import { isDeepStrictEqual } from "node:util";
56
56
  import { READ_CONTRACT_VERSION } from "../reason-codes";
57
57
 
58
58
  /** The read-contract commands, as a reader names them. */
59
- export const READ_CONTRACT_COMMANDS = ["ls", "graph", "check", "status", "records", "records --uncommitted", "graph --intent", "graph --composites", "runs", "wip"] as const;
59
+ export const READ_CONTRACT_COMMANDS = ["ls", "graph", "check", "status", "records", "records --uncommitted", "graph --intent", "graph --composites", "runs", "wip", "search", "search --capabilities"] as const;
60
60
  export type ReadContractCommand = (typeof READ_CONTRACT_COMMANDS)[number];
61
61
 
62
62
  /**
@@ -75,6 +75,10 @@ export const READ_CONTRACT_SCHEMAS: Record<ReadContractCommand, string> = {
75
75
  "graph --composites": "composites.schema.json",
76
76
  runs: "runs.schema.json",
77
77
  wip: "wip.schema.json",
78
+ // `workspace search` (#4374). Plain `chant search --json` prints the same document for one project.
79
+ search: "search.schema.json",
80
+ // The capability catalog (#4375), from the workspace's chant.
81
+ "search --capabilities": "capabilities.schema.json",
78
82
  };
79
83
 
80
84
  /** The flags that ask each command for its JSON document. A reader adds one of these and nothing else. */
@@ -89,6 +93,8 @@ export const READ_CONTRACT_JSON_FLAGS: Record<ReadContractCommand, readonly (rea
89
93
  "graph --composites": [[], ["--json"]],
90
94
  runs: [["--json"]],
91
95
  wip: [["--json"]],
96
+ search: [["--json"]],
97
+ "search --capabilities": [["--json"]],
92
98
  };
93
99
 
94
100
  /** The arguments the suite passes each read, on the reference workspace or the generated one. */
@@ -103,6 +109,9 @@ export const REFERENCE_READS: Record<ReadContractCommand, string[]> = {
103
109
  "graph --composites": [],
104
110
  runs: [],
105
111
  wip: [],
112
+ // A bare word: on either workspace it searches the chant member, delivery, and lists the others.
113
+ search: ["app"],
114
+ "search --capabilities": [],
106
115
  };
107
116
 
108
117
  /** One chant run: the arguments after `chant`, what it was given on stdin, and what it printed. */
@@ -344,6 +353,8 @@ export const MCP_READ_TOOLS: Partial<Record<ReadContractCommand, string>> = {
344
353
  "graph --intent": "workspace-graph",
345
354
  "graph --composites": "workspace-graph",
346
355
  runs: "workspace-runs",
356
+ search: "workspace-search",
357
+ "search --capabilities": "capabilities",
347
358
  };
348
359
 
349
360
  /**
@@ -359,9 +370,14 @@ export function mcpToolCall(argv: readonly string[]): { name: string; arguments:
359
370
  const args: Record<string, unknown> = {};
360
371
  const positional: string[] = [];
361
372
  const kinds: string[] = [];
373
+ const members: string[] = [];
362
374
  for (let i = 0; i < rest.length; i++) {
363
375
  const a = rest[i];
364
376
  const value = (): string | undefined => rest[++i];
377
+ if (a === "--") {
378
+ positional.push(...rest.slice(i + 1));
379
+ break;
380
+ }
365
381
  if (a === "--json") continue;
366
382
  if (a === "--format" && rest[i + 1] === "json") {
367
383
  i++;
@@ -381,7 +397,14 @@ export function mcpToolCall(argv: readonly string[]): { name: string; arguments:
381
397
  else if (a === "--by") args.by = value();
382
398
  else if (a === "--uncommitted") args.uncommitted = true;
383
399
  else if (a === "--composites") args.composites = true;
384
- else if (a.startsWith("-")) return undefined;
400
+ else if (verb === "search" && a === "--member") members.push(value() ?? "");
401
+ else if (verb === "search" && a === "--env") args.env = value();
402
+ else if (verb === "search" && a === "--snapshot") args.snapshot = value();
403
+ else if (verb === "search" && a === "--count-by") args.countBy = value();
404
+ else if (verb === "search" && a === "--show") args.show = value();
405
+ else if (verb === "search" && a === "--explain") args.explain = true;
406
+ else if (verb === "search" && a === "--capabilities") args.capabilities = true;
407
+ else if (/^-[A-Za-z-]/.test(a)) return undefined;
385
408
  else positional.push(a);
386
409
  }
387
410
  switch (verb) {
@@ -397,6 +420,12 @@ export function mcpToolCall(argv: readonly string[]): { name: string; arguments:
397
420
  return positional.length === 0 ? { name: "workspace-graph", arguments: { ...args, ...(kinds.length ? { kind: kinds } : {}) } } : undefined;
398
421
  case "runs":
399
422
  return positional.length === 0 && kinds.length === 0 ? { name: "workspace-runs", arguments: args } : undefined;
423
+ case "search":
424
+ // The catalog (#4375) is its own tool, and takes nothing.
425
+ if (args.capabilities) return positional.length === 0 && Object.keys(args).length === 1 && kinds.length === 0 && members.length === 0 ? { name: "capabilities", arguments: {} } : undefined;
426
+ return positional.length <= 1 && kinds.length === 0
427
+ ? { name: "workspace-search", arguments: { ...args, query: positional[0] ?? "", ...(members.length ? { member: members } : {}) } }
428
+ : undefined;
400
429
  default:
401
430
  return undefined;
402
431
  }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The disagreement lint (#2549, #2524 D6) in #1939's cross-build-root mode:
3
+ * LNK001 over the composed graph's links, and the cross-root checks the
4
+ * members' lexicons ship.
5
+ */
6
+ import { describe, test, expect } from "vitest";
7
+ import { createCrossRootContext, type CrossRootCheck } from "../lint/post-synth";
8
+ import type { WorkspaceGraph } from "./compose-graph";
9
+ import { crossRootLinks, crossRootLint, lnk001 } from "./cross-root-lint";
10
+ import { crossRootInputs } from "./workspace-run";
11
+
12
+ function member(name: string, status: "composed" | "skipped" = "composed") {
13
+ return { name, dir: name, kind: "chant", status, reason: null, chant: null, irVersion: 1, live: false };
14
+ }
15
+
16
+ const declared = (consumer: string, producer: string, output: string) => ({ consumer, producer, output, kind: "output", origin: "declared", resolves: "source", status: "resolved", label: "exact", input: null, reason: null });
17
+ const inferred = (consumer: string, producer: string, output: string, input: string) => ({ consumer, producer, output, kind: "output", origin: "inferred:joinKey", resolves: "source", status: "resolved", label: "exact", input, reason: null });
18
+
19
+ function graph(links: unknown[]): WorkspaceGraph {
20
+ return {
21
+ version: 1,
22
+ workspace: { name: "acme", root: "." },
23
+ members: [member("network"), member("legacy-net"), member("api")],
24
+ nodes: [
25
+ { id: "network/vpc", kind: "AWS::EC2::VPC", lexicon: "aws", member: "network", attrs: {} },
26
+ { id: "api/fn", kind: "AWS::Lambda::Function", lexicon: "aws", member: "api", attrs: {} },
27
+ { id: "api/svc", kind: "Deployment", lexicon: "k8s", member: "api", attrs: {} },
28
+ ],
29
+ edges: [],
30
+ groups: { byMember: {} },
31
+ exports: [],
32
+ imports: [],
33
+ links,
34
+ collectors: [],
35
+ records: [],
36
+ } as unknown as WorkspaceGraph;
37
+ }
38
+
39
+ describe("LNK001: a declared link and the graph's join disagree", () => {
40
+ test("flags a declaration that names another producer than the one the consumer's graph joins", () => {
41
+ const g = graph([declared("api", "legacy-net", "vpcId"), inferred("api", "network", "VpcId", "vpcId")]);
42
+ const ctx = createCrossRootContext(crossRootInputs(g), { links: crossRootLinks(g.links) });
43
+ const found = lnk001.check(ctx);
44
+ expect(found).toHaveLength(1);
45
+ expect(found[0]).toMatchObject({ checkId: "LNK001", severity: "warning", root: "api", entity: "vpcId" });
46
+ expect(found[0]!.message).toContain("declares a link to legacy-net's output vpcId");
47
+ expect(found[0]!.message).toContain("network's output VpcId");
48
+ });
49
+
50
+ test("agrees when the declaration names the producer the graph joins, or a handle the join doesn't touch", () => {
51
+ for (const links of [
52
+ [declared("api", "network", "subnetIds"), inferred("api", "network", "VpcId", "vpcId")],
53
+ [declared("api", "legacy-net", "dnsZone"), inferred("api", "network", "VpcId", "vpcId")],
54
+ ]) {
55
+ const g = graph(links);
56
+ expect(lnk001.check(createCrossRootContext(crossRootInputs(g), { links: crossRootLinks(g.links) }))).toEqual([]);
57
+ }
58
+ });
59
+
60
+ test("says nothing about a consumer whose graph was not read, or with no links passed", () => {
61
+ const g = graph([declared("api", "legacy-net", "vpcId"), inferred("api", "network", "VpcId", "vpcId")]);
62
+ g.members = g.members.map((m) => (m.name === "api" ? { ...m, status: "skipped" as const } : m));
63
+ expect(lnk001.check(createCrossRootContext(crossRootInputs(g), { links: crossRootLinks(g.links) }))).toEqual([]);
64
+ expect(lnk001.check(createCrossRootContext(crossRootInputs(graph([]))))).toEqual([]);
65
+ });
66
+ });
67
+
68
+ describe("crossRootLint", () => {
69
+ test("runs LNK001 and the cross-root checks of each lexicon the members' graphs name, and reports a lexicon it can't load", async () => {
70
+ const asked: string[] = [];
71
+ const awsCheck: CrossRootCheck = {
72
+ id: "AWSX01",
73
+ description: "sees every root",
74
+ scope: "cross-root",
75
+ check: (ctx) => [{ checkId: "AWSX01", severity: "info", message: `${ctx.roots.length} roots, ${ctx.links?.length ?? 0} links` }],
76
+ };
77
+ const result = await crossRootLint(graph([declared("api", "legacy-net", "vpcId"), inferred("api", "network", "VpcId", "vpcId")]), {
78
+ load: async (lexicon) => {
79
+ asked.push(lexicon);
80
+ if (lexicon === "k8s") throw new Error("Cannot find package '@intentius/chant-lexicon-k8s'");
81
+ return [awsCheck, { id: "PLAIN", description: "per-build, never run here", check: () => [] }];
82
+ },
83
+ });
84
+ expect(asked).toEqual(["aws", "k8s"]);
85
+ expect(result.checks).toEqual(["LNK001", "AWSX01"]);
86
+ expect(result.findings.map((f) => f.checkId)).toEqual(["LNK001", "AWSX01"]);
87
+ expect(result.findings[1]!.message).toBe("3 roots, 2 links");
88
+ expect(result.unloaded).toEqual([{ lexicon: "k8s", reason: "Cannot find package '@intentius/chant-lexicon-k8s'" }]);
89
+ });
90
+ });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * The disagreement lint (#2549, #2524 D6): `chant workspace lint --cross-root`.
3
+ *
4
+ * A member link is written down once, on the consumer. The members' graphs
5
+ * can surface the same relation on their own: a consumer's parameter that
6
+ * joins another member's output by name, or any relation a lexicon puts in
7
+ * its IR. The lint reports where the two disagree.
8
+ *
9
+ * It uses #1939's cross-build-root mode. Each composed member is one build
10
+ * root, read under the member's own toolchain as `chant workspace graph`
11
+ * reads it (`crossRootInputs` in `./workspace-run.ts`), and the composed
12
+ * graph's link table goes to every check as `ctx.links`. The checks are:
13
+ *
14
+ * - LNK001, core's own: the consumer declares a link to one member's output
15
+ * while its graph joins a parameter of the same name to another member's.
16
+ * - every cross-root check a lexicon ships through `crossRootChecks()`, for
17
+ * the lexicons the members' graphs name. A lexicon applies its own rule
18
+ * once it surfaces the relation in its IR. One that can't be loaded here
19
+ * is reported and skipped.
20
+ *
21
+ * Nothing runs unless `--cross-root` asks, so a plain `workspace lint`, and
22
+ * level 0, pay nothing for it.
23
+ */
24
+
25
+ import { joinKey } from "../join-key";
26
+ import type { CrossRootCheck, CrossRootLink, PostSynthCheck, PostSynthDiagnostic } from "../lint/post-synth";
27
+ import { runCrossRootChecks } from "../lint/post-synth";
28
+ import type { WorkspaceGraph } from "./compose-graph";
29
+ import { crossRootInputs } from "./workspace-run";
30
+
31
+ export const LNK001_ID = "LNK001";
32
+
33
+ /** The links of the composed graph, as a cross-root check reads them. Ambiguous rows and record links are left out. */
34
+ export function crossRootLinks(links: WorkspaceGraph["links"]): CrossRootLink[] {
35
+ const out: CrossRootLink[] = [];
36
+ for (const row of links) {
37
+ const r = row as { consumer?: unknown; producer?: unknown; output?: unknown; origin?: unknown; input?: unknown; status?: unknown; kind?: unknown };
38
+ if (typeof r.consumer !== "string" || typeof r.producer !== "string" || typeof r.output !== "string" || typeof r.origin !== "string") continue;
39
+ out.push({
40
+ consumer: r.consumer,
41
+ producer: r.producer,
42
+ output: r.output,
43
+ origin: r.origin,
44
+ ...(typeof r.input === "string" || r.input === null ? { input: r.input as string | null } : {}),
45
+ ...(typeof r.status === "string" ? { status: r.status } : {}),
46
+ });
47
+ }
48
+ return out;
49
+ }
50
+
51
+ /**
52
+ * LNK001: a declared link and a join the graph surfaces name different
53
+ * producers for the same handle. The consumer must be one of the run's roots,
54
+ * so its graph was read.
55
+ */
56
+ export const lnk001: CrossRootCheck = {
57
+ id: LNK001_ID,
58
+ description: "A declared member link and the join the members' graphs surface name different producers for the same output",
59
+ scope: "cross-root",
60
+ check(ctx) {
61
+ const links = ctx.links ?? [];
62
+ const findings: PostSynthDiagnostic[] = [];
63
+ const declared = links.filter((l) => l.origin === "declared" && l.status !== "invalid");
64
+ for (const inferred of links) {
65
+ if (!inferred.origin.startsWith("inferred:") || inferred.status !== "resolved") continue;
66
+ if (!ctx.root(inferred.consumer)) continue;
67
+ const keys = new Set([joinKey(inferred.output), ...(inferred.input ? [joinKey(inferred.input)] : [])]);
68
+ for (const d of declared) {
69
+ if (d.consumer !== inferred.consumer || d.producer === inferred.producer) continue;
70
+ if (!keys.has(joinKey(d.output))) continue;
71
+ findings.push({
72
+ checkId: LNK001_ID,
73
+ severity: "warning",
74
+ message:
75
+ `${d.consumer} declares a link to ${d.producer}'s output ${d.output}, but its graph joins ` +
76
+ `${inferred.input ? `parameter ${inferred.input}` : "a parameter"} to ${inferred.producer}'s output ${inferred.output}. ` +
77
+ `Point the link at the member the graph reads from, or rename the parameter so it joins the declared output.`,
78
+ root: d.consumer,
79
+ entity: inferred.input ?? undefined,
80
+ });
81
+ }
82
+ }
83
+ return findings;
84
+ },
85
+ };
86
+
87
+ /** What a cross-root lint run found. */
88
+ export interface CrossRootLintResult {
89
+ findings: PostSynthDiagnostic[];
90
+ /** The checks that ran, by id. */
91
+ checks: string[];
92
+ /** Lexicons whose cross-root checks could not be loaded here, and why. */
93
+ unloaded: { lexicon: string; reason: string }[];
94
+ }
95
+
96
+ /** Loads a lexicon's cross-root checks; the plugin loader by default. */
97
+ export type CrossRootCheckLoader = (lexicon: string) => Promise<ReadonlyArray<CrossRootCheck | PostSynthCheck>>;
98
+
99
+ async function pluginChecks(lexicon: string): Promise<ReadonlyArray<CrossRootCheck | PostSynthCheck>> {
100
+ const { loadPlugin } = await import("../cli/plugins");
101
+ const plugin = await loadPlugin(lexicon);
102
+ return plugin.crossRootChecks?.() ?? [];
103
+ }
104
+
105
+ /**
106
+ * Run the disagreement lint over a composed graph: one root per composed
107
+ * member, LNK001, and the cross-root checks of every lexicon the roots name.
108
+ */
109
+ export async function crossRootLint(graph: WorkspaceGraph, opts: { env?: string; load?: CrossRootCheckLoader } = {}): Promise<CrossRootLintResult> {
110
+ const roots = crossRootInputs(graph, opts.env);
111
+ const lexicons = [...new Set(roots.flatMap((r) => [...r.buildResult.entities.values()].map((e) => e.lexicon)))]
112
+ .filter((l) => typeof l === "string" && l !== "chant")
113
+ .sort();
114
+ const checks: Array<CrossRootCheck | PostSynthCheck> = [lnk001];
115
+ const unloaded: CrossRootLintResult["unloaded"] = [];
116
+ for (const lexicon of lexicons) {
117
+ try {
118
+ checks.push(...(await (opts.load ?? pluginChecks)(lexicon)));
119
+ } catch (err) {
120
+ unloaded.push({ lexicon, reason: err instanceof Error ? err.message.split("\n")[0]! : String(err) });
121
+ }
122
+ }
123
+ const findings = runCrossRootChecks(checks, roots, { ...(opts.env ? { env: opts.env } : {}), links: crossRootLinks(graph.links) });
124
+ const ran = checks.filter((c) => (c as CrossRootCheck).scope === "cross-root").map((c) => c.id);
125
+ return { findings, checks: ran, unloaded };
126
+ }
@@ -75,8 +75,9 @@ describe("the dbt kind in the sql lexicon's kinds file (#3932)", () => {
75
75
  test("validates, probes dbt_project.yml and names the dbt-project reader", () => {
76
76
  const { kinds, problems } = parseKindData(SQL_KINDS, "@intentius/chant-lexicon-sql");
77
77
  expect(problems).toEqual([]);
78
- expect(kinds.map((k) => [k.name, k.reader, k.probe])).toEqual([["dbt", "dbt-project", { anyFile: ["dbt_project.yml"] }]]);
79
- expect(Object.keys(kinds[0].fields ?? {})).toEqual(["sqlMember", "dialect"]);
78
+ expect(kinds.filter((k) => k.name === "dbt").map((k) => [k.name, k.reader, k.probe])).toEqual([["dbt", "dbt-project", { anyFile: ["dbt_project.yml"] }]]);
79
+ expect(kinds.map((k) => [k.name, k.reader])).toEqual([["dbt", "dbt-project"], ["goose", "goose-dir"], ["golang-migrate", "golang-migrate-dir"], ["clickhouse-migrations", "clickhouse-migrations-dir"], ["flyway", "flyway-dir"]]);
80
+ expect(Object.keys(kinds[0].fields ?? {})).toEqual(["sqlMember", "dialect", "modelSchema"]);
80
81
  });
81
82
 
82
83
  test("a reader this chant doesn't have keeps the kind without one, and says so", () => {