@intentius/chant 0.124.51 → 0.124.54

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 (593) hide show
  1. package/dist/audit/discover.d.ts.map +1 -1
  2. package/dist/audit/discover.js +3 -1
  3. package/dist/behaviour.d.ts +1 -1
  4. package/dist/behaviour.d.ts.map +1 -1
  5. package/dist/capabilities/templates.d.ts +65 -0
  6. package/dist/capabilities/templates.d.ts.map +1 -0
  7. package/dist/capabilities/templates.js +193 -0
  8. package/dist/capabilities/templates.json +145 -0
  9. package/dist/change-set-graph.d.ts +35 -3
  10. package/dist/change-set-graph.d.ts.map +1 -1
  11. package/dist/change-set-graph.js +60 -9
  12. package/dist/change-set.d.ts +65 -7
  13. package/dist/change-set.d.ts.map +1 -1
  14. package/dist/change-set.js +23 -12
  15. package/dist/ci-identity.d.ts +7 -2
  16. package/dist/ci-identity.d.ts.map +1 -1
  17. package/dist/ci-identity.js +4 -1
  18. package/dist/cli/commands/import-index.d.ts +52 -0
  19. package/dist/cli/commands/import-index.d.ts.map +1 -0
  20. package/dist/cli/commands/import-index.js +226 -0
  21. package/dist/cli/commands/lint.d.ts.map +1 -1
  22. package/dist/cli/commands/lint.js +4 -2
  23. package/dist/cli/handlers/components.d.ts.map +1 -1
  24. package/dist/cli/handlers/components.js +11 -2
  25. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  26. package/dist/cli/handlers/fan-out.js +16 -3
  27. package/dist/cli/handlers/graph.d.ts.map +1 -1
  28. package/dist/cli/handlers/graph.js +22 -4
  29. package/dist/cli/handlers/index-cloud.d.ts +14 -0
  30. package/dist/cli/handlers/index-cloud.d.ts.map +1 -0
  31. package/dist/cli/handlers/index-cloud.js +22 -0
  32. package/dist/cli/handlers/index-diff.d.ts +18 -0
  33. package/dist/cli/handlers/index-diff.d.ts.map +1 -0
  34. package/dist/cli/handlers/index-diff.js +142 -0
  35. package/dist/cli/handlers/lifecycle-drift.d.ts +25 -0
  36. package/dist/cli/handlers/lifecycle-drift.d.ts.map +1 -0
  37. package/dist/cli/handlers/lifecycle-drift.js +64 -0
  38. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  39. package/dist/cli/handlers/lifecycle.js +188 -28
  40. package/dist/cli/handlers/misc.d.ts +9 -0
  41. package/dist/cli/handlers/misc.d.ts.map +1 -1
  42. package/dist/cli/handlers/misc.js +77 -0
  43. package/dist/cli/handlers/pr.d.ts.map +1 -1
  44. package/dist/cli/handlers/pr.js +17 -3
  45. package/dist/cli/handlers/promote.d.ts.map +1 -1
  46. package/dist/cli/handlers/promote.js +89 -15
  47. package/dist/cli/handlers/query.d.ts.map +1 -1
  48. package/dist/cli/handlers/query.js +28 -4
  49. package/dist/cli/handlers/scenario.d.ts.map +1 -1
  50. package/dist/cli/handlers/scenario.js +8 -7
  51. package/dist/cli/handlers/search.d.ts +34 -4
  52. package/dist/cli/handlers/search.d.ts.map +1 -1
  53. package/dist/cli/handlers/search.js +216 -36
  54. package/dist/cli/main.d.ts.map +1 -1
  55. package/dist/cli/main.js +55 -1
  56. package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -1
  57. package/dist/cli/mcp/lifecycle-tools.js +3 -2
  58. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  59. package/dist/cli/mcp/resource-handlers.js +4 -3
  60. package/dist/cli/mcp/workspace-tools.js +1 -1
  61. package/dist/cli/parse-args.d.ts.map +1 -1
  62. package/dist/cli/parse-args.js +55 -2
  63. package/dist/cli/registry.d.ts +19 -1
  64. package/dist/cli/registry.d.ts.map +1 -1
  65. package/dist/cloud-lister/aws.d.ts +35 -0
  66. package/dist/cloud-lister/aws.d.ts.map +1 -0
  67. package/dist/cloud-lister/aws.js +203 -0
  68. package/dist/cloud-lister/azure.d.ts +34 -0
  69. package/dist/cloud-lister/azure.d.ts.map +1 -0
  70. package/dist/cloud-lister/azure.js +129 -0
  71. package/dist/cloud-lister/common.d.ts +40 -0
  72. package/dist/cloud-lister/common.d.ts.map +1 -0
  73. package/dist/cloud-lister/common.js +83 -0
  74. package/dist/cloud-lister/credentials.d.ts +50 -0
  75. package/dist/cloud-lister/credentials.d.ts.map +1 -0
  76. package/dist/cloud-lister/credentials.js +86 -0
  77. package/dist/cloud-lister/fakes.d.ts +27 -0
  78. package/dist/cloud-lister/fakes.d.ts.map +1 -0
  79. package/dist/cloud-lister/fakes.js +50 -0
  80. package/dist/cloud-lister/gcp.d.ts +42 -0
  81. package/dist/cloud-lister/gcp.d.ts.map +1 -0
  82. package/dist/cloud-lister/gcp.js +143 -0
  83. package/dist/cloud-lister/guard.d.ts +49 -0
  84. package/dist/cloud-lister/guard.d.ts.map +1 -0
  85. package/dist/cloud-lister/guard.js +103 -0
  86. package/dist/cloud-lister/ids.d.ts +23 -0
  87. package/dist/cloud-lister/ids.d.ts.map +1 -0
  88. package/dist/cloud-lister/ids.js +42 -0
  89. package/dist/cloud-lister/index.d.ts +67 -0
  90. package/dist/cloud-lister/index.d.ts.map +1 -0
  91. package/dist/cloud-lister/index.js +102 -0
  92. package/dist/cloud-lister/kubernetes.d.ts +64 -0
  93. package/dist/cloud-lister/kubernetes.d.ts.map +1 -0
  94. package/dist/cloud-lister/kubernetes.js +276 -0
  95. package/dist/cloud-lister/markers.d.ts +31 -0
  96. package/dist/cloud-lister/markers.d.ts.map +1 -0
  97. package/dist/cloud-lister/markers.js +73 -0
  98. package/dist/cloud-lister/mask.d.ts +31 -0
  99. package/dist/cloud-lister/mask.d.ts.map +1 -0
  100. package/dist/cloud-lister/mask.js +74 -0
  101. package/dist/cloud-lister/sigv4.d.ts +29 -0
  102. package/dist/cloud-lister/sigv4.d.ts.map +1 -0
  103. package/dist/cloud-lister/sigv4.js +54 -0
  104. package/dist/cloud-lister/transport.d.ts +55 -0
  105. package/dist/cloud-lister/transport.d.ts.map +1 -0
  106. package/dist/cloud-lister/transport.js +86 -0
  107. package/dist/cloud-lister/types.d.ts +121 -0
  108. package/dist/cloud-lister/types.d.ts.map +1 -0
  109. package/dist/cloud-lister/types.js +24 -0
  110. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  111. package/dist/components/capability-plugin-loader.js +6 -0
  112. package/dist/components/capability-plugin.d.ts +9 -0
  113. package/dist/components/capability-plugin.d.ts.map +1 -1
  114. package/dist/components/capability.d.ts +224 -0
  115. package/dist/components/capability.d.ts.map +1 -1
  116. package/dist/components/cli-support.d.ts +21 -0
  117. package/dist/components/cli-support.d.ts.map +1 -1
  118. package/dist/components/cli-support.js +58 -14
  119. package/dist/components/covered-round.d.ts +173 -0
  120. package/dist/components/covered-round.d.ts.map +1 -0
  121. package/dist/components/covered-round.js +200 -0
  122. package/dist/components/declared-reads.d.ts +38 -0
  123. package/dist/components/declared-reads.d.ts.map +1 -0
  124. package/dist/components/declared-reads.js +131 -0
  125. package/dist/components/deploy-units.d.ts.map +1 -1
  126. package/dist/components/deploy-units.js +4 -0
  127. package/dist/components/driver.d.ts +42 -1
  128. package/dist/components/driver.d.ts.map +1 -1
  129. package/dist/components/driver.js +74 -3
  130. package/dist/components/fan-out-run.d.ts +23 -1
  131. package/dist/components/fan-out-run.d.ts.map +1 -1
  132. package/dist/components/fan-out-run.js +82 -14
  133. package/dist/components/fan-out-support.d.ts +6 -0
  134. package/dist/components/fan-out-support.d.ts.map +1 -1
  135. package/dist/components/fan-out-support.js +4 -1
  136. package/dist/components/fan-out.d.ts +28 -0
  137. package/dist/components/fan-out.d.ts.map +1 -1
  138. package/dist/components/groups.d.ts +229 -0
  139. package/dist/components/groups.d.ts.map +1 -0
  140. package/dist/components/groups.js +760 -0
  141. package/dist/components/index.d.ts +7 -2
  142. package/dist/components/index.d.ts.map +1 -1
  143. package/dist/components/index.js +5 -0
  144. package/dist/components/plan-shape-conformance.d.ts +48 -0
  145. package/dist/components/plan-shape-conformance.d.ts.map +1 -0
  146. package/dist/components/plan-shape-conformance.js +122 -0
  147. package/dist/components/pr-run.d.ts +62 -7
  148. package/dist/components/pr-run.d.ts.map +1 -1
  149. package/dist/components/pr-run.js +354 -53
  150. package/dist/components/promote-rounds.d.ts +9 -0
  151. package/dist/components/promote-rounds.d.ts.map +1 -1
  152. package/dist/components/promote-rounds.js +23 -2
  153. package/dist/components/promote.d.ts +17 -0
  154. package/dist/components/promote.d.ts.map +1 -1
  155. package/dist/components/promote.js +36 -6
  156. package/dist/components/release-gate.d.ts +23 -1
  157. package/dist/components/release-gate.d.ts.map +1 -1
  158. package/dist/components/release-gate.js +97 -33
  159. package/dist/components/run-groups.d.ts +58 -0
  160. package/dist/components/run-groups.d.ts.map +1 -0
  161. package/dist/components/run-groups.js +78 -0
  162. package/dist/components/starter-plugin.d.ts +1 -0
  163. package/dist/components/starter-plugin.d.ts.map +1 -1
  164. package/dist/components/starter-plugin.js +6 -1
  165. package/dist/components/verbs/index.d.ts +1 -0
  166. package/dist/components/verbs/index.d.ts.map +1 -1
  167. package/dist/components/verbs/index.js +1 -0
  168. package/dist/components/verbs/member-outputs.d.ts +187 -0
  169. package/dist/components/verbs/member-outputs.d.ts.map +1 -0
  170. package/dist/components/verbs/member-outputs.js +218 -0
  171. package/dist/components/wave-plan.d.ts +2 -0
  172. package/dist/components/wave-plan.d.ts.map +1 -1
  173. package/dist/components/wave-plan.js +4 -1
  174. package/dist/config.d.ts +60 -0
  175. package/dist/config.d.ts.map +1 -1
  176. package/dist/config.js +27 -0
  177. package/dist/deep-observation.d.ts +1 -13
  178. package/dist/deep-observation.d.ts.map +1 -1
  179. package/dist/deep-observation.js +5 -22
  180. package/dist/drift-record.d.ts +542 -0
  181. package/dist/drift-record.d.ts.map +1 -0
  182. package/dist/drift-record.js +886 -0
  183. package/dist/engine-facts.d.ts +144 -0
  184. package/dist/engine-facts.d.ts.map +1 -0
  185. package/dist/engine-facts.js +14 -0
  186. package/dist/graph-ir.d.ts +153 -2
  187. package/dist/graph-ir.d.ts.map +1 -1
  188. package/dist/graph-ir.js +224 -6
  189. package/dist/graph-refs.d.ts +145 -0
  190. package/dist/graph-refs.d.ts.map +1 -1
  191. package/dist/graph-refs.js +305 -0
  192. package/dist/graph-schema/fixtures/adopt.json +15 -0
  193. package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
  194. package/dist/graph-schema/fixtures/index.json +68 -0
  195. package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
  196. package/dist/graph-schema/fixtures.d.ts +23 -0
  197. package/dist/graph-schema/fixtures.d.ts.map +1 -0
  198. package/dist/graph-schema/fixtures.js +246 -0
  199. package/dist/graph-schema/generate.d.ts +15 -0
  200. package/dist/graph-schema/generate.d.ts.map +1 -0
  201. package/dist/graph-schema/generate.js +267 -0
  202. package/dist/graph-schema/graph.schema.json +1387 -0
  203. package/dist/health.schema.json +2 -1
  204. package/dist/identity.d.ts.map +1 -1
  205. package/dist/identity.js +2 -0
  206. package/dist/index.d.ts +1 -0
  207. package/dist/index.d.ts.map +1 -1
  208. package/dist/index.js +2 -0
  209. package/dist/instance-nodes.d.ts +159 -0
  210. package/dist/instance-nodes.d.ts.map +1 -0
  211. package/dist/instance-nodes.js +231 -0
  212. package/dist/lexicon.d.ts +72 -0
  213. package/dist/lexicon.d.ts.map +1 -1
  214. package/dist/lifecycle/adoption-file.d.ts +56 -0
  215. package/dist/lifecycle/adoption-file.d.ts.map +1 -0
  216. package/dist/lifecycle/adoption-file.js +123 -0
  217. package/dist/lifecycle/cloud-index.d.ts +186 -0
  218. package/dist/lifecycle/cloud-index.d.ts.map +1 -0
  219. package/dist/lifecycle/cloud-index.js +618 -0
  220. package/dist/lifecycle/derive-edges.d.ts +59 -0
  221. package/dist/lifecycle/derive-edges.d.ts.map +1 -0
  222. package/dist/lifecycle/derive-edges.js +126 -0
  223. package/dist/lifecycle/git.d.ts +10 -9
  224. package/dist/lifecycle/git.d.ts.map +1 -1
  225. package/dist/lifecycle/git.js +7 -1
  226. package/dist/lifecycle/identity-match.d.ts +133 -0
  227. package/dist/lifecycle/identity-match.d.ts.map +1 -0
  228. package/dist/lifecycle/identity-match.js +147 -0
  229. package/dist/lifecycle/index-diff.d.ts +224 -0
  230. package/dist/lifecycle/index-diff.d.ts.map +1 -0
  231. package/dist/lifecycle/index-diff.js +327 -0
  232. package/dist/lifecycle/index-document.d.ts +130 -0
  233. package/dist/lifecycle/index-document.d.ts.map +1 -0
  234. package/dist/lifecycle/index-document.js +189 -0
  235. package/dist/lifecycle/index-drift.d.ts +121 -0
  236. package/dist/lifecycle/index-drift.d.ts.map +1 -0
  237. package/dist/lifecycle/index-drift.js +178 -0
  238. package/dist/lifecycle/index-import.d.ts +168 -0
  239. package/dist/lifecycle/index-import.d.ts.map +1 -0
  240. package/dist/lifecycle/index-import.js +515 -0
  241. package/dist/lifecycle/index-join.d.ts +129 -0
  242. package/dist/lifecycle/index-join.d.ts.map +1 -0
  243. package/dist/lifecycle/index-join.js +248 -0
  244. package/dist/lifecycle/index.d.ts +4 -0
  245. package/dist/lifecycle/index.d.ts.map +1 -1
  246. package/dist/lifecycle/index.js +4 -0
  247. package/dist/lifecycle/observation-baseline.d.ts +61 -21
  248. package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
  249. package/dist/lifecycle/observation-baseline.js +144 -19
  250. package/dist/lifecycle/observe.d.ts.map +1 -1
  251. package/dist/lifecycle/observe.js +6 -1
  252. package/dist/lifecycle/query.d.ts +45 -4
  253. package/dist/lifecycle/query.d.ts.map +1 -1
  254. package/dist/lifecycle/query.js +89 -7
  255. package/dist/lifecycle/replay.d.ts +10 -3
  256. package/dist/lifecycle/replay.d.ts.map +1 -1
  257. package/dist/lifecycle/replay.js +10 -6
  258. package/dist/lifecycle/scenario.d.ts +4 -3
  259. package/dist/lifecycle/scenario.d.ts.map +1 -1
  260. package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
  261. package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
  262. package/dist/lifecycle/snapshot-store-fake.js +21 -0
  263. package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
  264. package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
  265. package/dist/lifecycle/snapshot-store-s3.js +254 -0
  266. package/dist/lifecycle/snapshot-store.d.ts +170 -0
  267. package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
  268. package/dist/lifecycle/snapshot-store.js +832 -0
  269. package/dist/lifecycle/snapshot.d.ts +11 -0
  270. package/dist/lifecycle/snapshot.d.ts.map +1 -1
  271. package/dist/lifecycle/snapshot.js +18 -5
  272. package/dist/lifecycle/type-listing.d.ts +148 -0
  273. package/dist/lifecycle/type-listing.d.ts.map +1 -0
  274. package/dist/lifecycle/type-listing.js +166 -0
  275. package/dist/lint/component-checks.d.ts +15 -1
  276. package/dist/lint/component-checks.d.ts.map +1 -1
  277. package/dist/lint/component-checks.js +1 -0
  278. package/dist/lint/presets/relaxed.json +2 -1
  279. package/dist/lint/presets/strict.json +2 -1
  280. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
  281. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
  282. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
  283. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
  284. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
  285. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
  286. package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
  287. package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
  288. package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
  289. package/dist/lint/rules/comp/index.d.ts +2 -1
  290. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  291. package/dist/lint/rules/comp/index.js +4 -1
  292. package/dist/observation.d.ts +19 -2
  293. package/dist/observation.d.ts.map +1 -1
  294. package/dist/observation.js +18 -1
  295. package/dist/op/activities/activity-contracts.d.ts +1 -1
  296. package/dist/op/activities/activity-contracts.js +1 -1
  297. package/dist/op/activities/change-set.d.ts +6 -4
  298. package/dist/op/activities/change-set.d.ts.map +1 -1
  299. package/dist/op/activities/change-set.js +4 -2
  300. package/dist/op/activity-failure.d.ts +27 -0
  301. package/dist/op/activity-failure.d.ts.map +1 -1
  302. package/dist/op/activity-failure.js +31 -0
  303. package/dist/op/index.d.ts +2 -1
  304. package/dist/op/index.d.ts.map +1 -1
  305. package/dist/op/index.js +1 -1
  306. package/dist/op/local-executor.d.ts +5 -1
  307. package/dist/op/local-executor.d.ts.map +1 -1
  308. package/dist/op/local-executor.js +22 -8
  309. package/dist/pr-loop.d.ts +92 -3
  310. package/dist/pr-loop.d.ts.map +1 -1
  311. package/dist/pr-loop.js +62 -3
  312. package/dist/pulumi-urn.d.ts +101 -0
  313. package/dist/pulumi-urn.d.ts.map +1 -0
  314. package/dist/pulumi-urn.js +131 -0
  315. package/dist/release-floors.d.ts +23 -0
  316. package/dist/release-floors.d.ts.map +1 -0
  317. package/dist/release-floors.js +22 -0
  318. package/dist/sensitive-key.d.ts +23 -0
  319. package/dist/sensitive-key.d.ts.map +1 -0
  320. package/dist/sensitive-key.js +31 -0
  321. package/dist/wave-runtime.mjs +234 -234
  322. package/dist/workspace/adoption-report.d.ts +53 -0
  323. package/dist/workspace/adoption-report.d.ts.map +1 -0
  324. package/dist/workspace/adoption-report.js +76 -0
  325. package/dist/workspace/capabilities-contract.d.ts +7 -0
  326. package/dist/workspace/capabilities-contract.d.ts.map +1 -1
  327. package/dist/workspace/capabilities-contract.js +2 -1
  328. package/dist/workspace/capabilities.schema.json +98 -0
  329. package/dist/workspace/change-set.schema.json +42 -3
  330. package/dist/workspace/ci-cloud-identity.d.ts +13 -1
  331. package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
  332. package/dist/workspace/ci-cloud-identity.js +30 -1
  333. package/dist/workspace/compose-graph.d.ts +10 -1
  334. package/dist/workspace/compose-graph.d.ts.map +1 -1
  335. package/dist/workspace/compose-graph.js +6 -1
  336. package/dist/workspace/conformance/index.d.ts +1 -1
  337. package/dist/workspace/conformance/index.d.ts.map +1 -1
  338. package/dist/workspace/conformance/index.js +6 -1
  339. package/dist/workspace/drift-cli.d.ts +81 -0
  340. package/dist/workspace/drift-cli.d.ts.map +1 -0
  341. package/dist/workspace/drift-cli.js +317 -0
  342. package/dist/workspace/drift.schema.json +672 -0
  343. package/dist/workspace/graph-cli.d.ts +2 -0
  344. package/dist/workspace/graph-cli.d.ts.map +1 -1
  345. package/dist/workspace/graph-cli.js +3 -0
  346. package/dist/workspace/graph.schema.json +72 -0
  347. package/dist/workspace/init.d.ts +5 -1
  348. package/dist/workspace/init.d.ts.map +1 -1
  349. package/dist/workspace/init.js +55 -9
  350. package/dist/workspace/ledger.schema.json +1 -1
  351. package/dist/workspace/nested-graph.d.ts.map +1 -1
  352. package/dist/workspace/nested-graph.js +2 -0
  353. package/dist/workspace/pr-report.schema.json +76 -3
  354. package/dist/workspace/promote-waves.d.ts +112 -0
  355. package/dist/workspace/promote-waves.d.ts.map +1 -0
  356. package/dist/workspace/promote-waves.js +290 -0
  357. package/dist/workspace/promote.d.ts +84 -2
  358. package/dist/workspace/promote.d.ts.map +1 -1
  359. package/dist/workspace/promote.js +347 -61
  360. package/dist/workspace/reason-codes.d.ts +12 -0
  361. package/dist/workspace/reason-codes.d.ts.map +1 -1
  362. package/dist/workspace/reason-codes.js +14 -0
  363. package/dist/workspace/search-contract.d.ts +3 -3
  364. package/dist/workspace/search-contract.d.ts.map +1 -1
  365. package/dist/workspace/search-contract.js +2 -0
  366. package/dist/workspace/search.schema.json +103 -5
  367. package/package.json +14 -1
  368. package/src/__fixtures__/drift-record/README.md +58 -0
  369. package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
  370. package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
  371. package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
  372. package/src/__fixtures__/drift-record/component-status.json +91 -0
  373. package/src/__fixtures__/drift-record/deep-diff.json +259 -0
  374. package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
  375. package/src/__fixtures__/drift-record/live-diff.json +125 -0
  376. package/src/audit/discover.e2e.test.ts +14 -0
  377. package/src/audit/discover.ts +3 -1
  378. package/src/behaviour.ts +1 -1
  379. package/src/capabilities/templates.json +145 -0
  380. package/src/capabilities/templates.test.ts +212 -0
  381. package/src/capabilities/templates.ts +204 -0
  382. package/src/change-set-bundle.test.ts +83 -0
  383. package/src/change-set-graph.ts +80 -9
  384. package/src/change-set.test.ts +24 -0
  385. package/src/change-set.ts +77 -14
  386. package/src/ci-identity-targets.test.ts +23 -0
  387. package/src/ci-identity.ts +10 -4
  388. package/src/cli/commands/import-index.test.ts +117 -0
  389. package/src/cli/commands/import-index.ts +265 -0
  390. package/src/cli/commands/lint.ts +4 -2
  391. package/src/cli/handlers/components.test.ts +9 -0
  392. package/src/cli/handlers/components.ts +12 -2
  393. package/src/cli/handlers/fan-out.ts +17 -3
  394. package/src/cli/handlers/graph.test.ts +42 -0
  395. package/src/cli/handlers/graph.ts +26 -4
  396. package/src/cli/handlers/index-cloud.ts +32 -0
  397. package/src/cli/handlers/index-diff.test.ts +68 -0
  398. package/src/cli/handlers/index-diff.ts +146 -0
  399. package/src/cli/handlers/ledger-root-member.test.ts +161 -0
  400. package/src/cli/handlers/lifecycle-drift.ts +68 -0
  401. package/src/cli/handlers/lifecycle.test.ts +33 -11
  402. package/src/cli/handlers/lifecycle.ts +205 -30
  403. package/src/cli/handlers/misc.ts +73 -0
  404. package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
  405. package/src/cli/handlers/pr.ts +16 -3
  406. package/src/cli/handlers/promote.test.ts +64 -0
  407. package/src/cli/handlers/promote.ts +81 -6
  408. package/src/cli/handlers/query.ts +27 -4
  409. package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
  410. package/src/cli/handlers/run-groups-cli.test.ts +229 -0
  411. package/src/cli/handlers/scenario.test.ts +13 -0
  412. package/src/cli/handlers/scenario.ts +8 -7
  413. package/src/cli/handlers/search-index.test.ts +255 -0
  414. package/src/cli/handlers/search-json.test.ts +1 -1
  415. package/src/cli/handlers/search-ownership.test.ts +194 -0
  416. package/src/cli/handlers/search.ts +271 -38
  417. package/src/cli/main.test.ts +7 -0
  418. package/src/cli/main.ts +56 -1
  419. package/src/cli/mcp/lifecycle-tools.ts +3 -2
  420. package/src/cli/mcp/resource-handlers.ts +4 -3
  421. package/src/cli/mcp/workspace-tools.ts +1 -1
  422. package/src/cli/parse-args.ts +40 -2
  423. package/src/cli/registry.ts +19 -1
  424. package/src/cloud-lister/aws.test.ts +202 -0
  425. package/src/cloud-lister/aws.ts +226 -0
  426. package/src/cloud-lister/azure.test.ts +132 -0
  427. package/src/cloud-lister/azure.ts +142 -0
  428. package/src/cloud-lister/common.ts +115 -0
  429. package/src/cloud-lister/credentials.ts +110 -0
  430. package/src/cloud-lister/fakes.ts +66 -0
  431. package/src/cloud-lister/gcp.test.ts +121 -0
  432. package/src/cloud-lister/gcp.ts +159 -0
  433. package/src/cloud-lister/guard.test.ts +198 -0
  434. package/src/cloud-lister/guard.ts +111 -0
  435. package/src/cloud-lister/ids.ts +54 -0
  436. package/src/cloud-lister/index.ts +134 -0
  437. package/src/cloud-lister/kubernetes.test.ts +160 -0
  438. package/src/cloud-lister/kubernetes.ts +284 -0
  439. package/src/cloud-lister/markers.ts +74 -0
  440. package/src/cloud-lister/mask.ts +77 -0
  441. package/src/cloud-lister/shape.test.ts +81 -0
  442. package/src/cloud-lister/sigv4.ts +70 -0
  443. package/src/cloud-lister/transport.ts +118 -0
  444. package/src/cloud-lister/types.ts +135 -0
  445. package/src/components/capability-plugin-loader.ts +4 -0
  446. package/src/components/capability-plugin.ts +10 -0
  447. package/src/components/capability.ts +238 -0
  448. package/src/components/cli-support.ts +70 -14
  449. package/src/components/covered-round.test.ts +166 -0
  450. package/src/components/covered-round.ts +293 -0
  451. package/src/components/declared-reads.test.ts +103 -0
  452. package/src/components/declared-reads.ts +134 -0
  453. package/src/components/deploy-units.ts +4 -0
  454. package/src/components/driver.ts +101 -4
  455. package/src/components/fan-out-run.ts +95 -15
  456. package/src/components/fan-out-support.ts +10 -1
  457. package/src/components/fan-out.ts +30 -0
  458. package/src/components/groups.test.ts +320 -0
  459. package/src/components/groups.ts +880 -0
  460. package/src/components/index.ts +59 -0
  461. package/src/components/plan-shape-conformance.ts +130 -0
  462. package/src/components/pr-env-run-bundle.test.ts +2 -0
  463. package/src/components/pr-run.test.ts +728 -4
  464. package/src/components/pr-run.ts +395 -55
  465. package/src/components/promote-rounds.ts +25 -2
  466. package/src/components/promote.ts +55 -5
  467. package/src/components/registry.test.ts +1 -1
  468. package/src/components/release-gate.ts +115 -30
  469. package/src/components/release-groups.test.ts +362 -0
  470. package/src/components/run-groups.ts +113 -0
  471. package/src/components/starter-plugin.ts +6 -0
  472. package/src/components/verbs/index.ts +1 -0
  473. package/src/components/verbs/member-outputs.test.ts +205 -0
  474. package/src/components/verbs/member-outputs.ts +308 -0
  475. package/src/components/wave-plan.ts +5 -2
  476. package/src/config.ts +44 -0
  477. package/src/deep-observation.ts +5 -23
  478. package/src/drift-record.test.ts +596 -0
  479. package/src/drift-record.ts +1206 -0
  480. package/src/engine-facts.guard.test.ts +111 -0
  481. package/src/engine-facts.test.ts +178 -0
  482. package/src/engine-facts.ts +154 -0
  483. package/src/gated-waves-bundle.test.ts +2 -0
  484. package/src/graph-ir-live.test.ts +92 -0
  485. package/src/graph-ir.ts +317 -7
  486. package/src/graph-refs.ts +416 -0
  487. package/src/graph-schema/fixtures/adopt.json +15 -0
  488. package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
  489. package/src/graph-schema/fixtures/index.json +68 -0
  490. package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
  491. package/src/graph-schema/fixtures.ts +267 -0
  492. package/src/graph-schema/generate.ts +263 -0
  493. package/src/graph-schema/graph-schema.test.ts +153 -0
  494. package/src/graph-schema/graph.schema.json +1387 -0
  495. package/src/health.schema.json +2 -1
  496. package/src/identity.ts +2 -0
  497. package/src/index.ts +2 -0
  498. package/src/instance-nodes.ts +288 -0
  499. package/src/lexicon.ts +76 -0
  500. package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
  501. package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
  502. package/src/lifecycle/adoption-file.test.ts +100 -0
  503. package/src/lifecycle/adoption-file.ts +154 -0
  504. package/src/lifecycle/cloud-index.test.ts +517 -0
  505. package/src/lifecycle/cloud-index.ts +736 -0
  506. package/src/lifecycle/derive-edges.test.ts +193 -0
  507. package/src/lifecycle/derive-edges.ts +182 -0
  508. package/src/lifecycle/git.ts +10 -4
  509. package/src/lifecycle/identity-match.test.ts +308 -0
  510. package/src/lifecycle/identity-match.ts +257 -0
  511. package/src/lifecycle/index-diff.test.ts +198 -0
  512. package/src/lifecycle/index-diff.ts +476 -0
  513. package/src/lifecycle/index-document.test.ts +64 -0
  514. package/src/lifecycle/index-document.ts +299 -0
  515. package/src/lifecycle/index-drift.test.ts +191 -0
  516. package/src/lifecycle/index-drift.ts +304 -0
  517. package/src/lifecycle/index-import.test.ts +255 -0
  518. package/src/lifecycle/index-import.ts +634 -0
  519. package/src/lifecycle/index-join.test.ts +250 -0
  520. package/src/lifecycle/index-join.ts +304 -0
  521. package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
  522. package/src/lifecycle/index.ts +4 -0
  523. package/src/lifecycle/member-ledger.test.ts +25 -7
  524. package/src/lifecycle/observation-baseline.test.ts +72 -1
  525. package/src/lifecycle/observation-baseline.ts +190 -27
  526. package/src/lifecycle/observe.ts +6 -1
  527. package/src/lifecycle/query.test.ts +69 -1
  528. package/src/lifecycle/query.ts +103 -10
  529. package/src/lifecycle/replay.test.ts +9 -5
  530. package/src/lifecycle/replay.ts +9 -5
  531. package/src/lifecycle/scenario.ts +4 -3
  532. package/src/lifecycle/snapshot-store-fake.ts +29 -0
  533. package/src/lifecycle/snapshot-store-s3.ts +316 -0
  534. package/src/lifecycle/snapshot-store.test.ts +385 -0
  535. package/src/lifecycle/snapshot-store.ts +981 -0
  536. package/src/lifecycle/snapshot.test.ts +13 -0
  537. package/src/lifecycle/snapshot.ts +23 -6
  538. package/src/lifecycle/type-listing.test.ts +166 -0
  539. package/src/lifecycle/type-listing.ts +309 -0
  540. package/src/lint/component-checks.ts +17 -1
  541. package/src/lint/config.test.ts +3 -0
  542. package/src/lint/presets/relaxed.json +2 -1
  543. package/src/lint/presets/strict.json +2 -1
  544. package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
  545. package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
  546. package/src/lint/rules/comp/comp.test.ts +2 -1
  547. package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
  548. package/src/lint/rules/comp/comp008.test.ts +77 -0
  549. package/src/lint/rules/comp/index.ts +4 -1
  550. package/src/observation-edges.test.ts +33 -0
  551. package/src/observation.ts +36 -1
  552. package/src/op/activities/activity-contracts.ts +1 -1
  553. package/src/op/activities/change-set.ts +8 -5
  554. package/src/op/activity-failure.ts +36 -0
  555. package/src/op/index.ts +2 -1
  556. package/src/op/local-executor.test.ts +80 -0
  557. package/src/op/local-executor.ts +27 -9
  558. package/src/pr-loop-bundle.test.ts +5 -2
  559. package/src/pr-loop.test.ts +57 -1
  560. package/src/pr-loop.ts +154 -6
  561. package/src/pulumi-urn.ts +151 -0
  562. package/src/release-floors.ts +22 -0
  563. package/src/sensitive-key.ts +34 -0
  564. package/src/workspace/adoption-report.test.ts +61 -0
  565. package/src/workspace/adoption-report.ts +103 -0
  566. package/src/workspace/capabilities-contract.test.ts +4 -0
  567. package/src/workspace/capabilities-contract.ts +9 -1
  568. package/src/workspace/capabilities.schema.json +98 -0
  569. package/src/workspace/change-set.schema.json +42 -3
  570. package/src/workspace/ci-cloud-identity.ts +31 -1
  571. package/src/workspace/compose-graph.test.ts +12 -0
  572. package/src/workspace/compose-graph.ts +12 -1
  573. package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
  574. package/src/workspace/conformance/index.ts +6 -1
  575. package/src/workspace/drift-cli.test.ts +65 -0
  576. package/src/workspace/drift-cli.ts +365 -0
  577. package/src/workspace/drift.schema.json +672 -0
  578. package/src/workspace/graph-cli.ts +5 -0
  579. package/src/workspace/graph.schema.json +72 -0
  580. package/src/workspace/init.test.ts +38 -1
  581. package/src/workspace/init.ts +50 -8
  582. package/src/workspace/intent-gaps.test.ts +18 -2
  583. package/src/workspace/ledger.schema.json +1 -1
  584. package/src/workspace/nested-graph.ts +1 -0
  585. package/src/workspace/pr-report.schema.json +76 -3
  586. package/src/workspace/promote-waves.test.ts +296 -0
  587. package/src/workspace/promote-waves.ts +340 -0
  588. package/src/workspace/promote.test.ts +158 -4
  589. package/src/workspace/promote.ts +398 -57
  590. package/src/workspace/reason-codes.test.ts +4 -1
  591. package/src/workspace/reason-codes.ts +14 -0
  592. package/src/workspace/search-contract.ts +2 -0
  593. package/src/workspace/search.schema.json +103 -5
@@ -22,6 +22,12 @@
22
22
  * A stage whose components declare no gate has nowhere to wait, so the
23
23
  * promote stops there with nothing more deployed.
24
24
  *
25
+ * - **Groups** (ws-107, #4465). With `grouped`, the waves are the grouped
26
+ * fan-out's, so each group sits in one wave and is checked, planned again
27
+ * and run as one: when any member's input moved, every member left of the
28
+ * group is planned again with it. Under `--wave-gate` no group forms
29
+ * (`formRunGroups`), so there is none here.
30
+ *
25
31
  * A promote that stops at a round's or a wave's gate writes a resume record
26
32
  * ({@link PromoteResume}): the components that applied and the outputs they
27
33
  * left. The same promote run again with it skips what applied and carries on
@@ -37,6 +43,8 @@ import { waveGateName } from "../gated-waves";
37
43
  import { capabilitySteps } from "./wave-plan";
38
44
  import type { ChangeSetDocument } from "../change-set";
39
45
  import type { ReleasePlan } from "./release-gate";
46
+ import type { FanOutGroup, FanOutPlan } from "./fan-out";
47
+ import { groupsByComponent } from "./groups";
40
48
 
41
49
  export const PROMOTE_RESUME_SCHEMA = "chant-promote-resume/v1";
42
50
 
@@ -90,6 +98,8 @@ export interface PromoteRoundsOptions {
90
98
  replan: (only: ReadonlySet<string>, outputs: Record<string, Record<string, unknown>>) => Promise<ChangeSetDocument>;
91
99
  /** Store a stage's plan and bind its gates (empty resolutions included). */
92
100
  bind: (doc: ChangeSetDocument, gates: string[]) => Promise<ReleaseGateBinding>;
101
+ /** The grouped fan-out (#4465): its waves replace the ones `dependsOn` gives, and each group is checked as one. */
102
+ grouped?: FanOutPlan;
93
103
  }
94
104
 
95
105
  export interface PromoteRoundsResult {
@@ -169,7 +179,17 @@ function joinRuns(runs: DriverRunResult[], outputs: Record<string, Record<string
169
179
  */
170
180
  export async function runPromotionInRounds(options: PromoteRoundsOptions): Promise<PromoteRoundsResult> {
171
181
  const byName = new Map(options.components.map((c) => [c.name, c]));
172
- const { waves } = resolveComponentGraph(options.components.map((c) => ({ ...c, dependsOn: (c.dependsOn ?? []).filter((d) => byName.has(d)) })));
182
+ const grouped = options.grouped?.groups?.length ? options.grouped : undefined;
183
+ const placed = new Set(grouped?.order ?? []);
184
+ // A grouped fan-out over what a resume left holds no wave for what applied earlier; that is skipped anyway.
185
+ const waves = grouped
186
+ ? grouped.waves.map((w) => w.filter((name) => byName.has(name)))
187
+ : resolveComponentGraph(options.components.map((c) => ({ ...c, dependsOn: (c.dependsOn ?? []).filter((d) => byName.has(d)) }))).waves;
188
+ if (grouped) {
189
+ const missing = options.components.filter((c) => !placed.has(c.name) && !(options.completed ?? []).includes(c.name)).map((c) => c.name);
190
+ if (missing.length > 0) throw new Error(`the grouped waves leave out ${missing.join(", ")}, which this promote runs`);
191
+ }
192
+ const groupOf = grouped ? groupsByComponent(grouped) : new Map<string, FanOutGroup>();
173
193
  const completed = [...(options.completed ?? [])];
174
194
  const done = new Set(completed);
175
195
  let outputs = { ...options.outputs };
@@ -181,9 +201,12 @@ export async function runPromotionInRounds(options: PromoteRoundsOptions): Promi
181
201
  const waveNumber = index + 1;
182
202
  const pending = wave.filter((name) => !done.has(name));
183
203
  if (pending.length === 0) continue;
184
- const check = options.waveGate !== undefined && waveNumber > 1
204
+ const moved = options.waveGate !== undefined && waveNumber > 1
185
205
  ? pending
186
206
  : pending.filter((name) => movedInputs(byName.get(name)!, options.reads[name], outputs).length > 0);
207
+ // A group is planned again as one: a member whose input moved takes the rest of its group with it.
208
+ const withMates = new Set(moved.flatMap((name) => [name, ...(groupOf.get(name)?.components ?? [])]));
209
+ const check = pending.filter((name) => withMates.has(name));
187
210
  const batches: Array<{ names: string[]; binding: ReleaseGateBinding; stage?: PromoteStage; rename?: (g: string) => string }> = [];
188
211
  const rest = pending.filter((name) => !check.includes(name));
189
212
  if (check.length > 0) {
@@ -44,6 +44,8 @@ import { CapabilityRegistry, type Capability } from "./capability";
44
44
  import {
45
45
  runInterpretDriver,
46
46
  DriverRunFailure,
47
+ deployContext,
48
+ type InterpretGroups,
47
49
  type DriverComponent,
48
50
  type DriverComponentResult,
49
51
  type DriverGate,
@@ -53,6 +55,9 @@ import {
53
55
  type DriverStep,
54
56
  } from "./driver";
55
57
  import type { GateLedgerPort } from "../op/gate";
58
+ import type { GroupRun } from "./capability";
59
+ import type { FanOutGroup, FanOutPlan } from "./fan-out";
60
+ import { planAndRunComponentGroup, runComponentGroup } from "./groups";
56
61
  import type { Release } from "../lifecycle/release-store";
57
62
  import type { RunProgressEvent } from "./run-progress";
58
63
  import type { RunEventSink } from "../run-event-stream";
@@ -631,6 +636,27 @@ export interface RunPromotionOptions {
631
636
  onProgress?: (event: RunProgressEvent) => void;
632
637
  /** Where the run's events go (#4041, `--events-json`). */
633
638
  onRunEvent?: RunEventSink;
639
+ /**
640
+ * The grouped fan-out (ws-107, #4465, `formRunGroups`): each of its groups
641
+ * applies as one, in the wave it places it in. Absent, or without
642
+ * `groups`: every component runs its own deploy, as before.
643
+ */
644
+ grouped?: FanOutPlan;
645
+ /**
646
+ * The approved plan of each group, as the release gate's plan made it
647
+ * (`ReleasePlan.groups`). A group runs the one whose members are its own;
648
+ * the newest first. A group with none plans in its wave and applies only
649
+ * under `planGroupsAtWave`, and fails otherwise.
650
+ */
651
+ groupRuns?: readonly GroupRun[];
652
+ /** A promote with no release gate to bind plans each group in its wave (#4465). */
653
+ planGroupsAtWave?: boolean;
654
+ }
655
+
656
+ /** The run in `runs` that plans exactly the members of `group`. */
657
+ function approvedRunOf(runs: readonly GroupRun[] | undefined, group: FanOutGroup): GroupRun | undefined {
658
+ const want = [...group.components].sort().join("\n");
659
+ return runs?.find((r) => Object.keys(r.plans).sort().join("\n") === want);
634
660
  }
635
661
 
636
662
  /**
@@ -647,18 +673,42 @@ export async function runPromotion(options: RunPromotionOptions): Promise<Driver
647
673
  .filter((c) => names.has(c.name))
648
674
  .map((c) => ({ ...c, dependsOn: (c.dependsOn ?? []).filter((d) => names.has(d)) }));
649
675
  const expected = new Map(plan.items.map((i) => [i.component, i.digest]));
676
+ const registry = checkPublishedDigest(options.registry, expected);
677
+ const releaseIdentity = (component: string) => {
678
+ const item = plan.items.find((i) => i.component === component);
679
+ return item ? { version: item.digest, revision: item.source.gitSha } : undefined;
680
+ };
681
+ // ws-107 (#4465): a group applies as one, through its approved plan.
682
+ const grouped = options.grouped?.groups?.length ? options.grouped : undefined;
683
+ const byName = new Map(targets.map((c) => [c.name, c]));
684
+ const groups: InterpretGroups | undefined = grouped
685
+ ? {
686
+ waves: grouped.waves,
687
+ groups: grouped.groups!,
688
+ run: (group, componentOutputs) => {
689
+ const each = {
690
+ group,
691
+ components: byName,
692
+ registry,
693
+ env: plan.to,
694
+ ctxOf: (name: string) => deployContext({ env: plan.to, releaseIdentity }, name),
695
+ componentOutputs,
696
+ };
697
+ const approved = approvedRunOf(options.groupRuns, group);
698
+ return approved === undefined && options.planGroupsAtWave ? planAndRunComponentGroup(each) : runComponentGroup({ ...each, run: approved });
699
+ },
700
+ }
701
+ : undefined;
650
702
  try {
651
- return await runInterpretDriver(targets, checkPublishedDigest(options.registry, expected), {
703
+ return await runInterpretDriver(targets, registry, {
652
704
  env: plan.to,
653
705
  componentOutputs: options.componentOutputs ?? {},
654
706
  // #2574: a gate approval for this promote or rollback is bound to the
655
707
  // release it deploys, as well as the environment and composition.
656
708
  releases: expected,
657
709
  // #3061: the workload's service.version and vcs.ref.head.revision are the earlier release's own.
658
- releaseIdentity: (component) => {
659
- const item = plan.items.find((i) => i.component === component);
660
- return item ? { version: item.digest, revision: item.source.gitSha } : undefined;
661
- },
710
+ releaseIdentity,
711
+ ...(groups ? { groups } : {}),
662
712
  ...(options.onProgress ? { onProgress: options.onProgress } : {}),
663
713
  ...(options.onRunEvent ? { onRunEvent: options.onRunEvent } : {}),
664
714
  ...(options.gates ? { gates: options.gates } : {}),
@@ -99,7 +99,7 @@ describe("createCapabilityRegistry", () => {
99
99
  // for them to live in instead.
100
100
  test("family table lists exactly the agnostic starter families plus the Cloudflare apply leaves (#1293)", () => {
101
101
  expect(Object.keys(STARTER_VERB_FAMILIES).sort()).toEqual(
102
- ["build", "cloudflare", "escapeHatch", "publish", "sbom", "secrets", "supplyChainPolicy", "supplyChainSecurity", "waitVerify"].sort(),
102
+ ["build", "cloudflare", "engineOutputs", "escapeHatch", "publish", "sbom", "secrets", "supplyChainPolicy", "supplyChainSecurity", "waitVerify"].sort(),
103
103
  );
104
104
  });
105
105
 
@@ -60,7 +60,9 @@
60
60
  * recorded since (#4035): its record names what ran before that run.
61
61
  */
62
62
 
63
- import type { CapabilityRegistry, PlannedOutputs } from "./capability";
63
+ import type { CapabilityRegistry, GroupRun, PlannedOutputs } from "./capability";
64
+ import type { FanOutGroup, FanOutGroupFallback, FanOutPlan } from "./fan-out";
65
+ import { groupsByComponent, planComponentGroup } from "./groups";
64
66
  import { deployContext, resolveComponentGraph, resolveStepInput, unresolvedWiringReferences, type DriverComponent, type DriverGate, type DriverPhase, type DriverStep } from "./driver";
65
67
  import { componentPlanDigest } from "./gate-plan";
66
68
  import { gateName, RELEASE_GATE_OP } from "../op/gate-name";
@@ -137,6 +139,24 @@ export interface ReleaseChangeSetInput {
137
139
  * `seededOutputs`, which then holds what the run left. Default: every one.
138
140
  */
139
141
  only?: ReadonlySet<string>;
142
+ /**
143
+ * The promote's grouped fan-out (ws-107, #4465, `formRunGroups`). Each of
144
+ * its groups plans with one `planGroup` call in the wave it places it in,
145
+ * and the group digest is folded into each member's part, so the release
146
+ * gate's digest binds the composition. Absent, or without `groups`: every
147
+ * component plans by itself, wave by wave.
148
+ */
149
+ grouped?: FanOutPlan;
150
+ }
151
+
152
+ /** A group the release gate's plan planned as one (ws-107, #4465). */
153
+ export interface ReleasePlanGroup extends FanOutGroup {
154
+ /** The group digest folded into each member's `planDigest`; `null` when the group failed to plan. */
155
+ digest: string | null;
156
+ /** Why the group failed to plan. Every member is then a failed member. */
157
+ error?: string;
158
+ /** What `runGroup` gets back. It lives in the process that planned, as the promote that applies it is. */
159
+ run?: GroupRun;
140
160
  }
141
161
 
142
162
  /** A release's change set for an environment, and what each component was planned against. */
@@ -148,6 +168,10 @@ export interface ReleasePlan {
148
168
  plannedOutputs?: Record<string, string[]>;
149
169
  /** Per component, the references to a promoted upstream that no value stands for until it applies, sorted. Absent when none. */
150
170
  unknownInputs?: Record<string, string[]>;
171
+ /** The groups planned as one (#4465), in the order they planned. Absent when none. */
172
+ groups?: ReleasePlanGroup[];
173
+ /** The components that could have grouped and did not, with why: `grouped.fallbacks`. Absent when none. */
174
+ fallbacks?: FanOutGroupFallback[];
151
175
  }
152
176
 
153
177
  const byString = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);
@@ -287,10 +311,94 @@ export async function planReleaseSet(input: ReleaseChangeSetInput): Promise<Rele
287
311
  const inPlan = (name: string): boolean => input.only === undefined || input.only.has(name);
288
312
  const isPlannable = (c: DriverComponent): boolean =>
289
313
  capabilitySteps(c.deploy).some((s) => registry.has(s.kind) && registry.resolve(s.kind).plan !== undefined);
290
- const { waves } = resolveComponentGraph(components.map((c) => ({ ...c, dependsOn: (c.dependsOn ?? []).filter((d) => byName.has(d)) })));
314
+ const failedPart = (member: string, err: unknown): ChangeSetPart => ({
315
+ member: {
316
+ member,
317
+ lexicon: "chant",
318
+ planner: "chant",
319
+ status: "failed",
320
+ error: err instanceof Error ? err.message : String(err),
321
+ planDigest: null,
322
+ holes: [],
323
+ },
324
+ entries: [],
325
+ });
326
+ /** Read each dependency of `component` once, leaving out `mates`, its own group (#4465), and keep what it read. */
327
+ const readDependencies = async (component: DriverComponent, mates: ReadonlySet<string>): Promise<void> => {
328
+ for (const dep of component.dependsOn ?? []) {
329
+ if (mates.has(dep) || read.has(dep) || !byName.has(dep)) continue;
330
+ read.add(dep);
331
+ // A dependency this promote plans exposes what its plan says (#3999).
332
+ if (plannedBy.has(dep) && isPlannable(byName.get(dep)!)) {
333
+ const known = plannedBy.get(dep)?.known ?? {};
334
+ outputs[dep] = { ...known };
335
+ planned[dep] = Object.keys(known).sort();
336
+ continue;
337
+ }
338
+ const found = await readComponentOutputs(byName.get(dep)!, registry, deployContext({ env }, dep), outputs);
339
+ if (found) outputs[dep] = found;
340
+ }
341
+ reads[component.name] = Object.fromEntries(
342
+ (component.dependsOn ?? []).filter((d) => !mates.has(d) && d in outputs).map((d) => [d, structuredClone(outputs[d])]),
343
+ );
344
+ };
345
+ /** The references of `component`'s plannable steps to a promoted upstream outside `mates` that its plan can't say yet. */
346
+ const unknownOf = (component: DriverComponent, mates: ReadonlySet<string>): Set<string> => {
347
+ const unknown = new Set<string>();
348
+ for (const step of capabilitySteps(component.deploy)) {
349
+ if (!registry.has(step.kind) || registry.resolve(step.kind).plan === undefined) continue;
350
+ const { kind: _kind, ...rest } = step;
351
+ for (const r of unresolvedWiringReferences(rest, outputs)) if (plannedBy.has(r.source) && !r.publish && !mates.has(r.source)) unknown.add(r.ref);
352
+ }
353
+ return unknown;
354
+ };
355
+
356
+ // ws-107 (#4465): a group plans once, in the wave the grouped fan-out places it in.
357
+ const grouped = input.grouped?.groups?.length ? input.grouped : undefined;
358
+ const groupOf = grouped ? groupsByComponent(grouped) : new Map<string, FanOutGroup>();
359
+ const groups: ReleasePlanGroup[] = [];
360
+ const planGroupAt = async (group: FanOutGroup): Promise<void> => {
361
+ const mates = new Set(group.components);
362
+ try {
363
+ for (const name of group.components) await readDependencies(byName.get(name)!, mates);
364
+ } catch (err) {
365
+ for (const name of group.components) {
366
+ plannedBy.set(name, undefined);
367
+ parts.push(failedPart(name, err));
368
+ }
369
+ groups.push({ ...group, digest: null, error: err instanceof Error ? err.message : String(err) });
370
+ return;
371
+ }
372
+ const g = await planComponentGroup({ group, components: byName, registry, env, ctxOf: (name) => deployContext({ env }, name), outputs });
373
+ const error = g.plans.find((p): p is Extract<typeof p, { error: string }> => "error" in p)?.error;
374
+ groups.push({ ...group, digest: g.digest ?? null, ...(error !== undefined ? { error } : {}), ...(g.run ? { run: g.run } : {}) });
375
+ for (const p of g.plans) {
376
+ if ("error" in p) {
377
+ plannedBy.set(p.component, undefined);
378
+ parts.push(failedPart(p.component, p.error));
379
+ continue;
380
+ }
381
+ plannedBy.set(p.component, p.outputs);
382
+ parts.push(...p.parts);
383
+ const unknown = unknownOf(byName.get(p.component)!, mates);
384
+ if (unknown.size > 0) unknownInputs[p.component] = [...unknown].sort();
385
+ }
386
+ };
387
+
388
+ const waves = grouped
389
+ ? grouped.waves.map((w) => w.filter((name) => byName.has(name)))
390
+ : resolveComponentGraph(components.map((c) => ({ ...c, dependsOn: (c.dependsOn ?? []).filter((d) => byName.has(d)) }))).waves;
291
391
  for (const wave of waves) {
292
392
  const planning = wave.filter(inPlan);
393
+ const inWave = new Set(planning);
394
+ // What is left of a group in this plan; one member alone plans by itself.
395
+ const groupsHere = [...new Set(planning.map((name) => groupOf.get(name)).filter((g): g is FanOutGroup => g !== undefined))]
396
+ .map((g) => ({ ...g, components: g.components.filter((c) => inWave.has(c)) }))
397
+ .filter((g) => g.components.length > 1);
398
+ const grouping = new Set(groupsHere.flatMap((g) => g.components));
399
+ for (const group of groupsHere) await planGroupAt(group);
293
400
  for (const name of planning) {
401
+ if (grouping.has(name)) continue;
294
402
  const component = byName.get(name)!;
295
403
  const digest = input.digests.get(name) ?? "";
296
404
  const plannable = capabilitySteps(component.deploy).filter((s) => registry.has(s.kind) && registry.resolve(s.kind).plan !== undefined);
@@ -300,25 +408,11 @@ export async function planReleaseSet(input: ReleaseChangeSetInput): Promise<Rele
300
408
  }
301
409
  const ctx = deployContext({ env }, name);
302
410
  try {
303
- for (const dep of component.dependsOn ?? []) {
304
- if (read.has(dep) || !byName.has(dep)) continue;
305
- read.add(dep);
306
- // A dependency this promote plans exposes what its plan says (#3999).
307
- if (plannedBy.has(dep) && isPlannable(byName.get(dep)!)) {
308
- const known = plannedBy.get(dep)?.known ?? {};
309
- outputs[dep] = { ...known };
310
- planned[dep] = Object.keys(known).sort();
311
- continue;
312
- }
313
- const found = await readComponentOutputs(byName.get(dep)!, registry, deployContext({ env }, dep), outputs);
314
- if (found) outputs[dep] = found;
315
- }
316
- reads[name] = Object.fromEntries((component.dependsOn ?? []).filter((d) => d in outputs).map((d) => [d, structuredClone(outputs[d])]));
317
- const unknown = new Set<string>();
411
+ await readDependencies(component, new Set());
412
+ const unknown = unknownOf(component, new Set());
318
413
  let exposes: PlannedOutputs | undefined;
319
414
  for (const step of plannable) {
320
415
  const { kind, ...rest } = step;
321
- for (const r of unresolvedWiringReferences(rest, outputs)) if (plannedBy.has(r.source) && !r.publish) unknown.add(r.ref);
322
416
  const result = await registry.resolve(kind).plan!(planContext(ctx, rest, outputs), resolveStepInput(rest, {}, outputs) as never);
323
417
  if (result.outputs) exposes = { known: { ...(exposes?.known ?? {}), ...result.outputs.known }, unknown: [...new Set([...(exposes?.unknown ?? []), ...result.outputs.unknown])].sort() };
324
418
  parts.push(
@@ -339,18 +433,7 @@ export async function planReleaseSet(input: ReleaseChangeSetInput): Promise<Rele
339
433
  if (unknown.size > 0) unknownInputs[name] = [...unknown].sort();
340
434
  } catch (err) {
341
435
  plannedBy.set(name, undefined);
342
- parts.push({
343
- member: {
344
- member: name,
345
- lexicon: "chant",
346
- planner: "chant",
347
- status: "failed",
348
- error: err instanceof Error ? err.message : String(err),
349
- planDigest: null,
350
- holes: [],
351
- },
352
- entries: [],
353
- });
436
+ parts.push(failedPart(name, err));
354
437
  }
355
438
  }
356
439
  }
@@ -363,6 +446,8 @@ export async function planReleaseSet(input: ReleaseChangeSetInput): Promise<Rele
363
446
  reads,
364
447
  ...(Object.keys(planned).length > 0 ? { plannedOutputs: planned } : {}),
365
448
  ...(Object.keys(unknownInputs).length > 0 ? { unknownInputs } : {}),
449
+ ...(groups.length > 0 ? { groups } : {}),
450
+ ...(input.grouped?.fallbacks?.length ? { fallbacks: input.grouped.fallbacks } : {}),
366
451
  };
367
452
  }
368
453