@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
@@ -0,0 +1,107 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import Ajv2020 from "ajv/dist/2020";
6
+ import { afterAll, describe, expect, it } from "vitest";
7
+ import type { LedgerReadPort } from "./ledger-port";
8
+ import { ledgerFields, readLedgerTable, resolveLedgerRevision, revisionLedgerReadPort, LedgerRevisionError } from "./ledger-read";
9
+
10
+ const validate = new Ajv2020({ strict: false, allErrors: true }).compile(JSON.parse(readFileSync(join(__dirname, "..", "workspace", "ledger.schema.json"), "utf-8")) as object);
11
+ const valid = (doc: unknown) => {
12
+ const ok = validate(doc);
13
+ if (!ok) throw new Error(JSON.stringify(validate.errors));
14
+ return ok;
15
+ };
16
+
17
+ /** A port over files held in memory: `{ "<dir>/<file>": text }`. */
18
+ function memoryPort(files: Record<string, string>): LedgerReadPort {
19
+ return {
20
+ read: async (dir, file) => files[`${dir}/${file}`] ?? null,
21
+ list: async (dir) => Object.keys(files).filter((k) => k.startsWith(`${dir}/`)).map((k) => k.slice(dir.length + 1)),
22
+ trees: async () => [...new Set(Object.keys(files).map((k) => k.split("/")[0]))],
23
+ };
24
+ }
25
+
26
+ const pending = { version: 1, kind: "pending", op: "tf-apply", gate: "wave-1", timestamp: "2026-10-09T05:00:00.000Z", expiresAt: "2026-10-10T05:00:00.000Z", planDigest: "sha256:aa" };
27
+ const approval = { version: 1, op: "tf-apply", gate: "wave-1", timestamp: "2026-10-09T05:10:00.000Z", resolvedBy: "approver-one", planDigest: "sha256:aa" };
28
+ const other = { version: 1, kind: "pending", op: "deploy", gate: "prod", timestamp: "2026-10-20T05:00:00.000Z", expiresAt: "2026-10-21T05:00:00.000Z", planDigest: "sha256:bb" };
29
+ const gates = { "_gates/tf-apply.jsonl": `${JSON.stringify(pending)}\n${JSON.stringify(approval)}\nnot json\n`, "_gates/deploy.jsonl": `${JSON.stringify(other)}\n` };
30
+ const now = new Date("2026-10-23T00:00:00.000Z");
31
+
32
+ describe("chant ledger (#4363)", () => {
33
+ it("prints a table's rows as chant query loads them, filtered by --where", async () => {
34
+ const doc = await readLedgerTable("gates", [{ member: null, port: memoryPort(gates) }], { where: ["op=tf-apply", "kind=resolution"], now });
35
+ expect(valid(doc)).toBe(true);
36
+ if ("error" in doc) throw new Error(doc.error.message);
37
+ expect(doc.total).toBe(3);
38
+ expect(doc.malformed).toBe(1);
39
+ expect(doc.filter).toEqual({ where: ["op=tf-apply", "kind=resolution"], since: null });
40
+ expect(doc.rows).toHaveLength(1);
41
+ expect(doc.rows[0]).toMatchObject({ member: null, op: "tf-apply", gate: "wave-1", resolved_by: "approver-one", plan_digest: "sha256:aa", signed: false, empty: false });
42
+ expect(Object.keys(doc.rows[0])).toEqual(doc.columns);
43
+ });
44
+
45
+ it("keeps the rows at or after --since on the table's time column", async () => {
46
+ const doc = await readLedgerTable("gates", [{ member: null, port: memoryPort(gates) }], { since: "7d", now });
47
+ if ("error" in doc) throw new Error(doc.error.message);
48
+ expect(doc.filter.since).toBe("2026-10-16T00:00:00.000Z");
49
+ expect(doc.rows.map((r) => r.op)).toEqual(["deploy"]);
50
+ });
51
+
52
+ it("prints a JSON column parsed, and filters on a key under it", async () => {
53
+ const run = { version: 1, id: "r1", op: "tf-apply", env: "prod", started: "2026-10-22T00:00:00.000Z", ended: "2026-10-22T00:01:00.000Z", status: "succeeded", phases: [], labels: { team: "core" }, outcomes: {} };
54
+ const port = memoryPort({ "prod/runs__tf-apply.jsonl": `${JSON.stringify(run)}\n` });
55
+ const doc = await readLedgerTable("runs", [{ member: "app", port }], { where: ["labels.team=core"], now });
56
+ expect(valid(doc)).toBe(true);
57
+ if ("error" in doc) throw new Error(doc.error.message);
58
+ expect(doc.rows).toHaveLength(1);
59
+ expect(doc.rows[0]).toMatchObject({ member: "app", id: "r1", labels: { team: "core" } });
60
+ });
61
+
62
+ it("refuses an unknown table, a bad filter, and --since on a table with no time, without reading", async () => {
63
+ const never = async (): Promise<never> => {
64
+ throw new Error("read");
65
+ };
66
+ for (const [table, opts, code] of [
67
+ ["states", {}, "table-unknown"],
68
+ ["gates", { where: ["approver=x"] }, "filter-invalid"],
69
+ ["snapshots", { where: ["resources>many"] }, "filter-invalid"],
70
+ ["releases", { since: "7d" }, "filter-invalid"],
71
+ ] as const) {
72
+ const doc = await readLedgerTable(table, never, opts);
73
+ expect(valid(doc)).toBe(true);
74
+ expect(doc).toMatchObject({ error: { code } });
75
+ }
76
+ });
77
+
78
+ it("types each table's columns from chant query's schema", () => {
79
+ expect(ledgerFields("snapshots")).toMatchObject({ resources: "number", observed_at: "time", lexicon: "text" });
80
+ expect(ledgerFields("cuts")).toMatchObject({ signed: "boolean", timestamp: "time" });
81
+ expect(ledgerFields("resources").attributes).toBe("json");
82
+ });
83
+
84
+ describe("--at", () => {
85
+ const repo = mkdtempSync(join(tmpdir(), "chant-ledger-at-"));
86
+ afterAll(() => rmSync(repo, { recursive: true, force: true }));
87
+ const git = (...a: string[]): string => execFileSync("git", ["-C", repo, "-c", "user.name=t", "-c", "user.email=t@t", "-c", "commit.gpgsign=false", ...a], { encoding: "utf-8" });
88
+
89
+ it("reads the ledger as a commit of chant/lifecycle held it", async () => {
90
+ git("init", "-q", "-b", "chant/lifecycle");
91
+ mkdirSync(join(repo, "_gates"));
92
+ writeFileSync(join(repo, "_gates", "tf-apply.jsonl"), `${JSON.stringify(pending)}\n`);
93
+ git("add", "-A");
94
+ git("commit", "-q", "-m", "one");
95
+ writeFileSync(join(repo, "_gates", "tf-apply.jsonl"), `${JSON.stringify(pending)}\n${JSON.stringify(approval)}\n`);
96
+ git("commit", "-q", "-am", "two");
97
+ const at = (rev: string) => readLedgerTable("gates", [{ member: null, port: revisionLedgerReadPort(repo, resolveLedgerRevision(repo, rev), "") }], { at: rev });
98
+ const then = await at("chant/lifecycle~1");
99
+ const tip = await at("chant/lifecycle");
100
+ if ("error" in then || "error" in tip) throw new Error("read failed");
101
+ expect(then.at).toBe("chant/lifecycle~1");
102
+ expect(then.rows.map((r) => r.kind)).toEqual(["pending"]);
103
+ expect(tip.rows.map((r) => r.kind)).toEqual(["pending", "resolution"]);
104
+ expect(() => resolveLedgerRevision(repo, "no-such-rev")).toThrow(LedgerRevisionError);
105
+ });
106
+ });
107
+ });
@@ -0,0 +1,195 @@
1
+ /**
2
+ * `chant ledger <table>` (#4363): the rows of one ledger on `chant/lifecycle`
3
+ * as a JSON read contract (`workspace/ledger.schema.json`), filtered with
4
+ * `--where`, `--since` and `--at` instead of SQL.
5
+ *
6
+ * The tables, their columns and the rows are the ones `chant query` loads
7
+ * (`./query.ts` {@link QUERY_TABLES}, {@link loadQueryData}), so the two
8
+ * agree on every row. A column `chant query` keeps as JSON text is an object
9
+ * or array here. No SQL engine loads.
10
+ *
11
+ * `--since <time>` keeps the rows whose time column ({@link LEDGER_TIME_COLUMN})
12
+ * is at or after it. `--at <rev>` reads the ledger as the commit `rev` of
13
+ * `chant/lifecycle` held it ({@link revisionLedgerReadPort}).
14
+ *
15
+ * Everything here but {@link revisionLedgerReadPort} is pure over a
16
+ * {@link LedgerReadPort}, so a product that reads the branch its own way
17
+ * (terragucci, behold) supplies its own port.
18
+ */
19
+ import { execFileSync } from "node:child_process";
20
+ import { FilterError, matchesWhere, parseSince, parseWhere, type FilterFields, type FilterFieldType } from "../read-filter";
21
+ import type { ReasonCode } from "../workspace/reason-codes";
22
+ import type { LedgerReadPort } from "./ledger-port";
23
+ import { loadQueryData, QUERY_TABLES, type QueryLedger, type QueryRow, type QueryTable } from "./query";
24
+
25
+ /** The version of the `ledger` document this chant writes. */
26
+ export const LEDGER_READ_CONTRACT_VERSION = 1;
27
+
28
+ /** `$id` of the JSON Schema for `chant ledger --json`. */
29
+ export const LEDGER_READ_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/ledger/v1/ledger.schema.json";
30
+
31
+ /** The tables, in the order `chant query` lists them. */
32
+ export const LEDGER_TABLES = Object.keys(QUERY_TABLES) as QueryTable[];
33
+
34
+ /** The column `--since` compares, per table; null for a table with no time of its own. */
35
+ export const LEDGER_TIME_COLUMN: Record<QueryTable, string | null> = {
36
+ resources: "observed_at",
37
+ snapshots: "observed_at",
38
+ runs: "started",
39
+ gates: "timestamp",
40
+ deploys: "timestamp",
41
+ releases: null,
42
+ cuts: "timestamp",
43
+ };
44
+
45
+ /** The columns that hold JSON, which this read prints parsed. */
46
+ const JSON_COLUMNS = new Set(["attributes", "labels", "outcomes", "promoted_from", "restores", "redeploys", "artifacts"]);
47
+ /** The columns that hold an ISO instant. */
48
+ const TIME_COLUMNS = new Set(["last_updated", "observed_at", "started", "ended", "timestamp", "expires_at"]);
49
+ /** INTEGER columns that hold 0 or 1, which this read prints as booleans. */
50
+ const BOOLEAN_COLUMNS = new Set(["signed", "empty"]);
51
+
52
+ /** The fields `--where` takes on a table: its columns, typed. */
53
+ export function ledgerFields(table: QueryTable): FilterFields {
54
+ const out: Record<string, FilterFieldType> = {};
55
+ for (const [name, sqlType] of QUERY_TABLES[table]) {
56
+ out[name] = BOOLEAN_COLUMNS.has(name) ? "boolean" : sqlType === "INTEGER" ? "number" : JSON_COLUMNS.has(name) ? "json" : TIME_COLUMNS.has(name) ? "time" : "text";
57
+ }
58
+ return out;
59
+ }
60
+
61
+ export type LedgerValue = string | number | boolean | null | Record<string, unknown> | unknown[];
62
+ export type LedgerRow = Record<string, LedgerValue>;
63
+
64
+ /** The row as the contract prints it: JSON columns parsed, 0/1 columns as booleans. */
65
+ function contractRow(table: QueryTable, row: QueryRow): LedgerRow {
66
+ const out: LedgerRow = {};
67
+ for (const [name] of QUERY_TABLES[table]) {
68
+ const v = row[name] ?? null;
69
+ if (v !== null && JSON_COLUMNS.has(name) && typeof v === "string") {
70
+ try {
71
+ out[name] = JSON.parse(v) as LedgerValue;
72
+ } catch {
73
+ out[name] = v;
74
+ }
75
+ } else if (BOOLEAN_COLUMNS.has(name) && typeof v === "number") out[name] = v === 1;
76
+ else out[name] = v;
77
+ }
78
+ return out;
79
+ }
80
+
81
+ export interface LedgerReadOptions {
82
+ where?: readonly string[];
83
+ /** An ISO date or time, or a span back from now such as `7d`. */
84
+ since?: string;
85
+ /** The revision of `chant/lifecycle` the ports read, as given; only echoed here. */
86
+ at?: string | null;
87
+ now?: Date;
88
+ }
89
+
90
+ export type LedgerReadDocument =
91
+ | {
92
+ $schema: string;
93
+ contract: number;
94
+ table: QueryTable;
95
+ /** The table's columns, in order. */
96
+ columns: string[];
97
+ at: string | null;
98
+ filter: { where: string[]; since: string | null };
99
+ /** Rows in the table before the filter. */
100
+ total: number;
101
+ rows: LedgerRow[];
102
+ /** Lines and files of this table that could not be read as their kind, left out. */
103
+ malformed: number;
104
+ }
105
+ | { $schema: string; contract: number; error: { code: LedgerReadErrorCode; message: string } };
106
+
107
+ /** Why the read printed no rows. Closed. */
108
+ export const LEDGER_READ_ERROR_CODES = ["filter-invalid", "table-unknown", "revision-unknown", "ledger-unreadable"] as const satisfies readonly ReasonCode[];
109
+ export type LedgerReadErrorCode = (typeof LEDGER_READ_ERROR_CODES)[number];
110
+
111
+ const head = { $schema: LEDGER_READ_SCHEMA_ID, contract: LEDGER_READ_CONTRACT_VERSION };
112
+
113
+ /** A failure document. */
114
+ export function ledgerReadFailure(code: LedgerReadErrorCode, message: string): LedgerReadDocument {
115
+ return { ...head, error: { code, message } };
116
+ }
117
+
118
+ /** Read one table from the ledgers and filter it. A bad table or filter is a failure document, before anything is read. */
119
+ export async function readLedgerTable(table: string, ledgers: readonly QueryLedger[] | (() => Promise<readonly QueryLedger[]>), opts: LedgerReadOptions = {}): Promise<LedgerReadDocument> {
120
+ if (!(LEDGER_TABLES as string[]).includes(table)) return ledgerReadFailure("table-unknown", `no table ${JSON.stringify(table)}; the tables are ${LEDGER_TABLES.join(", ")}`);
121
+ const t = table as QueryTable;
122
+ let clauses;
123
+ let since: string | null = null;
124
+ try {
125
+ clauses = parseWhere(opts.where ?? [], ledgerFields(t), opts.now);
126
+ if (opts.since !== undefined) {
127
+ if (LEDGER_TIME_COLUMN[t] === null) throw new FilterError(`--since: ${t} has no time column; filter it with --where`);
128
+ since = parseSince(opts.since, opts.now);
129
+ }
130
+ } catch (err) {
131
+ if (err instanceof FilterError) return ledgerReadFailure("filter-invalid", err.message);
132
+ throw err;
133
+ }
134
+ const load = await loadQueryData(typeof ledgers === "function" ? await ledgers() : ledgers);
135
+ const all = load.data[t].map((r) => contractRow(t, r));
136
+ const timeCol = LEDGER_TIME_COLUMN[t];
137
+ const sinceMs = since === null ? null : Date.parse(since);
138
+ const rows = all.filter((r) => {
139
+ if (sinceMs !== null && timeCol !== null) {
140
+ const v = r[timeCol];
141
+ if (typeof v !== "string" || !(Date.parse(v) >= sinceMs)) return false;
142
+ }
143
+ return matchesWhere(r, clauses);
144
+ });
145
+ return {
146
+ ...head,
147
+ table: t,
148
+ columns: QUERY_TABLES[t].map(([c]) => c),
149
+ at: opts.at ?? null,
150
+ filter: { where: [...(opts.where ?? [])], since },
151
+ total: all.length,
152
+ rows,
153
+ malformed: load.malformed[t],
154
+ };
155
+ }
156
+
157
+ /** Thrown by {@link resolveLedgerRevision} for a revision git can't resolve. */
158
+ export class LedgerRevisionError extends Error {}
159
+
160
+ /** The commit `rev` names in the repository at `cwd`. Throws a {@link LedgerRevisionError}. */
161
+ export function resolveLedgerRevision(cwd: string, rev: string): string {
162
+ try {
163
+ return execFileSync("git", ["rev-parse", "--verify", "--quiet", `${rev}^{commit}`], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
164
+ } catch {
165
+ throw new LedgerRevisionError(`--at ${rev} names no commit here; give a commit of chant/lifecycle, such as chant/lifecycle~3`);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * A port over the ledger as the commit `commit` holds it, under `prefix`
171
+ * (`""`, or `_members/<member>/` per level). It never fetches.
172
+ */
173
+ export function revisionLedgerReadPort(cwd: string, commit: string, prefix: string): LedgerReadPort {
174
+ const git = (args: string[]): string | null => {
175
+ try {
176
+ return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 256 * 1024 * 1024 });
177
+ } catch {
178
+ return null;
179
+ }
180
+ };
181
+ const lsTree = (path: string, nameOnly: boolean): string[] => {
182
+ const spec = path === "" ? commit : `${commit}:${path}`;
183
+ const out = git(["ls-tree", "--full-tree", ...(nameOnly ? ["--name-only"] : []), spec]);
184
+ return out === null ? [] : out.split("\n").filter(Boolean);
185
+ };
186
+ return {
187
+ read: async (dir, file) => git(["show", `${commit}:${prefix}${dir}/${file}`]),
188
+ list: async (dir) => lsTree(`${prefix}${dir}`, true),
189
+ trees: async () =>
190
+ lsTree(prefix.replace(/\/$/, ""), false)
191
+ .map((line) => /^\d+\s+(\w+)\s+[0-9a-f]+\t(.+)$/.exec(line))
192
+ .filter((m): m is RegExpExecArray => m !== null && m[1] === "tree")
193
+ .map((m) => m[2]),
194
+ };
195
+ }
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi } from "vitest";
2
- import { observeResources, collectAmbient } from "./observe";
2
+ import { observeResources, collectAmbient, carryAnnotations } from "./observe";
3
3
  import { observation } from "../observation";
4
4
  import type { ObservationLexicon, ResourceMetadata } from "../lexicon";
5
5
  import type { BuildResult } from "../build";
@@ -59,6 +59,32 @@ describe("observeResources", () => {
59
59
  expect(Object.keys(observations[0].resources)).toEqual(["web-vpc"]);
60
60
  });
61
61
 
62
+ it("returns what a lexicon learned about an absent entity as annotations, outside the observations (#4372)", async () => {
63
+ const plugins = [
64
+ {
65
+ name: "aws",
66
+ serializer: {} as ObservationLexicon["serializer"],
67
+ describeResources: async () =>
68
+ observation({}, undefined, undefined, undefined, undefined, undefined, { "web-vpc": { planAction: "create" } }),
69
+ } as unknown as ObservationLexicon,
70
+ ];
71
+ const { observations, annotations } = await observeResources("prod", plugins, mockBuild());
72
+ // Absent: no observation row anywhere, so nothing that classifies sees it.
73
+ expect(observations.flatMap((o) => Object.keys(o.resources))).toEqual([]);
74
+ expect(annotations).toEqual({ "web-vpc": { planAction: "create" } });
75
+ });
76
+
77
+ it("carryAnnotations sets attributes by id and touches nothing else (#4372)", () => {
78
+ const nodes = [
79
+ { id: "a", kind: "K", lexicon: "x", attrs: { _status: "accent", name: "a" } },
80
+ { id: "b", kind: "K", lexicon: "x", attrs: { _status: "good" } },
81
+ ];
82
+ const out = carryAnnotations(nodes, { a: { planAction: "create" } });
83
+ expect(out[0]).toEqual({ id: "a", kind: "K", lexicon: "x", attrs: { _status: "accent", name: "a", planAction: "create" } });
84
+ expect(out[1]).toBe(nodes[1]);
85
+ expect(carryAnnotations(nodes, undefined)).toBe(nodes);
86
+ });
87
+
62
88
  it("passes the declared entity names for scoping", async () => {
63
89
  let names: string[] = [];
64
90
  const plugins = [awsPlugin(({ entityNames }) => { names = entityNames; return {}; })];
@@ -22,6 +22,7 @@ import {
22
22
  unobservedAll,
23
23
  formatUnobserved,
24
24
  type NormalizedObservation,
25
+ type RootRead,
25
26
  } from "../observation";
26
27
  import { zeroResourcesWarning } from "../live-endpoint";
27
28
  import { unqualifiedKey } from "./identity";
@@ -39,6 +40,40 @@ export interface ObserveResult {
39
40
  * answer, not ahead of it.
40
41
  */
41
42
  notes: string[];
43
+ /**
44
+ * Facts about declared entities beside their verdict (#4372), keyed like
45
+ * the observations' resources (stack-qualified where those are). A planned
46
+ * create is the case: absent, with the plan's attributes. Never part of
47
+ * `observations`, so nothing that classifies or paints presence sees it;
48
+ * {@link carryAnnotations} puts it on the declared nodes of an overlay.
49
+ */
50
+ annotations?: Record<string, Record<string, unknown>>;
51
+ /**
52
+ * Which read answered each declared entity (#2267), keyed like the
53
+ * observations' resources: `"state"`, `"live"`, `"plan"` or `"live+plan"`
54
+ * from the terraform lexicon. Present only for lexicons that report it.
55
+ */
56
+ sources?: Record<string, string>;
57
+ /** Which read answered each root, and why it fell back (structured, from the lexicon). */
58
+ rootReads?: Record<string, RootRead>;
59
+ }
60
+
61
+ /**
62
+ * Put each entity's annotations (#4372) on its node, by id. Only attributes
63
+ * are touched: `_status`, `physicalId` and `ownership` stay what the overlay
64
+ * decided, so a planned create is still painted pending and still absent to
65
+ * every consumer that asks. An annotation wins a collision with a declared
66
+ * attribute, the same precedence an observed attribute has in the overlay.
67
+ */
68
+ export function carryAnnotations<N extends { id: string; attrs: Record<string, unknown> }>(
69
+ nodes: N[],
70
+ annotations: Record<string, Record<string, unknown>> | undefined,
71
+ ): N[] {
72
+ if (!annotations || Object.keys(annotations).length === 0) return nodes;
73
+ return nodes.map((n) => {
74
+ const a = annotations[n.id];
75
+ return a && Object.keys(a).length > 0 ? { ...n, attrs: { ...n.attrs, ...a } } : n;
76
+ });
42
77
  }
43
78
 
44
79
  /**
@@ -57,6 +92,7 @@ function qualifyObservation(obs: NormalizedObservation, stackName: string): Norm
57
92
  queried: q(obs.queried),
58
93
  // Which read answered (#2267) is per entity, so it re-keys with them.
59
94
  sources: q(obs.sources),
95
+ ...(obs.annotations ? { annotations: q(obs.annotations) } : {}),
60
96
  notes: obs.notes,
61
97
  // Exports are already keyed by stack (#1279); nothing to qualify.
62
98
  ...(obs.stackExports ? { stackExports: obs.stackExports } : {}),
@@ -131,6 +167,9 @@ export async function observeResources(
131
167
  const warnings: string[] = [];
132
168
  const errors: string[] = [];
133
169
  const notes: string[] = [];
170
+ const annotations: Record<string, Record<string, unknown>> = {};
171
+ const sources: Record<string, string> = {};
172
+ const rootReads: Record<string, RootRead> = {};
134
173
 
135
174
  for (const plugin of plugins) {
136
175
  if (!plugin.describeResources) continue;
@@ -230,6 +269,9 @@ export async function observeResources(
230
269
  const line = `[${plugin.name}] ${note}`;
231
270
  if (!notes.includes(line)) notes.push(line);
232
271
  }
272
+ Object.assign(annotations, observed.annotations ?? {});
273
+ Object.assign(sources, observed.sources);
274
+ Object.assign(rootReads, observed.rootReads ?? {});
233
275
  // What the estate depends on but does not declare (#1273). Read after the
234
276
  // managed resources, because the declared observation is the closure's
235
277
  // roots — there is nothing to reference out from until it exists.
@@ -299,7 +341,7 @@ export async function observeResources(
299
341
  }
300
342
  }
301
343
 
302
- return { observations, warnings, errors, notes };
344
+ return { observations, warnings, errors, notes, annotations, ...(Object.keys(sources).length > 0 ? { sources } : {}), ...(Object.keys(rootReads).length > 0 ? { rootReads } : {}) };
303
345
  }
304
346
 
305
347
  /**
@@ -70,6 +70,30 @@ export function resolveTrunk(cwd: string, greenBranch: string | null): Trunk {
70
70
  return { remote: RELEASE_REMOTE, branch: "main", from: "default" };
71
71
  }
72
72
 
73
+ /**
74
+ * {@link checkOnTrunk} across the branches `ci.green` judges (#4325): the
75
+ * trunk `commit` is on, `trunk` itself or one of `others` (the declaration's
76
+ * further green branches, which `ci.branches` routes members to). A commit on
77
+ * none of them is refused with the first trunk's error.
78
+ */
79
+ export function checkOnGreenBranches(cwd: string, commit: string, trunk: Trunk, others: readonly string[]): Trunk {
80
+ try {
81
+ checkOnTrunk(cwd, commit, trunk);
82
+ return trunk;
83
+ } catch (err) {
84
+ for (const branch of others) {
85
+ const routed: Trunk = { ...trunk, branch };
86
+ try {
87
+ checkOnTrunk(cwd, commit, routed);
88
+ return routed;
89
+ } catch {
90
+ // Not on this one either; the next, then the first branch's error.
91
+ }
92
+ }
93
+ throw err;
94
+ }
95
+ }
96
+
73
97
  /**
74
98
  * Refuse `commit` unless it is reachable from `<remote>/<branch>` as this
75
99
  * clone knows it. A commit on the local branch only is `release-commit-not-pushed`;
@@ -39,7 +39,7 @@ import { sortedJsonReplacer } from "../utils";
39
39
  import type { OpConfig } from "../op/types";
40
40
  import type { StepRecord } from "../op/local-executor";
41
41
  import { opRunFailureOf, type OpRunRecord, type OpRunRecordInput, type OpRunPhaseRecord, type OpRunStepRecord } from "../op/runtime";
42
- import { readBlobFromPath, readPathSha, readBlobBySha, writeBlobToPath, RefCASConflictError } from "./git";
42
+ import { readBlobFromPath, readPathSha, readBlobBySha, writeBlobToPath, RefCASConflictError, type LedgerLocation } from "./git";
43
43
 
44
44
  /** Prefix folding the per-op run ledgers into one flat name under `<env>/`. See the module doc. */
45
45
  const PREFIX = "runs__";
@@ -95,6 +95,8 @@ export function buildRunRecord(
95
95
  runOrigin?: OpRunRecord["runOrigin"];
96
96
  /** Each phase's wall-clock time, by name, as the executor measured it. */
97
97
  phaseDurations?: Record<string, number>;
98
+ /** The workspace run that started this one (#2554). */
99
+ workspaceRun?: OpRunRecord["workspaceRun"];
98
100
  },
99
101
  ): OpRunRecordInput {
100
102
  const phases: OpRunPhaseRecord[] = [];
@@ -145,6 +147,7 @@ export function buildRunRecord(
145
147
  ...(times.resumed ? { resumed: times.resumed } : {}),
146
148
  ...(times.gitSha ? { gitSha: times.gitSha } : {}),
147
149
  ...(times.runOrigin ? { runOrigin: times.runOrigin } : {}),
150
+ ...(times.workspaceRun ? { workspaceRun: times.workspaceRun } : {}),
148
151
  };
149
152
  }
150
153
 
@@ -193,7 +196,7 @@ export async function appendRunRecord(
193
196
  export async function readRunLedger(
194
197
  environment: string,
195
198
  op: string,
196
- opts?: { cwd?: string },
199
+ opts?: LedgerLocation,
197
200
  ): Promise<{ records: OpRunRecord[]; malformed: number }> {
198
201
  const content = await readBlobFromPath(environment, fileFor(op), opts);
199
202
  if (!content) return { records: [], malformed: 0 };
@@ -0,0 +1,122 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import {
3
+ runPostSynthChecks,
4
+ runCrossRootChecks,
5
+ createCrossRootContext,
6
+ isPostSynthCheck,
7
+ isCrossRootCheck,
8
+ type CrossRootCheck,
9
+ type CrossRootInput,
10
+ type PostSynthCheck,
11
+ } from "./post-synth";
12
+
13
+ function buildResult(outputs: Record<string, string> = {}, entities: Record<string, unknown> = {}) {
14
+ const out = new Map(Object.entries(outputs));
15
+ return {
16
+ outputs: out,
17
+ entities: new Map(Object.entries(entities)) as Map<string, never>,
18
+ warnings: [] as string[],
19
+ errors: [] as Array<{ message: string; name: string }>,
20
+ sourceFileCount: 0,
21
+ };
22
+ }
23
+
24
+ const secretRoot: CrossRootInput = {
25
+ root: "workloads",
26
+ buildResult: buildResult({ k8s: "kind: Secret\nmetadata:\n name: db\n" }, { dbSecret: {} }),
27
+ };
28
+ const fluxRoot: CrossRootInput = {
29
+ root: "flux",
30
+ dir: "/repo/flux",
31
+ buildResult: buildResult({ k8s: "kind: Kustomization\nmetadata:\n name: apps\n---\nkind: GitRepository\n" }),
32
+ };
33
+
34
+ const kindsAcross: CrossRootCheck = {
35
+ id: "XR001",
36
+ description: "records kinds across roots",
37
+ scope: "cross-root",
38
+ check(ctx) {
39
+ return ctx.docs.map((d) => ({
40
+ checkId: "XR001",
41
+ severity: "info" as const,
42
+ message: String((d.value as { kind?: string }).kind),
43
+ root: d.root,
44
+ }));
45
+ },
46
+ };
47
+
48
+ describe("cross-root post-synth checks (chant #1939)", () => {
49
+ test("a per-build run never calls a cross-root check", () => {
50
+ let called = false;
51
+ const xr = { ...kindsAcross, check: () => { called = true; return []; } };
52
+ const diags = runPostSynthChecks([xr as unknown as PostSynthCheck], buildResult());
53
+ expect(diags).toEqual([]);
54
+ expect(called).toBe(false);
55
+ });
56
+
57
+ test("guards keep the two kinds apart", () => {
58
+ const plain: PostSynthCheck = { id: "P", description: "p", check: () => [] };
59
+ expect(isPostSynthCheck(kindsAcross)).toBe(false);
60
+ expect(isCrossRootCheck(kindsAcross)).toBe(true);
61
+ expect(isPostSynthCheck(plain)).toBe(true);
62
+ expect(isCrossRootCheck(plain)).toBe(false);
63
+ });
64
+
65
+ test("docs are the union across roots, tagged by root", () => {
66
+ const diags = runCrossRootChecks([kindsAcross], [secretRoot, fluxRoot]);
67
+ expect(diags.map((d) => `${d.root}:${d.message}`)).toEqual([
68
+ "workloads:Secret",
69
+ "flux:Kustomization",
70
+ "flux:GitRepository",
71
+ ]);
72
+ });
73
+
74
+ test("a cross-root check can see that the other half lives in another root", () => {
75
+ const needsDecryption: CrossRootCheck = {
76
+ id: "XR002",
77
+ description: "a Secret needs a Kustomization in some root",
78
+ scope: "cross-root",
79
+ check(ctx) {
80
+ const hasKs = ctx.docs.some((d) => (d.value as { kind?: string }).kind === "Kustomization");
81
+ return ctx.docs
82
+ .filter((d) => (d.value as { kind?: string }).kind === "Secret" && !hasKs)
83
+ .map((d) => ({ checkId: "XR002", severity: "warning" as const, message: "no Kustomization in any root", root: d.root }));
84
+ },
85
+ };
86
+ expect(runCrossRootChecks([needsDecryption], [secretRoot, fluxRoot])).toEqual([]);
87
+ expect(runCrossRootChecks([needsDecryption], [secretRoot])).toEqual([
88
+ { checkId: "XR002", severity: "warning", message: "no Kustomization in any root", root: "workloads" },
89
+ ]);
90
+ });
91
+
92
+ test("roots stay apart and each is a PostSynthContext", () => {
93
+ const ctx = createCrossRootContext([secretRoot, fluxRoot], { env: "prod" });
94
+ expect(ctx.roots.map((r) => r.root)).toEqual(["workloads", "flux"]);
95
+ expect(ctx.root("flux")?.dir).toBe("/repo/flux");
96
+ expect(ctx.root("flux")?.projectDir).toBe("/repo/flux");
97
+ expect(ctx.root("flux")?.docs).toHaveLength(2);
98
+ expect(ctx.root("workloads")?.env).toBe("prod");
99
+ expect(ctx.root("missing")).toBeUndefined();
100
+ expect(ctx.entities).toEqual([{ root: "workloads", name: "dbSecret", entity: {} }]);
101
+ expect(ctx.env).toBe("prod");
102
+ });
103
+
104
+ test("the union is parsed lazily, once", () => {
105
+ const ctx = createCrossRootContext([secretRoot, fluxRoot]);
106
+ expect(ctx.docs).toBe(ctx.docs);
107
+ expect(ctx.entities).toBe(ctx.entities);
108
+ });
109
+
110
+ test("plain checks in the list are skipped, and nothing is built when none is cross-root", () => {
111
+ let called = false;
112
+ const plain: PostSynthCheck = { id: "P", description: "p", check: () => { called = true; return []; } };
113
+ // Duplicate root names would throw if a context were built.
114
+ expect(runCrossRootChecks([plain], [secretRoot, secretRoot])).toEqual([]);
115
+ expect(called).toBe(false);
116
+ });
117
+
118
+ test("root names must be unique and non-empty", () => {
119
+ expect(() => createCrossRootContext([secretRoot, secretRoot])).toThrow(/appears twice/);
120
+ expect(() => createCrossRootContext([{ ...secretRoot, root: "" }])).toThrow(/needs a name/);
121
+ });
122
+ });
@@ -143,6 +143,8 @@ function isPostSynthCheck(obj: unknown): obj is PostSynthCheck {
143
143
  typeof o.description === "string" &&
144
144
  typeof o.check === "function" &&
145
145
  // Exclude LintRules (which also have check) by checking for PostSynthCheck-specific fields
146
- typeof o.severity !== "string"
146
+ typeof o.severity !== "string" &&
147
+ // A cross-root check (chant #1939) is not a per-build check.
148
+ o.scope !== "cross-root"
147
149
  );
148
150
  }