@intentius/chant 0.101.0 → 0.104.0

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 (739) hide show
  1. package/dist/agents/checks.d.ts.map +1 -1
  2. package/dist/attrref.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +2 -0
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts.map +1 -1
  6. package/dist/audit/nginx.d.ts.map +1 -1
  7. package/dist/audit/prior-art.d.ts +48 -0
  8. package/dist/audit/prior-art.d.ts.map +1 -1
  9. package/dist/behaviour-kinds.d.ts.map +1 -1
  10. package/dist/builder.d.ts.map +1 -1
  11. package/dist/change-set.d.ts +232 -0
  12. package/dist/change-set.d.ts.map +1 -0
  13. package/dist/cli/commands/audit.d.ts +7 -0
  14. package/dist/cli/commands/audit.d.ts.map +1 -1
  15. package/dist/cli/commands/build.d.ts.map +1 -1
  16. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  17. package/dist/cli/commands/check-lexicon.d.ts +16 -0
  18. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  19. package/dist/cli/commands/import.d.ts +8 -0
  20. package/dist/cli/commands/import.d.ts.map +1 -1
  21. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  22. package/dist/cli/commands/init.d.ts +19 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -1
  24. package/dist/cli/commands/onboard.d.ts +9 -0
  25. package/dist/cli/commands/onboard.d.ts.map +1 -1
  26. package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
  27. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
  28. package/dist/cli/handlers/build.d.ts.map +1 -1
  29. package/dist/cli/handlers/change-set.d.ts +11 -0
  30. package/dist/cli/handlers/change-set.d.ts.map +1 -0
  31. package/dist/cli/handlers/dev.d.ts.map +1 -1
  32. package/dist/cli/handlers/fan-out.d.ts +33 -0
  33. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  34. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  35. package/dist/cli/handlers/misc.d.ts.map +1 -1
  36. package/dist/cli/handlers/operator.d.ts +16 -0
  37. package/dist/cli/handlers/operator.d.ts.map +1 -1
  38. package/dist/cli/handlers/pr.d.ts +31 -0
  39. package/dist/cli/handlers/pr.d.ts.map +1 -0
  40. package/dist/cli/handlers/run.d.ts.map +1 -1
  41. package/dist/cli/lsp/server.d.ts.map +1 -1
  42. package/dist/cli/main.d.ts.map +1 -1
  43. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  44. package/dist/cli/mcp/server.d.ts.map +1 -1
  45. package/dist/cli/mcp/tools/build.d.ts.map +1 -1
  46. package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
  47. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  48. package/dist/cli/mcp/tools/import.d.ts.map +1 -1
  49. package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
  50. package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
  51. package/dist/cli/mcp/tools/search.d.ts.map +1 -1
  52. package/dist/cli/mcp/workspace-tools.d.ts +4 -3
  53. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  54. package/dist/cli/registry.d.ts +74 -3
  55. package/dist/cli/registry.d.ts.map +1 -1
  56. package/dist/codegen/fetch.d.ts.map +1 -1
  57. package/dist/codegen/naming.d.ts.map +1 -1
  58. package/dist/codegen/pinned-upgrade.d.ts +67 -3
  59. package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
  60. package/dist/codegen/registry.d.ts.map +1 -1
  61. package/dist/components/auto-release.d.ts +16 -0
  62. package/dist/components/auto-release.d.ts.map +1 -1
  63. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  64. package/dist/components/capability.d.ts +53 -0
  65. package/dist/components/capability.d.ts.map +1 -1
  66. package/dist/components/changed-files.d.ts +31 -0
  67. package/dist/components/changed-files.d.ts.map +1 -0
  68. package/dist/components/cli-support.d.ts +9 -1
  69. package/dist/components/cli-support.d.ts.map +1 -1
  70. package/dist/components/component.d.ts +24 -0
  71. package/dist/components/component.d.ts.map +1 -1
  72. package/dist/components/deploy-units.d.ts.map +1 -1
  73. package/dist/components/driver.d.ts +13 -0
  74. package/dist/components/driver.d.ts.map +1 -1
  75. package/dist/components/fan-out-output.d.ts +6 -0
  76. package/dist/components/fan-out-output.d.ts.map +1 -1
  77. package/dist/components/fan-out-record.d.ts +41 -0
  78. package/dist/components/fan-out-record.d.ts.map +1 -0
  79. package/dist/components/fan-out-run.d.ts +65 -0
  80. package/dist/components/fan-out-run.d.ts.map +1 -1
  81. package/dist/components/fan-out-support.d.ts +2 -0
  82. package/dist/components/fan-out-support.d.ts.map +1 -1
  83. package/dist/components/fan-out.d.ts +6 -0
  84. package/dist/components/fan-out.d.ts.map +1 -1
  85. package/dist/components/gated-wave-pipeline.d.ts +50 -0
  86. package/dist/components/gated-wave-pipeline.d.ts.map +1 -0
  87. package/dist/components/index.d.ts +1 -1
  88. package/dist/components/index.d.ts.map +1 -1
  89. package/dist/components/layers.d.ts +25 -0
  90. package/dist/components/layers.d.ts.map +1 -0
  91. package/dist/components/pr-pipeline.d.ts +61 -0
  92. package/dist/components/pr-pipeline.d.ts.map +1 -0
  93. package/dist/components/pr-run.d.ts +119 -0
  94. package/dist/components/pr-run.d.ts.map +1 -0
  95. package/dist/components/promote.d.ts.map +1 -1
  96. package/dist/components/starter-plugin.d.ts.map +1 -1
  97. package/dist/components/verbs/process-runner.d.ts.map +1 -1
  98. package/dist/components/verbs/r2-sync.d.ts.map +1 -1
  99. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  100. package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
  101. package/dist/components/verbs/shell.d.ts.map +1 -1
  102. package/dist/components/verbs/sign.d.ts.map +1 -1
  103. package/dist/components/verbs/verify.d.ts.map +1 -1
  104. package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
  105. package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
  106. package/dist/components/verbs/wrangler.d.ts.map +1 -1
  107. package/dist/components/wave-plan.d.ts +49 -0
  108. package/dist/components/wave-plan.d.ts.map +1 -0
  109. package/dist/composite.d.ts.map +1 -1
  110. package/dist/config.d.ts +19 -7
  111. package/dist/config.d.ts.map +1 -1
  112. package/dist/declarable.d.ts +16 -0
  113. package/dist/declarable.d.ts.map +1 -1
  114. package/dist/discovery/cache.d.ts.map +1 -1
  115. package/dist/discovery/fold-import.d.ts +15 -5
  116. package/dist/discovery/fold-import.d.ts.map +1 -1
  117. package/dist/discovery/param-deps.d.ts +16 -0
  118. package/dist/discovery/param-deps.d.ts.map +1 -1
  119. package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
  120. package/dist/discovery/specifier-resolve.d.ts +37 -0
  121. package/dist/discovery/specifier-resolve.d.ts.map +1 -0
  122. package/dist/errors.d.ts.map +1 -1
  123. package/dist/fold/fold.d.ts +3 -1
  124. package/dist/fold/fold.d.ts.map +1 -1
  125. package/dist/gated-waves.d.ts +147 -0
  126. package/dist/gated-waves.d.ts.map +1 -0
  127. package/dist/graph-detail.d.ts.map +1 -1
  128. package/dist/graph-ir.d.ts.map +1 -1
  129. package/dist/graph-layout.d.ts.map +1 -1
  130. package/dist/import/base-parser.d.ts.map +1 -1
  131. package/dist/import/embedded.d.ts +7 -0
  132. package/dist/import/embedded.d.ts.map +1 -1
  133. package/dist/import/parser-options.d.ts +14 -0
  134. package/dist/import/parser-options.d.ts.map +1 -0
  135. package/dist/import/parser.d.ts +17 -0
  136. package/dist/import/parser.d.ts.map +1 -1
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/kubectl-context.d.ts.map +1 -1
  140. package/dist/lexicon-output.d.ts.map +1 -1
  141. package/dist/lexicon-schema.d.ts +3 -3
  142. package/dist/lexicon.d.ts +103 -3
  143. package/dist/lexicon.d.ts.map +1 -1
  144. package/dist/lifecycle/assert-live.d.ts.map +1 -1
  145. package/dist/lifecycle/gate-ledger.d.ts +22 -0
  146. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  147. package/dist/lifecycle/git.d.ts +46 -1
  148. package/dist/lifecycle/git.d.ts.map +1 -1
  149. package/dist/lifecycle/member-ledger.d.ts.map +1 -1
  150. package/dist/lifecycle/observe.d.ts.map +1 -1
  151. package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
  152. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  153. package/dist/lifecycle/scenario.d.ts.map +1 -1
  154. package/dist/lifecycle/status.d.ts.map +1 -1
  155. package/dist/lifecycle/work-lease.d.ts +61 -2
  156. package/dist/lifecycle/work-lease.d.ts.map +1 -1
  157. package/dist/lint/config.d.ts +8 -8
  158. package/dist/lint/engine.d.ts +6 -0
  159. package/dist/lint/engine.d.ts.map +1 -1
  160. package/dist/lint/post-synth.d.ts +18 -0
  161. package/dist/lint/post-synth.d.ts.map +1 -1
  162. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  163. package/dist/op/activities/activity-contracts.d.ts +137 -11
  164. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  165. package/dist/op/activities/apply.d.ts +79 -9
  166. package/dist/op/activities/apply.d.ts.map +1 -1
  167. package/dist/op/activities/change-set.d.ts +62 -0
  168. package/dist/op/activities/change-set.d.ts.map +1 -0
  169. package/dist/op/activities/decide.d.ts +2 -0
  170. package/dist/op/activities/decide.d.ts.map +1 -1
  171. package/dist/op/activities/factory.d.ts +137 -0
  172. package/dist/op/activities/factory.d.ts.map +1 -0
  173. package/dist/op/activities/index.d.ts +5 -1
  174. package/dist/op/activities/index.d.ts.map +1 -1
  175. package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
  176. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
  177. package/dist/op/activity-contract.d.ts +1 -1
  178. package/dist/op/activity-contract.d.ts.map +1 -1
  179. package/dist/op/activity-failure.d.ts.map +1 -1
  180. package/dist/op/activity-profiles.d.ts.map +1 -1
  181. package/dist/op/builders.d.ts +11 -0
  182. package/dist/op/builders.d.ts.map +1 -1
  183. package/dist/op/composites/apply-op.d.ts +12 -5
  184. package/dist/op/composites/apply-op.d.ts.map +1 -1
  185. package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
  186. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
  187. package/dist/op/decide-backend.d.ts +5 -1
  188. package/dist/op/decide-backend.d.ts.map +1 -1
  189. package/dist/op/decide-read-inputs.d.ts +4 -2
  190. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  191. package/dist/op/emulator-lifecycle.d.ts +21 -2
  192. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  193. package/dist/op/factory.d.ts +83 -0
  194. package/dist/op/factory.d.ts.map +1 -0
  195. package/dist/op/gate-approval.d.ts +5 -1
  196. package/dist/op/gate-approval.d.ts.map +1 -1
  197. package/dist/op/gate-wait.d.ts.map +1 -1
  198. package/dist/op/gate.d.ts +19 -3
  199. package/dist/op/gate.d.ts.map +1 -1
  200. package/dist/op/index.d.ts +6 -1
  201. package/dist/op/index.d.ts.map +1 -1
  202. package/dist/op/local-executor.d.ts +6 -0
  203. package/dist/op/local-executor.d.ts.map +1 -1
  204. package/dist/op/op-ir.d.ts +2 -0
  205. package/dist/op/op-ir.d.ts.map +1 -1
  206. package/dist/op/op-verb-class.d.ts.map +1 -1
  207. package/dist/op/operator.d.ts.map +1 -1
  208. package/dist/op/run-context.d.ts +39 -0
  209. package/dist/op/run-context.d.ts.map +1 -0
  210. package/dist/op/run-live.d.ts.map +1 -1
  211. package/dist/op/runtimes/local.d.ts.map +1 -1
  212. package/dist/op/source-archive.d.ts.map +1 -1
  213. package/dist/op/steward-beside.d.ts.map +1 -1
  214. package/dist/op/steward-points.d.ts.map +1 -1
  215. package/dist/op/steward.d.ts.map +1 -1
  216. package/dist/op/types.d.ts +16 -3
  217. package/dist/op/types.d.ts.map +1 -1
  218. package/dist/op/work-lease-run.d.ts +24 -4
  219. package/dist/op/work-lease-run.d.ts.map +1 -1
  220. package/dist/plan-summary.d.ts +207 -0
  221. package/dist/plan-summary.d.ts.map +1 -0
  222. package/dist/pr-forge.d.ts +105 -0
  223. package/dist/pr-forge.d.ts.map +1 -0
  224. package/dist/pr-loop.d.ts +178 -0
  225. package/dist/pr-loop.d.ts.map +1 -0
  226. package/dist/project-root.d.ts.map +1 -1
  227. package/dist/provenance.d.ts +21 -0
  228. package/dist/provenance.d.ts.map +1 -1
  229. package/dist/pseudo-parameter.d.ts.map +1 -1
  230. package/dist/reconcile.d.ts.map +1 -1
  231. package/dist/secret-materialization.d.ts.map +1 -1
  232. package/dist/telemetry-attribution.d.ts +31 -0
  233. package/dist/telemetry-attribution.d.ts.map +1 -1
  234. package/dist/terraform/emit-source.d.ts.map +1 -1
  235. package/dist/terraform/hcl2json.d.ts +45 -0
  236. package/dist/terraform/hcl2json.d.ts.map +1 -0
  237. package/dist/terraform/parse.d.ts +4 -40
  238. package/dist/terraform/parse.d.ts.map +1 -1
  239. package/dist/testing.d.ts.map +1 -1
  240. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  241. package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
  242. package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
  243. package/dist/workspace/agent-cli.d.ts +5 -0
  244. package/dist/workspace/agent-cli.d.ts.map +1 -1
  245. package/dist/workspace/box-cli.d.ts +12 -0
  246. package/dist/workspace/box-cli.d.ts.map +1 -0
  247. package/dist/workspace/box-factory.d.ts +97 -0
  248. package/dist/workspace/box-factory.d.ts.map +1 -0
  249. package/dist/workspace/box-listing.d.ts +85 -0
  250. package/dist/workspace/box-listing.d.ts.map +1 -0
  251. package/dist/workspace/box-publish.d.ts +147 -0
  252. package/dist/workspace/box-publish.d.ts.map +1 -0
  253. package/dist/workspace/broker-protocol.d.ts +190 -0
  254. package/dist/workspace/broker-protocol.d.ts.map +1 -0
  255. package/dist/workspace/changes.d.ts.map +1 -1
  256. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
  257. package/dist/workspace/checks/boxes.d.ts.map +1 -1
  258. package/dist/workspace/checks.d.ts +5 -0
  259. package/dist/workspace/checks.d.ts.map +1 -1
  260. package/dist/workspace/compose-graph.d.ts +10 -0
  261. package/dist/workspace/compose-graph.d.ts.map +1 -1
  262. package/dist/workspace/composites.d.ts +2 -2
  263. package/dist/workspace/composites.d.ts.map +1 -1
  264. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +17 -0
  265. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -0
  266. package/dist/workspace/conformance/broker.d.ts +202 -0
  267. package/dist/workspace/conformance/broker.d.ts.map +1 -0
  268. package/dist/workspace/conformance/index.d.ts +68 -6
  269. package/dist/workspace/conformance/index.d.ts.map +1 -1
  270. package/dist/workspace/conformance/vitest.d.ts +19 -3
  271. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  272. package/dist/workspace/conformance/writer.d.ts +462 -0
  273. package/dist/workspace/conformance/writer.d.ts.map +1 -0
  274. package/dist/workspace/decide.d.ts +126 -12
  275. package/dist/workspace/decide.d.ts.map +1 -1
  276. package/dist/workspace/decision-points.schema.json +11 -4
  277. package/dist/workspace/declaration.d.ts +156 -4
  278. package/dist/workspace/declaration.d.ts.map +1 -1
  279. package/dist/workspace/declaration.schema.json +383 -5
  280. package/dist/workspace/environments.d.ts +36 -10
  281. package/dist/workspace/environments.d.ts.map +1 -1
  282. package/dist/workspace/factory-rules.d.ts +180 -0
  283. package/dist/workspace/factory-rules.d.ts.map +1 -0
  284. package/dist/workspace/generated-files.d.ts.map +1 -1
  285. package/dist/workspace/graph-cache.d.ts.map +1 -1
  286. package/dist/workspace/graph-cli.d.ts +2 -0
  287. package/dist/workspace/graph-cli.d.ts.map +1 -1
  288. package/dist/workspace/identity.d.ts +111 -0
  289. package/dist/workspace/identity.d.ts.map +1 -0
  290. package/dist/workspace/init.d.ts +21 -0
  291. package/dist/workspace/init.d.ts.map +1 -1
  292. package/dist/workspace/intent-cli.d.ts +7 -4
  293. package/dist/workspace/intent-cli.d.ts.map +1 -1
  294. package/dist/workspace/intent-record.d.ts +36 -2
  295. package/dist/workspace/intent-record.d.ts.map +1 -1
  296. package/dist/workspace/intent.d.ts +249 -8
  297. package/dist/workspace/intent.d.ts.map +1 -1
  298. package/dist/workspace/json-edit.d.ts +24 -0
  299. package/dist/workspace/json-edit.d.ts.map +1 -0
  300. package/dist/workspace/jsonc.d.ts +11 -0
  301. package/dist/workspace/jsonc.d.ts.map +1 -1
  302. package/dist/workspace/kinds.d.ts +66 -4
  303. package/dist/workspace/kinds.d.ts.map +1 -1
  304. package/dist/workspace/lexicon-symbols.d.ts +29 -0
  305. package/dist/workspace/lexicon-symbols.d.ts.map +1 -0
  306. package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
  307. package/dist/workspace/lineage-lock.d.ts +2 -2
  308. package/dist/workspace/lineage-lock.d.ts.map +1 -1
  309. package/dist/workspace/lineage-upgrade.d.ts +31 -2
  310. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  311. package/dist/workspace/links.d.ts.map +1 -1
  312. package/dist/workspace/point-answer.schema.json +42 -2
  313. package/dist/workspace/points-cli.d.ts +10 -3
  314. package/dist/workspace/points-cli.d.ts.map +1 -1
  315. package/dist/workspace/points.d.ts +66 -5
  316. package/dist/workspace/points.d.ts.map +1 -1
  317. package/dist/workspace/principal-classes.d.ts +114 -0
  318. package/dist/workspace/principal-classes.d.ts.map +1 -0
  319. package/dist/workspace/reason-codes.d.ts +53 -4
  320. package/dist/workspace/reason-codes.d.ts.map +1 -1
  321. package/dist/workspace/record-assets.d.ts.map +1 -1
  322. package/dist/workspace/record-sessions.d.ts +5 -7
  323. package/dist/workspace/record-sessions.d.ts.map +1 -1
  324. package/dist/workspace/records-checkout.d.ts +54 -0
  325. package/dist/workspace/records-checkout.d.ts.map +1 -0
  326. package/dist/workspace/records-cli.d.ts +34 -1
  327. package/dist/workspace/records-cli.d.ts.map +1 -1
  328. package/dist/workspace/records-close.d.ts +7 -3
  329. package/dist/workspace/records-close.d.ts.map +1 -1
  330. package/dist/workspace/records-since.d.ts.map +1 -1
  331. package/dist/workspace/records-write.d.ts +68 -7
  332. package/dist/workspace/records-write.d.ts.map +1 -1
  333. package/dist/workspace/records.d.ts +43 -9
  334. package/dist/workspace/records.d.ts.map +1 -1
  335. package/dist/workspace/runs-cli.d.ts +296 -0
  336. package/dist/workspace/runs-cli.d.ts.map +1 -0
  337. package/dist/workspace/runs.d.ts +544 -0
  338. package/dist/workspace/runs.d.ts.map +1 -0
  339. package/dist/workspace/session-kinds.d.ts +1 -1
  340. package/dist/workspace/session-kinds.d.ts.map +1 -1
  341. package/dist/workspace/source-block.d.ts +1 -1
  342. package/dist/workspace/source-block.d.ts.map +1 -1
  343. package/dist/workspace/squash.d.ts +80 -0
  344. package/dist/workspace/squash.d.ts.map +1 -0
  345. package/dist/workspace/status-gates.d.ts +17 -3
  346. package/dist/workspace/status-gates.d.ts.map +1 -1
  347. package/dist/workspace/status.d.ts +64 -0
  348. package/dist/workspace/status.d.ts.map +1 -1
  349. package/dist/workspace/symbols.d.ts +63 -0
  350. package/dist/workspace/symbols.d.ts.map +1 -0
  351. package/dist/workspace/trailer-joins.d.ts +46 -0
  352. package/dist/workspace/trailer-joins.d.ts.map +1 -0
  353. package/dist/workspace/trailers.d.ts +89 -0
  354. package/dist/workspace/trailers.d.ts.map +1 -0
  355. package/dist/workspace/trust/attestor.d.ts.map +1 -1
  356. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  357. package/dist/workspace/trust/run-commits.d.ts +46 -0
  358. package/dist/workspace/trust/run-commits.d.ts.map +1 -0
  359. package/dist/workspace/trust/run-statement.d.ts +128 -0
  360. package/dist/workspace/trust/run-statement.d.ts.map +1 -0
  361. package/dist/workspace/trust/seal.d.ts +38 -0
  362. package/dist/workspace/trust/seal.d.ts.map +1 -1
  363. package/dist/workspace/trust/test-repo.d.ts.map +1 -1
  364. package/dist/workspace/trust/verify-cli.d.ts +1 -1
  365. package/dist/workspace/trust/verify.d.ts +16 -2
  366. package/dist/workspace/trust/verify.d.ts.map +1 -1
  367. package/dist/workspace/wip-cli.d.ts +21 -0
  368. package/dist/workspace/wip-cli.d.ts.map +1 -0
  369. package/dist/workspace/wip.d.ts +268 -0
  370. package/dist/workspace/wip.d.ts.map +1 -0
  371. package/dist/workspace/work-cli.d.ts +39 -3
  372. package/dist/workspace/work-cli.d.ts.map +1 -1
  373. package/dist/workspace/work-evidence.d.ts +1 -1
  374. package/dist/workspace/work-evidence.d.ts.map +1 -1
  375. package/dist/workspace/work-size.d.ts +55 -0
  376. package/dist/workspace/work-size.d.ts.map +1 -0
  377. package/dist/workspace/work.d.ts +26 -1
  378. package/dist/workspace/work.d.ts.map +1 -1
  379. package/dist/workspace/workspace-kinds.schema.json +122 -0
  380. package/dist/workspace/workspace-principals.schema.json +60 -0
  381. package/dist/workspace/write-lock-cli.d.ts +60 -0
  382. package/dist/workspace/write-lock-cli.d.ts.map +1 -0
  383. package/dist/workspace/write-lock.d.ts +152 -0
  384. package/dist/workspace/write-lock.d.ts.map +1 -0
  385. package/dist/workspace/write-scope.d.ts +83 -19
  386. package/dist/workspace/write-scope.d.ts.map +1 -1
  387. package/dist/yaml.d.ts +21 -39
  388. package/dist/yaml.d.ts.map +1 -1
  389. package/package.json +7 -2
  390. package/src/__fixtures__/change-set/lifecycle-plan.floci.json +59 -0
  391. package/src/__fixtures__/change-set/lifecycle-v1.ts.txt +4 -0
  392. package/src/__fixtures__/change-set/lifecycle-v2.ts.txt +4 -0
  393. package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +95 -0
  394. package/src/__fixtures__/change-set/warden.change-set.golden.json +106 -0
  395. package/src/__fixtures__/change-set/warden.plan.json +44 -0
  396. package/src/__fixtures__/plan-summary/normalization-vectors.json +1593 -0
  397. package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +31 -0
  398. package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +31 -0
  399. package/src/__fixtures__/pr-loop/pr-plan.golden.json +127 -0
  400. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +37 -0
  401. package/src/audit/core.test.ts +15 -0
  402. package/src/audit/core.ts +8 -2
  403. package/src/audit/discover.test.ts +11 -0
  404. package/src/audit/discover.ts +7 -2
  405. package/src/audit/prior-art.ts +15 -0
  406. package/src/change-set-bundle.test.ts +57 -0
  407. package/src/change-set.test.ts +215 -0
  408. package/src/change-set.ts +445 -0
  409. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
  410. package/src/cli/commands/audit-opt-in.test.ts +54 -0
  411. package/src/cli/commands/audit.ts +29 -2
  412. package/src/cli/commands/build.test.ts +19 -0
  413. package/src/cli/commands/build.ts +3 -2
  414. package/src/cli/commands/carve-emit-state.test.ts +4 -0
  415. package/src/cli/commands/carve-emit.ts +5 -19
  416. package/src/cli/commands/check-lexicon.test.ts +62 -1
  417. package/src/cli/commands/check-lexicon.ts +36 -6
  418. package/src/cli/commands/import-live.test.ts +18 -0
  419. package/src/cli/commands/import-parser-options.test.ts +133 -0
  420. package/src/cli/commands/import.ts +21 -3
  421. package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
  422. package/src/cli/commands/init.test.ts +8 -1
  423. package/src/cli/commands/init.ts +23 -8
  424. package/src/cli/commands/lint.test.ts +37 -0
  425. package/src/cli/commands/onboard.test.ts +53 -3
  426. package/src/cli/commands/onboard.ts +52 -4
  427. package/src/cli/commands/pinned-upgrade.ts +73 -33
  428. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
  429. package/src/cli/handlers/build.ts +13 -1
  430. package/src/cli/handlers/change-set.test.ts +56 -0
  431. package/src/cli/handlers/change-set.ts +44 -0
  432. package/src/cli/handlers/components.ts +2 -2
  433. package/src/cli/handlers/dev.ts +8 -6
  434. package/src/cli/handlers/fan-out.test.ts +221 -2
  435. package/src/cli/handlers/fan-out.ts +165 -75
  436. package/src/cli/handlers/graph.ts +3 -0
  437. package/src/cli/handlers/lifecycle.test.ts +67 -0
  438. package/src/cli/handlers/lifecycle.ts +6 -5
  439. package/src/cli/handlers/misc.ts +2 -0
  440. package/src/cli/handlers/operator.test.ts +21 -0
  441. package/src/cli/handlers/operator.ts +121 -6
  442. package/src/cli/handlers/pr.ts +373 -0
  443. package/src/cli/handlers/run.test.ts +111 -0
  444. package/src/cli/handlers/run.ts +86 -24
  445. package/src/cli/handlers/scenario.ts +2 -2
  446. package/src/cli/main.test.ts +21 -0
  447. package/src/cli/main.ts +246 -30
  448. package/src/cli/mcp/never-over-mcp.test.ts +101 -0
  449. package/src/cli/mcp/op-tools.ts +24 -2
  450. package/src/cli/mcp/workspace-tools.test.ts +5 -2
  451. package/src/cli/mcp/workspace-tools.ts +62 -7
  452. package/src/cli/registry.ts +74 -3
  453. package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
  454. package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
  455. package/src/codegen/pinned-upgrade.test.ts +104 -0
  456. package/src/codegen/pinned-upgrade.ts +250 -56
  457. package/src/codegen/testdata/postgres-tags.json +242 -0
  458. package/src/components/auto-release.ts +1 -1
  459. package/src/components/capability.ts +55 -0
  460. package/src/components/changed-files.ts +66 -0
  461. package/src/components/cli-support.ts +28 -5
  462. package/src/components/component.schema.json +17 -0
  463. package/src/components/component.ts +25 -0
  464. package/src/components/deploy-units.ts +3 -0
  465. package/src/components/driver.test.ts +52 -0
  466. package/src/components/driver.ts +31 -13
  467. package/src/components/fan-out-output.ts +42 -1
  468. package/src/components/fan-out-record.ts +69 -0
  469. package/src/components/fan-out-run.test.ts +17 -0
  470. package/src/components/fan-out-run.ts +304 -1
  471. package/src/components/fan-out-support.ts +3 -0
  472. package/src/components/fan-out-waves.test.ts +375 -0
  473. package/src/components/fan-out.ts +18 -17
  474. package/src/components/gated-wave-pipeline.ts +82 -0
  475. package/src/components/index.ts +1 -0
  476. package/src/components/layers.ts +30 -0
  477. package/src/components/pr-pipeline.ts +91 -0
  478. package/src/components/pr-run.test.ts +205 -0
  479. package/src/components/pr-run.ts +280 -0
  480. package/src/components/promote.ts +5 -0
  481. package/src/components/verbs/shell.test.ts +11 -0
  482. package/src/components/verbs/shell.ts +7 -3
  483. package/src/components/wave-plan.ts +124 -0
  484. package/src/config.test.ts +21 -0
  485. package/src/config.ts +15 -0
  486. package/src/declarable.test.ts +21 -0
  487. package/src/declarable.ts +19 -0
  488. package/src/discovery/fold-composite.test.ts +41 -5
  489. package/src/discovery/fold-import.test.ts +129 -0
  490. package/src/discovery/fold-import.ts +643 -131
  491. package/src/discovery/fold-package-composite.test.ts +264 -0
  492. package/src/discovery/fold-specifiers.test.ts +191 -0
  493. package/src/discovery/param-deps.test.ts +66 -1
  494. package/src/discovery/param-deps.ts +45 -0
  495. package/src/discovery/sandbox/bundle.ts +1 -1
  496. package/src/discovery/specifier-resolve.ts +344 -0
  497. package/src/fold/fold.test.ts +39 -1
  498. package/src/fold/fold.ts +83 -9
  499. package/src/fold-provenance.test.ts +140 -0
  500. package/src/gated-waves-bundle.test.ts +62 -0
  501. package/src/gated-waves.test.ts +118 -0
  502. package/src/gated-waves.ts +240 -0
  503. package/src/graph-ir.test.ts +83 -0
  504. package/src/graph-ir.ts +29 -0
  505. package/src/import/embedded.test.ts +20 -0
  506. package/src/import/embedded.ts +13 -0
  507. package/src/import/parser-options.ts +46 -0
  508. package/src/import/parser.ts +20 -0
  509. package/src/index.ts +1 -0
  510. package/src/lexicon.ts +106 -3
  511. package/src/lifecycle/gate-ledger.ts +23 -0
  512. package/src/lifecycle/git.ts +102 -16
  513. package/src/lifecycle/observe.test.ts +18 -0
  514. package/src/lifecycle/observe.ts +6 -4
  515. package/src/lifecycle/snapshot.ts +2 -2
  516. package/src/lifecycle/work-lease.test.ts +1 -1
  517. package/src/lifecycle/work-lease.ts +93 -2
  518. package/src/lint/engine.test.ts +55 -0
  519. package/src/lint/engine.ts +30 -4
  520. package/src/lint/output-docs.test.ts +59 -56
  521. package/src/lint/post-synth.test.ts +19 -0
  522. package/src/lint/post-synth.ts +19 -1
  523. package/src/meta/declared-imports.test.ts +1 -1
  524. package/src/op/activities/activity-contracts.ts +84 -0
  525. package/src/op/activities/apply.test.ts +189 -5
  526. package/src/op/activities/apply.ts +230 -8
  527. package/src/op/activities/change-set.ts +114 -0
  528. package/src/op/activities/decide.ts +3 -0
  529. package/src/op/activities/factory.ts +675 -0
  530. package/src/op/activities/index.ts +9 -0
  531. package/src/op/activities/lexicon-upgrade.test.ts +160 -6
  532. package/src/op/activities/lexicon-upgrade.ts +154 -51
  533. package/src/op/activity-contract.test.ts +21 -1
  534. package/src/op/activity-contract.ts +19 -9
  535. package/src/op/builders.ts +23 -0
  536. package/src/op/composites/apply-op.ts +12 -5
  537. package/src/op/composites/composites.test.ts +30 -3
  538. package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
  539. package/src/op/composites/lexicon-upgrade-op.ts +29 -8
  540. package/src/op/decide-backend.ts +5 -1
  541. package/src/op/decide-read-inputs.ts +33 -5
  542. package/src/op/emulator-lifecycle.test.ts +23 -0
  543. package/src/op/emulator-lifecycle.ts +52 -14
  544. package/src/op/factory.e2e.test.ts +232 -0
  545. package/src/op/factory.ts +118 -0
  546. package/src/op/gate-approval.test.ts +74 -0
  547. package/src/op/gate-approval.ts +5 -1
  548. package/src/op/gate.ts +31 -5
  549. package/src/op/index.ts +11 -1
  550. package/src/op/local-executor.test.ts +15 -0
  551. package/src/op/local-executor.ts +40 -12
  552. package/src/op/local-output.ts +1 -1
  553. package/src/op/op-ir.test.ts +9 -0
  554. package/src/op/op-ir.ts +4 -0
  555. package/src/op/op-verb-class.ts +3 -0
  556. package/src/op/operator.ts +4 -0
  557. package/src/op/run-context.test.ts +141 -0
  558. package/src/op/run-context.ts +51 -0
  559. package/src/op/runtimes/local.test.ts +14 -0
  560. package/src/op/runtimes/local.ts +2 -0
  561. package/src/op/steward-beside.ts +2 -0
  562. package/src/op/types.ts +16 -3
  563. package/src/op/work-lease-run.test.ts +47 -0
  564. package/src/op/work-lease-run.ts +76 -11
  565. package/src/plan-summary.test.ts +263 -0
  566. package/src/plan-summary.ts +845 -0
  567. package/src/pr-forge.test.ts +237 -0
  568. package/src/pr-forge.ts +332 -0
  569. package/src/pr-loop-bundle.test.ts +46 -0
  570. package/src/pr-loop.test.ts +171 -0
  571. package/src/pr-loop.ts +324 -0
  572. package/src/provenance.ts +35 -0
  573. package/src/telemetry-attribution.test.ts +23 -1
  574. package/src/telemetry-attribution.ts +51 -0
  575. package/src/terraform/hcl2json.test.ts +21 -0
  576. package/src/terraform/hcl2json.ts +81 -0
  577. package/src/terraform/parse.ts +11 -74
  578. package/src/workspace/agent-cli.ts +7 -3
  579. package/src/workspace/agent.schema.json +22 -0
  580. package/src/workspace/app-kind.test.ts +196 -0
  581. package/src/workspace/box-cli.ts +113 -0
  582. package/src/workspace/box-factory.test.ts +323 -0
  583. package/src/workspace/box-factory.ts +136 -0
  584. package/src/workspace/box-listing-write.schema.json +204 -0
  585. package/src/workspace/box-listing.test.ts +234 -0
  586. package/src/workspace/box-listing.ts +324 -0
  587. package/src/workspace/box-publish.schema.json +498 -0
  588. package/src/workspace/box-publish.test.ts +204 -0
  589. package/src/workspace/box-publish.ts +420 -0
  590. package/src/workspace/box-ship.test.ts +96 -0
  591. package/src/workspace/broker-protocol.schema.json +186 -0
  592. package/src/workspace/broker-protocol.test.ts +122 -0
  593. package/src/workspace/broker-protocol.ts +317 -0
  594. package/src/workspace/builder-tiers.test.ts +146 -0
  595. package/src/workspace/change-set.schema.json +460 -0
  596. package/src/workspace/changes.schema.json +12 -20
  597. package/src/workspace/changes.ts +4 -1
  598. package/src/workspace/chant-migrations.e2e.test.ts +17 -5
  599. package/src/workspace/checks/boxes.test.ts +5 -0
  600. package/src/workspace/checks.ts +39 -8
  601. package/src/workspace/component-environments.test.ts +105 -0
  602. package/src/workspace/compose-graph.ts +7 -0
  603. package/src/workspace/composites.schema.json +23 -4
  604. package/src/workspace/composites.test.ts +8 -8
  605. package/src/workspace/composites.ts +13 -2
  606. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +2 -2
  607. package/src/workspace/conformance/__fixtures__/reference-broker.ts +207 -0
  608. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +32 -0
  609. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +135 -0
  610. package/src/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
  611. package/src/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
  612. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +228 -0
  613. package/src/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
  614. package/src/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
  615. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +54 -0
  616. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +289 -0
  617. package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
  618. package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
  619. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +603 -0
  620. package/src/workspace/conformance/broker.test.ts +122 -0
  621. package/src/workspace/conformance/broker.ts +792 -0
  622. package/src/workspace/conformance/conformance.e2e.test.ts +36 -3
  623. package/src/workspace/conformance/index.mjs +51 -3
  624. package/src/workspace/conformance/index.ts +107 -15
  625. package/src/workspace/conformance/vitest.ts +76 -2
  626. package/src/workspace/conformance/writer-conformance.e2e.test.ts +177 -0
  627. package/src/workspace/conformance/writer.test.ts +186 -0
  628. package/src/workspace/conformance/writer.ts +1191 -0
  629. package/src/workspace/decide.test.ts +208 -4
  630. package/src/workspace/decide.ts +433 -73
  631. package/src/workspace/decision-points.schema.json +11 -4
  632. package/src/workspace/declaration.schema.json +383 -5
  633. package/src/workspace/declaration.ts +315 -10
  634. package/src/workspace/environments.ts +66 -14
  635. package/src/workspace/factory-rules.test.ts +62 -0
  636. package/src/workspace/factory-rules.ts +218 -0
  637. package/src/workspace/graph-cli.ts +6 -3
  638. package/src/workspace/graph.schema.json +314 -0
  639. package/src/workspace/identity.test.ts +219 -0
  640. package/src/workspace/identity.ts +186 -0
  641. package/src/workspace/init.ts +82 -2
  642. package/src/workspace/intent-cli.ts +84 -13
  643. package/src/workspace/intent-patch-id.test.ts +162 -0
  644. package/src/workspace/intent-record.schema.json +336 -1
  645. package/src/workspace/intent-record.ts +96 -16
  646. package/src/workspace/intent-reviews.test.ts +76 -0
  647. package/src/workspace/intent-squash.test.ts +213 -0
  648. package/src/workspace/intent-why.test.ts +274 -0
  649. package/src/workspace/intent.schema.json +950 -9
  650. package/src/workspace/intent.test.ts +11 -2
  651. package/src/workspace/intent.ts +722 -65
  652. package/src/workspace/json-edit.test.ts +121 -0
  653. package/src/workspace/json-edit.ts +142 -0
  654. package/src/workspace/jsonc.ts +20 -0
  655. package/src/workspace/kinds.ts +201 -39
  656. package/src/workspace/lexicon-symbols.test.ts +148 -0
  657. package/src/workspace/lexicon-symbols.ts +125 -0
  658. package/src/workspace/lineage-upgrade.test.ts +63 -0
  659. package/src/workspace/lineage-upgrade.ts +146 -5
  660. package/src/workspace/plan-summary.schema.json +155 -0
  661. package/src/workspace/point-answer.schema.json +42 -2
  662. package/src/workspace/points-cli.ts +42 -16
  663. package/src/workspace/points-write.schema.json +163 -4
  664. package/src/workspace/points.schema.json +157 -2
  665. package/src/workspace/points.test.ts +13 -1
  666. package/src/workspace/points.ts +118 -12
  667. package/src/workspace/pr-report.schema.json +95 -0
  668. package/src/workspace/principal-classes.test.ts +175 -0
  669. package/src/workspace/principal-classes.ts +307 -0
  670. package/src/workspace/profiles.test.ts +159 -0
  671. package/src/workspace/read-contract.test.ts +2 -2
  672. package/src/workspace/reason-codes.test.ts +23 -3
  673. package/src/workspace/reason-codes.ts +61 -4
  674. package/src/workspace/record-sessions.ts +9 -8
  675. package/src/workspace/records-amend.schema.json +80 -0
  676. package/src/workspace/records-checkout.ts +106 -0
  677. package/src/workspace/records-cli.ts +123 -17
  678. package/src/workspace/records-close.schema.json +79 -0
  679. package/src/workspace/records-close.ts +31 -7
  680. package/src/workspace/records-concurrent.e2e.test.ts +135 -0
  681. package/src/workspace/records-concurrent.test.ts +145 -0
  682. package/src/workspace/records-formats.test.ts +46 -10
  683. package/src/workspace/records-new.schema.json +9 -0
  684. package/src/workspace/records-review.schema.json +80 -0
  685. package/src/workspace/records-sessions-write.test.ts +69 -0
  686. package/src/workspace/records-sessions.test.ts +22 -0
  687. package/src/workspace/records-uncommitted.test.ts +171 -0
  688. package/src/workspace/records-write.ts +219 -30
  689. package/src/workspace/records.schema.json +88 -1
  690. package/src/workspace/records.ts +76 -8
  691. package/src/workspace/reference-kinds/app/package.json +10 -0
  692. package/src/workspace/reference-kinds/app/workspace-kinds.json +45 -0
  693. package/src/workspace/run-statement.schema.json +622 -0
  694. package/src/workspace/runs-cli.ts +710 -0
  695. package/src/workspace/runs-statement.test.ts +239 -0
  696. package/src/workspace/runs-write.schema.json +994 -0
  697. package/src/workspace/runs.schema.json +1257 -0
  698. package/src/workspace/runs.test.ts +281 -0
  699. package/src/workspace/runs.ts +970 -0
  700. package/src/workspace/session-kinds.ts +3 -3
  701. package/src/workspace/squash.ts +198 -0
  702. package/src/workspace/status-contract.test.ts +10 -2
  703. package/src/workspace/status-gates.ts +25 -9
  704. package/src/workspace/status.schema.json +252 -4
  705. package/src/workspace/status.ts +110 -1
  706. package/src/workspace/symbols.ts +191 -0
  707. package/src/workspace/trailer-joins.test.ts +171 -0
  708. package/src/workspace/trailer-joins.ts +82 -0
  709. package/src/workspace/trailers.ts +148 -0
  710. package/src/workspace/trust/run-commits.ts +115 -0
  711. package/src/workspace/trust/run-statement.ts +226 -0
  712. package/src/workspace/trust/seal.ts +58 -1
  713. package/src/workspace/trust/verify-cli.ts +6 -5
  714. package/src/workspace/trust/verify.ts +39 -6
  715. package/src/workspace/wip-cli.ts +97 -0
  716. package/src/workspace/wip-write.schema.json +504 -0
  717. package/src/workspace/wip.schema.json +334 -0
  718. package/src/workspace/wip.test.ts +294 -0
  719. package/src/workspace/wip.ts +753 -0
  720. package/src/workspace/work-cli.e2e.test.ts +28 -1
  721. package/src/workspace/work-cli.ts +136 -8
  722. package/src/workspace/work-evidence.schema.json +5 -0
  723. package/src/workspace/work-history.schema.json +73 -1
  724. package/src/workspace/work-history.test.ts +48 -5
  725. package/src/workspace/work-kind-fields.test.ts +154 -0
  726. package/src/workspace/work-lease.schema.json +14 -1
  727. package/src/workspace/work-size.test.ts +77 -0
  728. package/src/workspace/work-size.ts +93 -0
  729. package/src/workspace/work.ts +82 -0
  730. package/src/workspace/workspace-kinds.schema.json +122 -0
  731. package/src/workspace/workspace-principals.schema.json +60 -0
  732. package/src/workspace/write-lock-cli.ts +131 -0
  733. package/src/workspace/write-lock.schema.json +172 -0
  734. package/src/workspace/write-lock.test.ts +189 -0
  735. package/src/workspace/write-lock.ts +474 -0
  736. package/src/workspace/write-scope.test.ts +144 -3
  737. package/src/workspace/write-scope.ts +192 -45
  738. package/src/yaml.test.ts +111 -1
  739. package/src/yaml.ts +141 -549
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Planning and applying a pull request's members (#3183), against a fake
3
+ * root capability and an in-memory gate ledger. The five-root run against
4
+ * real `tofu` roots is `lexicons/terraform/src/components/pr-loop.tofu.test.ts`.
5
+ *
6
+ * The capability stands in for `terraform-apply`: `plan` returns the root, a
7
+ * digest over what it would write (its `version` and every input) and a
8
+ * change-set part; `outputs` reads what it exposes now; `run` applies.
9
+ */
10
+
11
+ import { describe, expect, test } from "vitest";
12
+ import { CapabilityRegistry, type DeployContext } from "./capability";
13
+ import { memoryGateLedgerPort } from "../op/gate";
14
+ import { planFanOut } from "./fan-out";
15
+ import { applyPrSet, approvalOf, approversWithoutReview, decidePrGate, planPrSet, readOnlyLedger } from "./pr-run";
16
+ import { changeSetDigest } from "../change-set";
17
+ import { computePlanDigest } from "../lifecycle/plan-digest";
18
+ import type { DriverComponent } from "./driver";
19
+
20
+ interface RootInput {
21
+ root: string;
22
+ vars?: Record<string, unknown>;
23
+ }
24
+
25
+ const root = (name: string, dependsOn: string[] = [], vars?: Record<string, unknown>): DriverComponent => ({
26
+ name,
27
+ dependsOn,
28
+ deploy: [{ phase: "Apply", steps: [{ kind: "fake-root", root: name, ...(vars ? { vars } : {}) }] }],
29
+ });
30
+
31
+ /** net -> a, b; a -> app; dns alone. */
32
+ const ESTATE: DriverComponent[] = [
33
+ root("net"),
34
+ root("a", ["net"], { cidr: { stackOutput: { stack: "net", name: "cidr" } } }),
35
+ root("b", ["net"], { cidr: { stackOutput: { stack: "net", name: "cidr" } } }),
36
+ root("app", ["a"], { subnet: { stackOutput: { stack: "a", name: "id" } } }),
37
+ root("dns"),
38
+ ];
39
+
40
+ function world() {
41
+ const w = {
42
+ registry: new CapabilityRegistry(),
43
+ /** What each root would write. */
44
+ versions: {} as Record<string, string>,
45
+ /** What each root's state exposes. */
46
+ state: { net: { cidr: "10.0.0.0/16" }, a: { id: "a-v1" } } as Record<string, Record<string, unknown>>,
47
+ planned: [] as Array<{ root: string; vars?: Record<string, unknown> }>,
48
+ outputsRead: [] as string[],
49
+ applied: [] as string[],
50
+ fromPlan: {} as Record<string, boolean>,
51
+ failing: new Set<string>(),
52
+ planFailing: new Set<string>(),
53
+ };
54
+ const digestOf = (input: RootInput) => computePlanDigest("fake-root", { root: input.root, version: w.versions[input.root] ?? "v1", vars: input.vars ?? null });
55
+ w.registry.register({
56
+ kind: "fake-root",
57
+ async plan(_ctx: DeployContext, input: RootInput) {
58
+ w.planned.push({ root: input.root, ...(input.vars ? { vars: input.vars } : {}) });
59
+ if (w.planFailing.has(input.root)) throw new Error(`${input.root}: no value for a required variable`);
60
+ const planDigest = digestOf(input);
61
+ const changes = (w.versions[input.root] ?? "v1") !== "v1";
62
+ return {
63
+ member: input.root,
64
+ planDigest,
65
+ artifact: { planDigest },
66
+ changeSet: {
67
+ member: { member: input.root, lexicon: "terraform", planner: "tofu", status: "planned", planDigest, holes: [] },
68
+ entries: changes
69
+ ? [{ member: input.root, lexicon: "terraform", planner: "tofu", address: "terraform_data.x", type: "terraform_data", action: "update", attributes: [] }]
70
+ : [],
71
+ },
72
+ };
73
+ },
74
+ async outputs(_ctx: DeployContext, input: RootInput) {
75
+ w.outputsRead.push(input.root);
76
+ return { ...(w.state[input.root] ?? {}) };
77
+ },
78
+ async run(ctx: DeployContext, input: RootInput) {
79
+ w.fromPlan[input.root] = ctx.plans?.[input.root] !== undefined;
80
+ if (w.failing.has(input.root)) throw new Error(`${input.root} failed`);
81
+ w.applied.push(input.root);
82
+ if (input.root === "a") w.state.a = { id: `a-${w.versions.a ?? "v1"}` };
83
+ return { outputs: { ...(w.state[input.root] ?? {}) } };
84
+ },
85
+ } as never);
86
+ return w;
87
+ }
88
+
89
+ const NOW = "2026-10-03T00:00:00Z";
90
+ const LATER = "2026-10-03T01:00:00Z";
91
+
92
+ const approval = (planDigest: string, resolvedBy = "github:alice") => ({
93
+ version: 1 as const,
94
+ kind: "resolution" as const,
95
+ op: "pr-12",
96
+ gate: "pr-apply",
97
+ resolvedBy,
98
+ timestamp: LATER,
99
+ planDigest,
100
+ });
101
+
102
+ describe("planPrSet", () => {
103
+ test("plans the changed root and its dependents, each against the outputs its dependency has now", async () => {
104
+ const w = world();
105
+ w.versions.a = "v2";
106
+ const plan = planFanOut({ components: ESTATE, changed: ["a"] });
107
+ const set = await planPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod" });
108
+
109
+ expect(plan.order).toEqual(["a", "app"]);
110
+ expect(w.planned).toEqual([
111
+ { root: "a", vars: { cidr: "10.0.0.0/16" } },
112
+ { root: "app", vars: { subnet: "a-v1" } },
113
+ ]);
114
+ // net is read for a, and a for app, before anything applies.
115
+ expect(w.outputsRead).toEqual(["net", "a"]);
116
+ expect(set.members.map((m) => [m.member, m.status, m.counts.update])).toEqual([
117
+ ["a", "planned", 1],
118
+ ["app", "planned", 0],
119
+ ]);
120
+ expect(set.doc.digest).toBe(changeSetDigest(set.members.map((m) => ({ member: m.member, planDigest: m.planDigest }))));
121
+ expect(set.failed).toBe(false);
122
+ });
123
+
124
+ test("planning the same members against the same state gives the same digest; a changed root moves it", async () => {
125
+ const plan = planFanOut({ components: ESTATE, changed: ["a"] });
126
+ const w = world();
127
+ w.versions.a = "v2";
128
+ const first = await planPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod" });
129
+ const again = await planPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod" });
130
+ w.versions.a = "v3";
131
+ const moved = await planPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod" });
132
+ expect(again.doc.digest).toBe(first.doc.digest);
133
+ expect(moved.doc.digest).not.toBe(first.doc.digest);
134
+ });
135
+
136
+ test("a component that fails to plan is a failed member with no digest", async () => {
137
+ const w = world();
138
+ w.planFailing.add("app");
139
+ const set = await planPrSet({ components: ESTATE, plan: planFanOut({ components: ESTATE, changed: ["a"] }), registry: w.registry, env: "prod" });
140
+ expect(set.failed).toBe(true);
141
+ expect(set.members.find((m) => m.member === "app")).toMatchObject({ status: "plan-failed", planDigest: null, error: "app: no value for a required variable" });
142
+ });
143
+ });
144
+
145
+ describe("the gate", () => {
146
+ test("pending with no approval, approved for the digest, changed for another one", async () => {
147
+ const w = world();
148
+ const set = await planPrSet({ components: ESTATE, plan: planFanOut({ components: ESTATE, changed: ["a"] }), registry: w.registry, env: "prod" });
149
+ const input = { op: "pr-12", gate: "pr-apply", digest: set.doc.digest, now: NOW };
150
+
151
+ expect(approvalOf(await decidePrGate(readOnlyLedger(memoryGateLedgerPort()), input))).toEqual({ status: "pending" });
152
+ expect(approvalOf(await decidePrGate(memoryGateLedgerPort({ resolutions: [approval(set.doc.digest)] }), input))).toEqual({
153
+ status: "approved",
154
+ approvedBy: ["github:alice"],
155
+ });
156
+ const other = `jcs1-sha256:${"9".repeat(64)}`;
157
+ expect(approvalOf(await decidePrGate(memoryGateLedgerPort({ resolutions: [approval(other)] }), input))).toEqual({ status: "changed", approved: other });
158
+ });
159
+
160
+ test("the plan stage's ledger records nothing", async () => {
161
+ const ledger = memoryGateLedgerPort();
162
+ await decidePrGate(readOnlyLedger(ledger), { op: "pr-12", gate: "pr-apply", digest: `jcs1-sha256:${"1".repeat(64)}`, now: NOW });
163
+ expect(ledger.appended).toEqual([]);
164
+ });
165
+
166
+ test("an approver needs a standing approving review, named the forge's way", () => {
167
+ expect(approversWithoutReview(["github:alice", "GitHub:Bob"], ["github:alice", "github:bob"])).toEqual([]);
168
+ expect(approversWithoutReview(["github:mallory"], ["github:alice"])).toEqual(["github:mallory"]);
169
+ });
170
+ });
171
+
172
+ describe("applyPrSet", () => {
173
+ test("applies each member's approved plan in order and marks a dependent whose inputs moved", async () => {
174
+ const w = world();
175
+ w.versions.a = "v2";
176
+ const plan = planFanOut({ components: ESTATE, changed: ["a"] });
177
+ const set = await planPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod" });
178
+ const { members, status } = await applyPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod", set });
179
+
180
+ expect(status).toBe("applied");
181
+ expect(w.applied).toEqual(["a", "app"]);
182
+ expect(w.fromPlan).toEqual({ a: true, app: true });
183
+ expect(members.map((m) => [m.member, m.status, m.inputsMoved])).toEqual([
184
+ ["a", "applied", undefined],
185
+ ["app", "applied", ["a"]],
186
+ ]);
187
+ });
188
+
189
+ test("a failed member blocks its dependents and nothing else", async () => {
190
+ const w = world();
191
+ w.failing.add("a");
192
+ const plan = planFanOut({ components: ESTATE, changed: ["net", "dns"] });
193
+ const set = await planPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod" });
194
+ const { members, status } = await applyPrSet({ components: ESTATE, plan, registry: w.registry, env: "prod", set });
195
+ expect(status).toBe("failed");
196
+ expect(Object.fromEntries(members.map((m) => [m.member, m.status]))).toEqual({
197
+ a: "failed",
198
+ app: "blocked",
199
+ b: "applied",
200
+ dns: "applied",
201
+ net: "applied",
202
+ });
203
+ expect(members.find((m) => m.member === "a")?.error).toBe("a failed");
204
+ });
205
+ });
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Plan and apply a pull request's members (#3183).
3
+ *
4
+ * `../pr-loop.ts` says what the gate binds and what the report holds; this
5
+ * produces them. Both stages call {@link planPrSet}: the plan stage to show
6
+ * the plan and its digest, the apply stage to plan the same members again
7
+ * before anything applies and decide the gate against what it gets. Planning
8
+ * everything before applying anything is what makes the two digests
9
+ * comparable: each member is planned against the same state both times.
10
+ *
11
+ * ## Dependents read the outputs standing now
12
+ *
13
+ * A member that reads another one's output through `stackOutput()` is
14
+ * planned with the value that output has now, read through the capability's
15
+ * `outputs` (`readComponentOutputs`). When the change moves that output, the
16
+ * dependent's approved plan still carries the old value; the apply applies
17
+ * it as approved and marks the member `inputsMoved`, and the next run plans
18
+ * it against the new value. A change that must carry a moved output through
19
+ * its dependents in one rollout is what gated waves are for (#3049): each
20
+ * wave is planned after the one before it applied.
21
+ */
22
+
23
+ import { composeChangeSet, type ChangeSetDocument, type ChangeSetPart } from "../change-set";
24
+ import { evaluateGate, type GateCheck, type GateLedgerPort } from "../op/gate";
25
+ import {
26
+ memberCounts,
27
+ PR_REPORT_CONTRACT,
28
+ PR_REPORT_SCHEMA_ID,
29
+ type PrApproval,
30
+ type PrMember,
31
+ type PrRefusal,
32
+ type PrReport,
33
+ type PrStatus,
34
+ } from "../pr-loop";
35
+ import { deployContext, type DriverComponent } from "./driver";
36
+ import { deployUnits } from "./deploy-units";
37
+ import { runFanOut, type FanOutRunResult } from "./fan-out-run";
38
+ import type { ComponentChangeSignal, FanOutPlan } from "./fan-out";
39
+ import type { CapabilityRegistry } from "./capability";
40
+ import type { RunProgressEvent } from "./run-progress";
41
+ import { planWaveComponent, readComponentOutputs } from "./wave-plan";
42
+
43
+ export interface PrPlanSetOptions {
44
+ /** Every component in the project. */
45
+ components: DriverComponent[];
46
+ /** The fan-out the change derives (`planFanOut`). Its `order` is what is planned. */
47
+ plan: FanOutPlan;
48
+ registry: CapabilityRegistry;
49
+ env: string;
50
+ vars?: Record<string, unknown>;
51
+ /** Outputs supplied by the caller (`--seed-outputs`). They win over outputs read here. */
52
+ seededOutputs?: Record<string, Record<string, unknown>>;
53
+ }
54
+
55
+ /** Every member of a pull request, planned. */
56
+ export interface PrPlanSet {
57
+ /** The change-set document over every member. Its digest is what the gate binds. */
58
+ doc: ChangeSetDocument;
59
+ members: PrMember[];
60
+ /** Per component, per member: what its steps hand `run`, so it applies this plan. */
61
+ plans: Record<string, Record<string, unknown>>;
62
+ /** The outputs the plans read: the seeds, and each dependency's outputs as they stood. */
63
+ outputs: Record<string, Record<string, unknown>>;
64
+ /** Whether any component failed to plan. */
65
+ failed: boolean;
66
+ }
67
+
68
+ /** A failed component's members: the units its steps target, else the component itself. */
69
+ function failedParts(component: DriverComponent, error: string): ChangeSetPart[] {
70
+ const units = deployUnits(component.deploy).map((u) => u.unit);
71
+ return (units.length > 0 ? units : [component.name]).map((member) => ({
72
+ member: { member, lexicon: "chant", planner: "chant", status: "failed", error, planDigest: null, holes: [] },
73
+ entries: [],
74
+ }));
75
+ }
76
+
77
+ /**
78
+ * Plan every component in `plan.order`, a wave at a time, each against the
79
+ * outputs its dependencies have now. Never throws for a component that fails
80
+ * to plan: it becomes a failed member, and `failed` is set.
81
+ */
82
+ export async function planPrSet(options: PrPlanSetOptions): Promise<PrPlanSet> {
83
+ const byName = new Map(options.components.map((c) => [c.name, c]));
84
+ const outputs: Record<string, Record<string, unknown>> = { ...(options.seededOutputs ?? {}) };
85
+ const read = new Set(Object.keys(outputs));
86
+ const parts: ChangeSetPart[] = [];
87
+ const componentOf = new Map<string, string>();
88
+ const plans: Record<string, Record<string, unknown>> = {};
89
+ let failed = false;
90
+
91
+ for (const wave of options.plan.waves) {
92
+ // The dependencies' outputs first, each read once. A dependency that
93
+ // cannot say leaves its references unresolved, which its dependent's
94
+ // plan reports if it needs them.
95
+ for (const name of wave) {
96
+ for (const dep of byName.get(name)?.dependsOn ?? []) {
97
+ if (read.has(dep) || !byName.has(dep)) continue;
98
+ read.add(dep);
99
+ const found = await readComponentOutputs(byName.get(dep)!, options.registry, deployContext(options, dep), outputs);
100
+ if (found) outputs[dep] = found;
101
+ }
102
+ }
103
+ const planned = await Promise.all(
104
+ wave.map((name) => planWaveComponent(byName.get(name)!, options.registry, deployContext(options, name), outputs)),
105
+ );
106
+ for (const p of planned) {
107
+ const component = byName.get(p.component)!;
108
+ if ("error" in p) {
109
+ failed = true;
110
+ for (const part of failedParts(component, p.error)) {
111
+ parts.push(part);
112
+ componentOf.set(part.member.member, p.component);
113
+ }
114
+ continue;
115
+ }
116
+ plans[p.component] = p.plans;
117
+ for (const part of p.parts) {
118
+ parts.push(part);
119
+ componentOf.set(part.member.member, p.component);
120
+ }
121
+ }
122
+ }
123
+
124
+ const doc = composeChangeSet(parts);
125
+ const members: PrMember[] = doc.members.map((m) => ({
126
+ member: m.member,
127
+ component: componentOf.get(m.member) ?? m.member,
128
+ planDigest: m.planDigest,
129
+ status: m.status === "failed" ? "plan-failed" : "planned",
130
+ counts: memberCounts(doc, m.member),
131
+ ...(m.error ? { error: m.error } : {}),
132
+ }));
133
+ return { doc, members, plans, outputs, failed };
134
+ }
135
+
136
+ /**
137
+ * A ledger that reads the real one and records nothing. The plan stage
138
+ * decides the gate only to say where the approval stands; it runs the pull
139
+ * request's own code and holds no write access to `chant/lifecycle`.
140
+ */
141
+ export function readOnlyLedger(port: GateLedgerPort): GateLedgerPort {
142
+ return {
143
+ read: (op) => port.read(op),
144
+ ...(port.approvalRule ? { approvalRule: (gate: string) => port.approvalRule!(gate) } : {}),
145
+ async appendPending(input) {
146
+ return { record: { version: 1, kind: "pending", ...input }, pushed: false, pushWarning: "not recorded: the plan stage writes nothing" };
147
+ },
148
+ };
149
+ }
150
+
151
+ /** Where the approval stands, as the report says it. */
152
+ export function approvalOf(check: GateCheck): PrApproval {
153
+ if (check.satisfied) {
154
+ const by = check.approvals?.length ? check.approvals.map((r) => r.resolvedBy) : [check.resolution.resolvedBy];
155
+ return { status: "approved", approvedBy: [...new Set(by)] };
156
+ }
157
+ if (check.mismatch) return { status: "changed", ...(check.mismatch.approved ? { approved: check.mismatch.approved } : {}) };
158
+ return { status: "pending" };
159
+ }
160
+
161
+ /** Decide the pull request's gate against `digest`. */
162
+ export async function decidePrGate(
163
+ port: GateLedgerPort,
164
+ input: { op: string; gate: string; digest: string; description?: string; now?: string },
165
+ ): Promise<GateCheck> {
166
+ return evaluateGate(port, {
167
+ op: input.op,
168
+ gate: input.gate,
169
+ planDigest: input.digest,
170
+ ...(input.description ? { description: input.description } : {}),
171
+ ...(input.now ? { now: input.now } : {}),
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Who approved the gate but has no standing approving review on the pull
177
+ * request. Each approval's `resolvedBy` is compared with the forge's own
178
+ * naming of each approver (#3163's form).
179
+ */
180
+ export function approversWithoutReview(approvedBy: readonly string[], reviewers: readonly string[]): string[] {
181
+ const norm = (s: string) => s.trim().toLowerCase();
182
+ const allowed = new Set(reviewers.map(norm));
183
+ return approvedBy.filter((by) => !allowed.has(norm(by)));
184
+ }
185
+
186
+ export interface PrApplyOptions {
187
+ components: DriverComponent[];
188
+ plan: FanOutPlan;
189
+ registry: CapabilityRegistry;
190
+ env: string;
191
+ vars?: Record<string, unknown>;
192
+ /** The set {@link planPrSet} planned and the gate approved. */
193
+ set: PrPlanSet;
194
+ onProgress?: (event: RunProgressEvent) => void;
195
+ }
196
+
197
+ const sameJson = (a: unknown, b: unknown): boolean => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
198
+
199
+ /**
200
+ * Apply the approved plans in dependency order. A failed component blocks
201
+ * its dependents and nothing else (`runFanOut`).
202
+ */
203
+ export async function applyPrSet(options: PrApplyOptions): Promise<{ members: PrMember[]; status: "applied" | "failed"; run: FanOutRunResult }> {
204
+ const before = options.set.outputs;
205
+ const run = await runFanOut(options.plan, options.components, options.registry, {
206
+ env: options.env,
207
+ ...(options.vars ? { vars: options.vars } : {}),
208
+ plans: options.set.plans,
209
+ componentOutputs: { ...before },
210
+ ...(options.onProgress ? { onProgress: options.onProgress } : {}),
211
+ });
212
+ const completed = new Set(run.completed);
213
+ const blocked = new Set(run.blocked.map((b) => b.component));
214
+ const errorOf = (component: string): string | undefined => {
215
+ const result = run.results.find((r) => r.component === component);
216
+ if (result?.status === "gated") return `stopped at its own gate ${result.gate?.gate ?? ""}`.trim();
217
+ return result?.records.find((r) => r.status === "fail")?.error;
218
+ };
219
+ const byName = new Map(options.components.map((c) => [c.name, c]));
220
+ const members = options.set.members.map((m): PrMember => {
221
+ if (completed.has(m.component)) {
222
+ const moved = (byName.get(m.component)?.dependsOn ?? [])
223
+ .filter((dep) => completed.has(dep) && !sameJson(before[dep], run.componentOutputs[dep]))
224
+ .sort();
225
+ return { ...m, status: "applied", ...(moved.length > 0 ? { inputsMoved: moved } : {}) };
226
+ }
227
+ if (blocked.has(m.component)) return { ...m, status: "blocked" };
228
+ const error = errorOf(m.component);
229
+ return { ...m, status: "failed", ...(error ? { error } : {}) };
230
+ });
231
+ return { members, status: run.status === "ok" ? "applied" : "failed", run };
232
+ }
233
+
234
+ export interface PrReportInput {
235
+ stage: "plan" | "apply";
236
+ pr: number | null;
237
+ env: string;
238
+ base: string;
239
+ head: string;
240
+ op: string;
241
+ gate: string;
242
+ plan: FanOutPlan;
243
+ signal: ComponentChangeSignal;
244
+ set: PrPlanSet;
245
+ approval: PrApproval;
246
+ status: PrStatus;
247
+ members?: PrMember[];
248
+ refusal?: PrRefusal;
249
+ message?: string;
250
+ }
251
+
252
+ /** The report a stage writes. */
253
+ export function prReport(input: PrReportInput): PrReport {
254
+ const changed = new Set(input.signal.changed);
255
+ return {
256
+ $schema: PR_REPORT_SCHEMA_ID,
257
+ contract: PR_REPORT_CONTRACT,
258
+ stage: input.stage,
259
+ pr: input.pr,
260
+ env: input.env,
261
+ base: input.base,
262
+ head: input.head,
263
+ op: input.op,
264
+ gate: input.gate,
265
+ digest: input.set.doc.digest,
266
+ status: input.status,
267
+ ...(input.refusal ? { refusal: input.refusal } : {}),
268
+ ...(input.message ? { message: input.message } : {}),
269
+ approval: input.approval,
270
+ selection: {
271
+ changed: input.plan.order.filter((c) => changed.has(c)),
272
+ dependents: input.plan.order.filter((c) => !changed.has(c)),
273
+ unclaimed: input.signal.unclaimed,
274
+ indeterminate: input.plan.indeterminate,
275
+ waves: input.plan.waves,
276
+ },
277
+ members: input.members ?? input.set.members,
278
+ changeSet: input.set.doc,
279
+ };
280
+ }
@@ -560,6 +560,11 @@ export async function runPromotion(options: RunPromotionOptions): Promise<Driver
560
560
  // #2574: a gate approval for this promote or rollback is bound to the
561
561
  // release it deploys, as well as the environment and composition.
562
562
  releases: expected,
563
+ // #3061: the workload's service.version and vcs.ref.head.revision are the earlier release's own.
564
+ releaseIdentity: (component) => {
565
+ const item = plan.items.find((i) => i.component === component);
566
+ return item ? { version: item.digest, revision: item.source.gitSha } : undefined;
567
+ },
563
568
  ...(options.onProgress ? { onProgress: options.onProgress } : {}),
564
569
  ...(options.gates ? { gates: options.gates } : {}),
565
570
  ...(options.now ? { now: options.now } : {}),
@@ -33,4 +33,15 @@ describe("shell (#557)", () => {
33
33
  it("declares no rollback — an escape-hatch shell owns its own compensation", () => {
34
34
  expect(createShellCapability(createMockProcessRunner().runner).rollback).toBeUndefined();
35
35
  });
36
+
37
+ it("exports a release's attributes for a docker compose up, and a value the step sets wins (#3061)", async () => {
38
+ const mock = createMockProcessRunner();
39
+ const release = { version: "sha256:abc", revision: "0123abc" };
40
+ await createShellCapability(mock.runner).run({ ...ctx, release }, { cmd: "docker compose up -d", reason: "compose" });
41
+ expect(mock.calls.at(-1)!.command).toBe(
42
+ "env CHANT_RELEASE_ATTRIBUTES=',service.version=sha256%3Aabc,vcs.ref.head.revision=0123abc' docker compose up -d",
43
+ );
44
+ await createShellCapability(mock.runner).run({ ...ctx, release }, { cmd: "up", env: { CHANT_RELEASE_ATTRIBUTES: "" }, reason: "x" });
45
+ expect(mock.calls.at(-1)!.command).toBe("env CHANT_RELEASE_ATTRIBUTES='' up");
46
+ });
36
47
  });
@@ -8,6 +8,7 @@
8
8
 
9
9
  import type { Capability } from "../capability";
10
10
  import { defaultProcessRunner, q, type ProcessRunner } from "./process-runner";
11
+ import { releaseEnvironment } from "../../telemetry-attribution";
11
12
 
12
13
  export interface ShellInput {
13
14
  /** Command to run. */
@@ -36,10 +37,13 @@ export interface ShellOutput {
36
37
  export function createShellCapability(processRunner: ProcessRunner = defaultProcessRunner()): Capability<ShellInput, ShellOutput> {
37
38
  return {
38
39
  kind: "shell",
39
- async run(_ctx, input) {
40
+ async run(ctx, input) {
41
+ // A release run exports its attributes (#3061, ws-081), so a `docker compose up` here fills the
42
+ // Compose file's `${CHANT_RELEASE_ATTRIBUTES:-}`. A value the step sets itself wins.
43
+ const env = { ...releaseEnvironment(ctx.release), ...(input.env ?? {}) };
40
44
  const envPrefix =
41
- input.env && Object.keys(input.env).length > 0
42
- ? `env ${Object.entries(input.env).map(([k, v]) => `${k}=${q(v)}`).join(" ")} `
45
+ Object.keys(env).length > 0
46
+ ? `env ${Object.entries(env).map(([k, v]) => `${k}=${q(v)}`).join(" ")} `
43
47
  : "";
44
48
  const { stdout } = await processRunner.run(`${envPrefix}${input.cmd}`, { cwd: input.cwd });
45
49
  return { stdout, exitCode: 0 };
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Plan one wave of a gated-wave fan-out (#3049).
3
+ *
4
+ * Each component in the wave is asked what it would change: every deploy
5
+ * step whose capability has a `plan` (`Capability.plan`, ./capability.ts) is
6
+ * planned, with its wiring resolved against the outputs the earlier waves
7
+ * left behind. That is the point of planning at the wave rather than up
8
+ * front: a root that reads an upstream output plans against the value the
9
+ * upstream wave actually wrote.
10
+ *
11
+ * A component with no plannable step is still a member of the wave. Its plan
12
+ * digest is the composition digest a gate inside it would bind
13
+ * (`componentPlanDigest`, ./gate-plan.ts, #2574), so a wave mixing roots and
14
+ * other components still has one digest that moves when any of them does.
15
+ *
16
+ * Known limit: a step input that reads an earlier step of the same
17
+ * component (`@Phase.field`) has no value before the component runs, so it
18
+ * plans as `undefined`. Cross-component wiring (`stackOutput()`,
19
+ * `@<component>.publish.*`) resolves.
20
+ */
21
+
22
+ import { resolveStepInput, type DriverComponent, type DriverPhase, type DriverStep } from "./driver";
23
+ import { componentPlanDigest } from "./gate-plan";
24
+ import type { CapabilityRegistry, DeployContext } from "./capability";
25
+ import type { WaveMember } from "../gated-waves";
26
+ import type { ChangeSetPart } from "../change-set";
27
+
28
+ /**
29
+ * What planning one component produced: its members, what its steps hand to
30
+ * `run`, and each member's change-set part (#3183), or why it could not plan.
31
+ * A member whose capability returns no part gets one with no entries, so the
32
+ * parts always cover the members.
33
+ */
34
+ export type WaveComponentPlan =
35
+ | { component: string; members: WaveMember[]; plans: Record<string, unknown>; parts: ChangeSetPart[] }
36
+ | { component: string; error: string };
37
+
38
+ /** A part with no entries, for a member whose planner describes no changes. */
39
+ function emptyPart(member: WaveMember): ChangeSetPart {
40
+ return {
41
+ member: { member: member.member, lexicon: "chant", planner: "chant", status: "planned", planDigest: member.planDigest, holes: [] },
42
+ entries: [],
43
+ };
44
+ }
45
+
46
+ /** Every capability step in a composition, nested phases included, gates left out. */
47
+ function capabilitySteps(phases: readonly DriverPhase[]): DriverStep[] {
48
+ const out: DriverStep[] = [];
49
+ const walk = (steps: DriverPhase["steps"]): void => {
50
+ for (const step of steps) {
51
+ if (typeof (step as DriverPhase).phase === "string" && Array.isArray((step as DriverPhase).steps)) {
52
+ walk((step as DriverPhase).steps);
53
+ continue;
54
+ }
55
+ if ((step as { kind?: unknown }).kind === "gate") continue;
56
+ out.push(step as DriverStep);
57
+ }
58
+ };
59
+ for (const phase of phases) walk(phase.steps);
60
+ return out;
61
+ }
62
+
63
+ /** Plan one component against the outputs earlier waves produced. Never throws: a failure comes back as `error`. */
64
+ export async function planWaveComponent(
65
+ component: DriverComponent,
66
+ registry: CapabilityRegistry,
67
+ ctx: DeployContext,
68
+ componentOutputs: Record<string, Record<string, unknown>>,
69
+ ): Promise<WaveComponentPlan> {
70
+ try {
71
+ const members: WaveMember[] = [];
72
+ const plans: Record<string, unknown> = {};
73
+ const parts: ChangeSetPart[] = [];
74
+ for (const step of capabilitySteps(component.deploy)) {
75
+ const capability = registry.resolve(step.kind);
76
+ if (!capability.plan) continue;
77
+ const { kind: _kind, ...rest } = step;
78
+ const input = resolveStepInput(rest, {}, componentOutputs);
79
+ const planned = await capability.plan(ctx, input as never);
80
+ const member = { member: planned.member, planDigest: planned.planDigest };
81
+ members.push(member);
82
+ parts.push(planned.changeSet ?? emptyPart(member));
83
+ if (planned.artifact !== undefined) plans[planned.member] = planned.artifact;
84
+ }
85
+ if (members.length === 0) {
86
+ const member = {
87
+ member: component.name,
88
+ planDigest: componentPlanDigest({
89
+ environment: ctx.env,
90
+ component,
91
+ ...(ctx.vars ? { vars: ctx.vars } : {}),
92
+ }),
93
+ };
94
+ members.push(member);
95
+ parts.push(emptyPart(member));
96
+ }
97
+ return { component: component.name, members, plans, parts };
98
+ } catch (err) {
99
+ return { component: component.name, error: err instanceof Error ? err.message : String(err) };
100
+ }
101
+ }
102
+
103
+ /**
104
+ * The outputs `component` exposes now, read from each step whose capability
105
+ * has `outputs` (#3183), merged the way the driver merges a run's outputs.
106
+ * `undefined` when no step can say. A pull request's plan reads these for
107
+ * the components a planned one depends on, before anything has applied.
108
+ */
109
+ export async function readComponentOutputs(
110
+ component: DriverComponent,
111
+ registry: CapabilityRegistry,
112
+ ctx: DeployContext,
113
+ componentOutputs: Record<string, Record<string, unknown>>,
114
+ ): Promise<Record<string, unknown> | undefined> {
115
+ let found: Record<string, unknown> | undefined;
116
+ for (const step of capabilitySteps(component.deploy)) {
117
+ const capability = registry.resolve(step.kind);
118
+ if (!capability.outputs) continue;
119
+ const { kind: _kind, ...rest } = step;
120
+ const input = resolveStepInput(rest, {}, componentOutputs);
121
+ found = { ...found, ...(await capability.outputs(ctx, input as never)) };
122
+ }
123
+ return found;
124
+ }