@intentius/chant 0.124.52 → 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 (584) 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 +15 -2
  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 +215 -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/declared-reads.d.ts +38 -0
  120. package/dist/components/declared-reads.d.ts.map +1 -0
  121. package/dist/components/declared-reads.js +131 -0
  122. package/dist/components/deploy-units.d.ts.map +1 -1
  123. package/dist/components/deploy-units.js +4 -0
  124. package/dist/components/driver.d.ts +34 -1
  125. package/dist/components/driver.d.ts.map +1 -1
  126. package/dist/components/driver.js +66 -3
  127. package/dist/components/fan-out-run.d.ts +23 -1
  128. package/dist/components/fan-out-run.d.ts.map +1 -1
  129. package/dist/components/fan-out-run.js +82 -14
  130. package/dist/components/fan-out-support.d.ts +6 -0
  131. package/dist/components/fan-out-support.d.ts.map +1 -1
  132. package/dist/components/fan-out-support.js +4 -1
  133. package/dist/components/fan-out.d.ts +28 -0
  134. package/dist/components/fan-out.d.ts.map +1 -1
  135. package/dist/components/groups.d.ts +229 -0
  136. package/dist/components/groups.d.ts.map +1 -0
  137. package/dist/components/groups.js +760 -0
  138. package/dist/components/index.d.ts +6 -2
  139. package/dist/components/index.d.ts.map +1 -1
  140. package/dist/components/index.js +4 -0
  141. package/dist/components/plan-shape-conformance.d.ts +48 -0
  142. package/dist/components/plan-shape-conformance.d.ts.map +1 -0
  143. package/dist/components/plan-shape-conformance.js +122 -0
  144. package/dist/components/pr-run.d.ts +41 -4
  145. package/dist/components/pr-run.d.ts.map +1 -1
  146. package/dist/components/pr-run.js +209 -39
  147. package/dist/components/promote-rounds.d.ts +9 -0
  148. package/dist/components/promote-rounds.d.ts.map +1 -1
  149. package/dist/components/promote-rounds.js +23 -2
  150. package/dist/components/promote.d.ts +17 -0
  151. package/dist/components/promote.d.ts.map +1 -1
  152. package/dist/components/promote.js +36 -6
  153. package/dist/components/release-gate.d.ts +23 -1
  154. package/dist/components/release-gate.d.ts.map +1 -1
  155. package/dist/components/release-gate.js +97 -33
  156. package/dist/components/run-groups.d.ts +58 -0
  157. package/dist/components/run-groups.d.ts.map +1 -0
  158. package/dist/components/run-groups.js +78 -0
  159. package/dist/components/starter-plugin.d.ts +1 -0
  160. package/dist/components/starter-plugin.d.ts.map +1 -1
  161. package/dist/components/starter-plugin.js +6 -1
  162. package/dist/components/verbs/index.d.ts +1 -0
  163. package/dist/components/verbs/index.d.ts.map +1 -1
  164. package/dist/components/verbs/index.js +1 -0
  165. package/dist/components/verbs/member-outputs.d.ts +187 -0
  166. package/dist/components/verbs/member-outputs.d.ts.map +1 -0
  167. package/dist/components/verbs/member-outputs.js +218 -0
  168. package/dist/config.d.ts +60 -0
  169. package/dist/config.d.ts.map +1 -1
  170. package/dist/config.js +27 -0
  171. package/dist/deep-observation.d.ts +1 -13
  172. package/dist/deep-observation.d.ts.map +1 -1
  173. package/dist/deep-observation.js +5 -22
  174. package/dist/drift-record.d.ts +542 -0
  175. package/dist/drift-record.d.ts.map +1 -0
  176. package/dist/drift-record.js +886 -0
  177. package/dist/engine-facts.d.ts +144 -0
  178. package/dist/engine-facts.d.ts.map +1 -0
  179. package/dist/engine-facts.js +14 -0
  180. package/dist/graph-ir.d.ts +153 -2
  181. package/dist/graph-ir.d.ts.map +1 -1
  182. package/dist/graph-ir.js +224 -6
  183. package/dist/graph-refs.d.ts +145 -0
  184. package/dist/graph-refs.d.ts.map +1 -1
  185. package/dist/graph-refs.js +305 -0
  186. package/dist/graph-schema/fixtures/adopt.json +15 -0
  187. package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
  188. package/dist/graph-schema/fixtures/index.json +68 -0
  189. package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
  190. package/dist/graph-schema/fixtures.d.ts +23 -0
  191. package/dist/graph-schema/fixtures.d.ts.map +1 -0
  192. package/dist/graph-schema/fixtures.js +246 -0
  193. package/dist/graph-schema/generate.d.ts +15 -0
  194. package/dist/graph-schema/generate.d.ts.map +1 -0
  195. package/dist/graph-schema/generate.js +267 -0
  196. package/dist/graph-schema/graph.schema.json +1387 -0
  197. package/dist/health.schema.json +2 -1
  198. package/dist/identity.d.ts.map +1 -1
  199. package/dist/identity.js +2 -0
  200. package/dist/index.d.ts +1 -0
  201. package/dist/index.d.ts.map +1 -1
  202. package/dist/index.js +2 -0
  203. package/dist/instance-nodes.d.ts +159 -0
  204. package/dist/instance-nodes.d.ts.map +1 -0
  205. package/dist/instance-nodes.js +231 -0
  206. package/dist/lexicon.d.ts +72 -0
  207. package/dist/lexicon.d.ts.map +1 -1
  208. package/dist/lifecycle/adoption-file.d.ts +56 -0
  209. package/dist/lifecycle/adoption-file.d.ts.map +1 -0
  210. package/dist/lifecycle/adoption-file.js +123 -0
  211. package/dist/lifecycle/cloud-index.d.ts +186 -0
  212. package/dist/lifecycle/cloud-index.d.ts.map +1 -0
  213. package/dist/lifecycle/cloud-index.js +618 -0
  214. package/dist/lifecycle/derive-edges.d.ts +59 -0
  215. package/dist/lifecycle/derive-edges.d.ts.map +1 -0
  216. package/dist/lifecycle/derive-edges.js +126 -0
  217. package/dist/lifecycle/git.d.ts +10 -9
  218. package/dist/lifecycle/git.d.ts.map +1 -1
  219. package/dist/lifecycle/git.js +7 -1
  220. package/dist/lifecycle/identity-match.d.ts +133 -0
  221. package/dist/lifecycle/identity-match.d.ts.map +1 -0
  222. package/dist/lifecycle/identity-match.js +147 -0
  223. package/dist/lifecycle/index-diff.d.ts +224 -0
  224. package/dist/lifecycle/index-diff.d.ts.map +1 -0
  225. package/dist/lifecycle/index-diff.js +327 -0
  226. package/dist/lifecycle/index-document.d.ts +130 -0
  227. package/dist/lifecycle/index-document.d.ts.map +1 -0
  228. package/dist/lifecycle/index-document.js +189 -0
  229. package/dist/lifecycle/index-drift.d.ts +121 -0
  230. package/dist/lifecycle/index-drift.d.ts.map +1 -0
  231. package/dist/lifecycle/index-drift.js +178 -0
  232. package/dist/lifecycle/index-import.d.ts +168 -0
  233. package/dist/lifecycle/index-import.d.ts.map +1 -0
  234. package/dist/lifecycle/index-import.js +515 -0
  235. package/dist/lifecycle/index-join.d.ts +129 -0
  236. package/dist/lifecycle/index-join.d.ts.map +1 -0
  237. package/dist/lifecycle/index-join.js +248 -0
  238. package/dist/lifecycle/index.d.ts +4 -0
  239. package/dist/lifecycle/index.d.ts.map +1 -1
  240. package/dist/lifecycle/index.js +4 -0
  241. package/dist/lifecycle/observation-baseline.d.ts +61 -21
  242. package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
  243. package/dist/lifecycle/observation-baseline.js +144 -19
  244. package/dist/lifecycle/observe.d.ts.map +1 -1
  245. package/dist/lifecycle/observe.js +6 -1
  246. package/dist/lifecycle/query.d.ts +45 -4
  247. package/dist/lifecycle/query.d.ts.map +1 -1
  248. package/dist/lifecycle/query.js +89 -7
  249. package/dist/lifecycle/replay.d.ts +10 -3
  250. package/dist/lifecycle/replay.d.ts.map +1 -1
  251. package/dist/lifecycle/replay.js +10 -6
  252. package/dist/lifecycle/scenario.d.ts +4 -3
  253. package/dist/lifecycle/scenario.d.ts.map +1 -1
  254. package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
  255. package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
  256. package/dist/lifecycle/snapshot-store-fake.js +21 -0
  257. package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
  258. package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
  259. package/dist/lifecycle/snapshot-store-s3.js +254 -0
  260. package/dist/lifecycle/snapshot-store.d.ts +170 -0
  261. package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
  262. package/dist/lifecycle/snapshot-store.js +832 -0
  263. package/dist/lifecycle/snapshot.d.ts +11 -0
  264. package/dist/lifecycle/snapshot.d.ts.map +1 -1
  265. package/dist/lifecycle/snapshot.js +18 -5
  266. package/dist/lifecycle/type-listing.d.ts +148 -0
  267. package/dist/lifecycle/type-listing.d.ts.map +1 -0
  268. package/dist/lifecycle/type-listing.js +166 -0
  269. package/dist/lint/component-checks.d.ts +15 -1
  270. package/dist/lint/component-checks.d.ts.map +1 -1
  271. package/dist/lint/component-checks.js +1 -0
  272. package/dist/lint/presets/relaxed.json +2 -1
  273. package/dist/lint/presets/strict.json +2 -1
  274. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
  275. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
  276. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
  277. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
  278. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
  279. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
  280. package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
  281. package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
  282. package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
  283. package/dist/lint/rules/comp/index.d.ts +2 -1
  284. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  285. package/dist/lint/rules/comp/index.js +4 -1
  286. package/dist/observation.d.ts +19 -2
  287. package/dist/observation.d.ts.map +1 -1
  288. package/dist/observation.js +18 -1
  289. package/dist/op/activities/activity-contracts.d.ts +1 -1
  290. package/dist/op/activities/activity-contracts.js +1 -1
  291. package/dist/op/activities/change-set.d.ts +6 -4
  292. package/dist/op/activities/change-set.d.ts.map +1 -1
  293. package/dist/op/activities/change-set.js +4 -2
  294. package/dist/op/activity-failure.d.ts +27 -0
  295. package/dist/op/activity-failure.d.ts.map +1 -1
  296. package/dist/op/activity-failure.js +31 -0
  297. package/dist/op/index.d.ts +2 -1
  298. package/dist/op/index.d.ts.map +1 -1
  299. package/dist/op/index.js +1 -1
  300. package/dist/op/local-executor.d.ts +5 -1
  301. package/dist/op/local-executor.d.ts.map +1 -1
  302. package/dist/op/local-executor.js +22 -8
  303. package/dist/pr-loop.d.ts +56 -1
  304. package/dist/pr-loop.d.ts.map +1 -1
  305. package/dist/pr-loop.js +51 -1
  306. package/dist/pulumi-urn.d.ts +101 -0
  307. package/dist/pulumi-urn.d.ts.map +1 -0
  308. package/dist/pulumi-urn.js +131 -0
  309. package/dist/release-floors.d.ts +23 -0
  310. package/dist/release-floors.d.ts.map +1 -0
  311. package/dist/release-floors.js +22 -0
  312. package/dist/sensitive-key.d.ts +23 -0
  313. package/dist/sensitive-key.d.ts.map +1 -0
  314. package/dist/sensitive-key.js +31 -0
  315. package/dist/wave-runtime.mjs +234 -234
  316. package/dist/workspace/adoption-report.d.ts +53 -0
  317. package/dist/workspace/adoption-report.d.ts.map +1 -0
  318. package/dist/workspace/adoption-report.js +76 -0
  319. package/dist/workspace/capabilities-contract.d.ts +7 -0
  320. package/dist/workspace/capabilities-contract.d.ts.map +1 -1
  321. package/dist/workspace/capabilities-contract.js +2 -1
  322. package/dist/workspace/capabilities.schema.json +98 -0
  323. package/dist/workspace/change-set.schema.json +42 -3
  324. package/dist/workspace/ci-cloud-identity.d.ts +13 -1
  325. package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
  326. package/dist/workspace/ci-cloud-identity.js +30 -1
  327. package/dist/workspace/compose-graph.d.ts +10 -1
  328. package/dist/workspace/compose-graph.d.ts.map +1 -1
  329. package/dist/workspace/compose-graph.js +6 -1
  330. package/dist/workspace/conformance/index.d.ts +1 -1
  331. package/dist/workspace/conformance/index.d.ts.map +1 -1
  332. package/dist/workspace/conformance/index.js +6 -1
  333. package/dist/workspace/drift-cli.d.ts +81 -0
  334. package/dist/workspace/drift-cli.d.ts.map +1 -0
  335. package/dist/workspace/drift-cli.js +317 -0
  336. package/dist/workspace/drift.schema.json +672 -0
  337. package/dist/workspace/graph-cli.d.ts +2 -0
  338. package/dist/workspace/graph-cli.d.ts.map +1 -1
  339. package/dist/workspace/graph-cli.js +3 -0
  340. package/dist/workspace/graph.schema.json +72 -0
  341. package/dist/workspace/init.d.ts +5 -1
  342. package/dist/workspace/init.d.ts.map +1 -1
  343. package/dist/workspace/init.js +55 -9
  344. package/dist/workspace/ledger.schema.json +1 -1
  345. package/dist/workspace/nested-graph.d.ts.map +1 -1
  346. package/dist/workspace/nested-graph.js +2 -0
  347. package/dist/workspace/pr-report.schema.json +51 -2
  348. package/dist/workspace/promote-waves.d.ts +112 -0
  349. package/dist/workspace/promote-waves.d.ts.map +1 -0
  350. package/dist/workspace/promote-waves.js +290 -0
  351. package/dist/workspace/promote.d.ts +84 -2
  352. package/dist/workspace/promote.d.ts.map +1 -1
  353. package/dist/workspace/promote.js +347 -61
  354. package/dist/workspace/reason-codes.d.ts +12 -0
  355. package/dist/workspace/reason-codes.d.ts.map +1 -1
  356. package/dist/workspace/reason-codes.js +14 -0
  357. package/dist/workspace/search-contract.d.ts +3 -3
  358. package/dist/workspace/search-contract.d.ts.map +1 -1
  359. package/dist/workspace/search-contract.js +2 -0
  360. package/dist/workspace/search.schema.json +103 -5
  361. package/package.json +14 -1
  362. package/src/__fixtures__/drift-record/README.md +58 -0
  363. package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
  364. package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
  365. package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
  366. package/src/__fixtures__/drift-record/component-status.json +91 -0
  367. package/src/__fixtures__/drift-record/deep-diff.json +259 -0
  368. package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
  369. package/src/__fixtures__/drift-record/live-diff.json +125 -0
  370. package/src/audit/discover.e2e.test.ts +14 -0
  371. package/src/audit/discover.ts +3 -1
  372. package/src/behaviour.ts +1 -1
  373. package/src/capabilities/templates.json +145 -0
  374. package/src/capabilities/templates.test.ts +212 -0
  375. package/src/capabilities/templates.ts +204 -0
  376. package/src/change-set-bundle.test.ts +83 -0
  377. package/src/change-set-graph.ts +80 -9
  378. package/src/change-set.test.ts +24 -0
  379. package/src/change-set.ts +77 -14
  380. package/src/ci-identity-targets.test.ts +23 -0
  381. package/src/ci-identity.ts +10 -4
  382. package/src/cli/commands/import-index.test.ts +117 -0
  383. package/src/cli/commands/import-index.ts +265 -0
  384. package/src/cli/commands/lint.ts +4 -2
  385. package/src/cli/handlers/components.test.ts +9 -0
  386. package/src/cli/handlers/components.ts +12 -2
  387. package/src/cli/handlers/fan-out.ts +17 -3
  388. package/src/cli/handlers/graph.test.ts +42 -0
  389. package/src/cli/handlers/graph.ts +26 -4
  390. package/src/cli/handlers/index-cloud.ts +32 -0
  391. package/src/cli/handlers/index-diff.test.ts +68 -0
  392. package/src/cli/handlers/index-diff.ts +146 -0
  393. package/src/cli/handlers/ledger-root-member.test.ts +161 -0
  394. package/src/cli/handlers/lifecycle-drift.ts +68 -0
  395. package/src/cli/handlers/lifecycle.test.ts +33 -11
  396. package/src/cli/handlers/lifecycle.ts +205 -30
  397. package/src/cli/handlers/misc.ts +73 -0
  398. package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
  399. package/src/cli/handlers/pr.ts +14 -2
  400. package/src/cli/handlers/promote.test.ts +64 -0
  401. package/src/cli/handlers/promote.ts +81 -6
  402. package/src/cli/handlers/query.ts +27 -4
  403. package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
  404. package/src/cli/handlers/run-groups-cli.test.ts +229 -0
  405. package/src/cli/handlers/scenario.test.ts +13 -0
  406. package/src/cli/handlers/scenario.ts +8 -7
  407. package/src/cli/handlers/search-index.test.ts +255 -0
  408. package/src/cli/handlers/search-json.test.ts +1 -1
  409. package/src/cli/handlers/search-ownership.test.ts +194 -0
  410. package/src/cli/handlers/search.ts +271 -38
  411. package/src/cli/main.test.ts +7 -0
  412. package/src/cli/main.ts +56 -1
  413. package/src/cli/mcp/lifecycle-tools.ts +3 -2
  414. package/src/cli/mcp/resource-handlers.ts +4 -3
  415. package/src/cli/mcp/workspace-tools.ts +1 -1
  416. package/src/cli/parse-args.ts +40 -2
  417. package/src/cli/registry.ts +19 -1
  418. package/src/cloud-lister/aws.test.ts +202 -0
  419. package/src/cloud-lister/aws.ts +226 -0
  420. package/src/cloud-lister/azure.test.ts +132 -0
  421. package/src/cloud-lister/azure.ts +142 -0
  422. package/src/cloud-lister/common.ts +115 -0
  423. package/src/cloud-lister/credentials.ts +110 -0
  424. package/src/cloud-lister/fakes.ts +66 -0
  425. package/src/cloud-lister/gcp.test.ts +121 -0
  426. package/src/cloud-lister/gcp.ts +159 -0
  427. package/src/cloud-lister/guard.test.ts +198 -0
  428. package/src/cloud-lister/guard.ts +111 -0
  429. package/src/cloud-lister/ids.ts +54 -0
  430. package/src/cloud-lister/index.ts +134 -0
  431. package/src/cloud-lister/kubernetes.test.ts +160 -0
  432. package/src/cloud-lister/kubernetes.ts +284 -0
  433. package/src/cloud-lister/markers.ts +74 -0
  434. package/src/cloud-lister/mask.ts +77 -0
  435. package/src/cloud-lister/shape.test.ts +81 -0
  436. package/src/cloud-lister/sigv4.ts +70 -0
  437. package/src/cloud-lister/transport.ts +118 -0
  438. package/src/cloud-lister/types.ts +135 -0
  439. package/src/components/capability-plugin-loader.ts +4 -0
  440. package/src/components/capability-plugin.ts +10 -0
  441. package/src/components/capability.ts +229 -0
  442. package/src/components/cli-support.ts +70 -14
  443. package/src/components/declared-reads.test.ts +103 -0
  444. package/src/components/declared-reads.ts +134 -0
  445. package/src/components/deploy-units.ts +4 -0
  446. package/src/components/driver.ts +89 -4
  447. package/src/components/fan-out-run.ts +95 -15
  448. package/src/components/fan-out-support.ts +10 -1
  449. package/src/components/fan-out.ts +30 -0
  450. package/src/components/groups.test.ts +320 -0
  451. package/src/components/groups.ts +880 -0
  452. package/src/components/index.ts +42 -0
  453. package/src/components/plan-shape-conformance.ts +130 -0
  454. package/src/components/pr-env-run-bundle.test.ts +2 -0
  455. package/src/components/pr-run.test.ts +516 -3
  456. package/src/components/pr-run.ts +231 -41
  457. package/src/components/promote-rounds.ts +25 -2
  458. package/src/components/promote.ts +55 -5
  459. package/src/components/registry.test.ts +1 -1
  460. package/src/components/release-gate.ts +115 -30
  461. package/src/components/release-groups.test.ts +362 -0
  462. package/src/components/run-groups.ts +113 -0
  463. package/src/components/starter-plugin.ts +6 -0
  464. package/src/components/verbs/index.ts +1 -0
  465. package/src/components/verbs/member-outputs.test.ts +205 -0
  466. package/src/components/verbs/member-outputs.ts +308 -0
  467. package/src/config.ts +44 -0
  468. package/src/deep-observation.ts +5 -23
  469. package/src/drift-record.test.ts +596 -0
  470. package/src/drift-record.ts +1206 -0
  471. package/src/engine-facts.guard.test.ts +111 -0
  472. package/src/engine-facts.test.ts +178 -0
  473. package/src/engine-facts.ts +154 -0
  474. package/src/gated-waves-bundle.test.ts +2 -0
  475. package/src/graph-ir-live.test.ts +92 -0
  476. package/src/graph-ir.ts +317 -7
  477. package/src/graph-refs.ts +416 -0
  478. package/src/graph-schema/fixtures/adopt.json +15 -0
  479. package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
  480. package/src/graph-schema/fixtures/index.json +68 -0
  481. package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
  482. package/src/graph-schema/fixtures.ts +267 -0
  483. package/src/graph-schema/generate.ts +263 -0
  484. package/src/graph-schema/graph-schema.test.ts +153 -0
  485. package/src/graph-schema/graph.schema.json +1387 -0
  486. package/src/health.schema.json +2 -1
  487. package/src/identity.ts +2 -0
  488. package/src/index.ts +2 -0
  489. package/src/instance-nodes.ts +288 -0
  490. package/src/lexicon.ts +76 -0
  491. package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
  492. package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
  493. package/src/lifecycle/adoption-file.test.ts +100 -0
  494. package/src/lifecycle/adoption-file.ts +154 -0
  495. package/src/lifecycle/cloud-index.test.ts +517 -0
  496. package/src/lifecycle/cloud-index.ts +736 -0
  497. package/src/lifecycle/derive-edges.test.ts +193 -0
  498. package/src/lifecycle/derive-edges.ts +182 -0
  499. package/src/lifecycle/git.ts +10 -4
  500. package/src/lifecycle/identity-match.test.ts +308 -0
  501. package/src/lifecycle/identity-match.ts +257 -0
  502. package/src/lifecycle/index-diff.test.ts +198 -0
  503. package/src/lifecycle/index-diff.ts +476 -0
  504. package/src/lifecycle/index-document.test.ts +64 -0
  505. package/src/lifecycle/index-document.ts +299 -0
  506. package/src/lifecycle/index-drift.test.ts +191 -0
  507. package/src/lifecycle/index-drift.ts +304 -0
  508. package/src/lifecycle/index-import.test.ts +255 -0
  509. package/src/lifecycle/index-import.ts +634 -0
  510. package/src/lifecycle/index-join.test.ts +250 -0
  511. package/src/lifecycle/index-join.ts +304 -0
  512. package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
  513. package/src/lifecycle/index.ts +4 -0
  514. package/src/lifecycle/member-ledger.test.ts +25 -7
  515. package/src/lifecycle/observation-baseline.test.ts +72 -1
  516. package/src/lifecycle/observation-baseline.ts +190 -27
  517. package/src/lifecycle/observe.ts +6 -1
  518. package/src/lifecycle/query.test.ts +69 -1
  519. package/src/lifecycle/query.ts +103 -10
  520. package/src/lifecycle/replay.test.ts +9 -5
  521. package/src/lifecycle/replay.ts +9 -5
  522. package/src/lifecycle/scenario.ts +4 -3
  523. package/src/lifecycle/snapshot-store-fake.ts +29 -0
  524. package/src/lifecycle/snapshot-store-s3.ts +316 -0
  525. package/src/lifecycle/snapshot-store.test.ts +385 -0
  526. package/src/lifecycle/snapshot-store.ts +981 -0
  527. package/src/lifecycle/snapshot.test.ts +13 -0
  528. package/src/lifecycle/snapshot.ts +23 -6
  529. package/src/lifecycle/type-listing.test.ts +166 -0
  530. package/src/lifecycle/type-listing.ts +309 -0
  531. package/src/lint/component-checks.ts +17 -1
  532. package/src/lint/config.test.ts +3 -0
  533. package/src/lint/presets/relaxed.json +2 -1
  534. package/src/lint/presets/strict.json +2 -1
  535. package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
  536. package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
  537. package/src/lint/rules/comp/comp.test.ts +2 -1
  538. package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
  539. package/src/lint/rules/comp/comp008.test.ts +77 -0
  540. package/src/lint/rules/comp/index.ts +4 -1
  541. package/src/observation-edges.test.ts +33 -0
  542. package/src/observation.ts +36 -1
  543. package/src/op/activities/activity-contracts.ts +1 -1
  544. package/src/op/activities/change-set.ts +8 -5
  545. package/src/op/activity-failure.ts +36 -0
  546. package/src/op/index.ts +2 -1
  547. package/src/op/local-executor.test.ts +80 -0
  548. package/src/op/local-executor.ts +27 -9
  549. package/src/pr-loop-bundle.test.ts +5 -2
  550. package/src/pr-loop.test.ts +43 -0
  551. package/src/pr-loop.ts +111 -2
  552. package/src/pulumi-urn.ts +151 -0
  553. package/src/release-floors.ts +22 -0
  554. package/src/sensitive-key.ts +34 -0
  555. package/src/workspace/adoption-report.test.ts +61 -0
  556. package/src/workspace/adoption-report.ts +103 -0
  557. package/src/workspace/capabilities-contract.test.ts +4 -0
  558. package/src/workspace/capabilities-contract.ts +9 -1
  559. package/src/workspace/capabilities.schema.json +98 -0
  560. package/src/workspace/change-set.schema.json +42 -3
  561. package/src/workspace/ci-cloud-identity.ts +31 -1
  562. package/src/workspace/compose-graph.test.ts +12 -0
  563. package/src/workspace/compose-graph.ts +12 -1
  564. package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
  565. package/src/workspace/conformance/index.ts +6 -1
  566. package/src/workspace/drift-cli.test.ts +65 -0
  567. package/src/workspace/drift-cli.ts +365 -0
  568. package/src/workspace/drift.schema.json +672 -0
  569. package/src/workspace/graph-cli.ts +5 -0
  570. package/src/workspace/graph.schema.json +72 -0
  571. package/src/workspace/init.test.ts +38 -1
  572. package/src/workspace/init.ts +50 -8
  573. package/src/workspace/intent-gaps.test.ts +18 -2
  574. package/src/workspace/ledger.schema.json +1 -1
  575. package/src/workspace/nested-graph.ts +1 -0
  576. package/src/workspace/pr-report.schema.json +51 -2
  577. package/src/workspace/promote-waves.test.ts +296 -0
  578. package/src/workspace/promote-waves.ts +340 -0
  579. package/src/workspace/promote.test.ts +158 -4
  580. package/src/workspace/promote.ts +398 -57
  581. package/src/workspace/reason-codes.test.ts +4 -1
  582. package/src/workspace/reason-codes.ts +14 -0
  583. package/src/workspace/search-contract.ts +2 -0
  584. package/src/workspace/search.schema.json +103 -5
@@ -39,6 +39,7 @@ import {
39
39
  type DriverGate,
40
40
  type DriverPhase,
41
41
  type DriverRunResult,
42
+ type InterpretGroups,
42
43
  } from "./driver";
43
44
  import type { PendingGateRecord } from "../lifecycle/gate-ledger";
44
45
  import type { GateDigestMismatch, GateLedgerPort } from "../op/gate";
@@ -51,7 +52,10 @@ import { existsSync, statSync } from "node:fs";
51
52
  import { getHeadCommit } from "../lifecycle/git";
52
53
  import { buildCapabilityRegistry } from "./capability-plugin-loader";
53
54
  import type { CapabilityRegistry } from "./capability";
55
+ import type { FanOutPlan } from "./fan-out";
56
+ import { componentSetFanOut, formRunGroups, planAndRunComponentGroup } from "./groups";
54
57
  import { applyConfigDefaults } from "./config-defaults";
58
+ import { withDeclaredReads } from "./declared-reads";
55
59
  import { loadChantConfig, type ChantConfig } from "../config";
56
60
 
57
61
  /** One component in `chant list --components` output. */
@@ -510,6 +514,8 @@ export interface RunComponentsResult {
510
514
  };
511
515
  /** This run's resolved build-time parameters (chant #1108) — the component-driver counterpart of `../cli/commands/build.ts`'s `BuildResult.buildParams`. Present only once the run actually reached dispatch (mirrors `BuildResult.buildParams`, which is likewise absent on an early-error return). */
512
516
  buildParams?: BuildParamProvenance[];
517
+ /** The grouped fan-out the run applied (ws-107, #4465), when a group formed or a component fell back from one. */
518
+ grouped?: FanOutPlan;
513
519
  }
514
520
 
515
521
  /**
@@ -545,6 +551,13 @@ export interface ResolvedComponentTargets {
545
551
  targets: DriverComponent[];
546
552
  /** For a list selector (#3950): the dependencies the closure added that the list did not name, in discovery order. */
547
553
  added?: string[];
554
+ /**
555
+ * For a list selector: the names the list gave, and every enabled component
556
+ * the closure was taken over (#4516). `runComponents` takes the closure again
557
+ * once the declared reads are written in, so a listed importer pulls in the
558
+ * component whose export it reads.
559
+ */
560
+ list?: { named: string[]; enabled: DriverComponent[] };
548
561
  /** Component name to the environments it declares (#3153), for the components that declare any (#4330). */
549
562
  sites?: Record<string, ComponentEnvironmentDeclaration[]>;
550
563
  error?: string;
@@ -650,6 +663,14 @@ function resolveComponentList(
650
663
  }
651
664
 
652
665
  const enabled = withoutDisabled(discovered, all);
666
+ return { success: true, ...dependencyClosure(names, enabled), list: { named: names, enabled } };
667
+ }
668
+
669
+ /**
670
+ * `names` and every component of `enabled` they depend on, transitively, in
671
+ * `enabled`'s order, and which of those the names did not give (#3950).
672
+ */
673
+ export function dependencyClosure(names: readonly string[], enabled: readonly DriverComponent[]): { targets: DriverComponent[]; added: string[] } {
653
674
  const byName = new Map(enabled.map((c) => [c.name, c]));
654
675
  const included = new Set<string>();
655
676
  const visit = (name: string): void => {
@@ -662,7 +683,7 @@ function resolveComponentList(
662
683
 
663
684
  const targets = enabled.filter((c) => included.has(c.name));
664
685
  const named = new Set(names);
665
- return { success: true, targets, added: targets.map((c) => c.name).filter((n) => !named.has(n)) };
686
+ return { targets, added: targets.map((c) => c.name).filter((n) => !named.has(n)) };
666
687
  }
667
688
 
668
689
  export async function runComponents(
@@ -674,13 +695,6 @@ export async function runComponents(
674
695
  if (!resolved.success) {
675
696
  return { success: false, selected: [], error: resolved.error };
676
697
  }
677
- const targets = resolved.targets;
678
- const narrowed = options.targets ?? {};
679
- const outside = Object.keys(narrowed).filter((name) => !targets.some((c) => c.name === name));
680
- if (outside.length > 0) {
681
- return { success: false, selected: [], error: `--target names ${outside.join(", ")}, which this run does not run` };
682
- }
683
- const targeted = Object.keys(narrowed).length > 0 ? { targets: narrowed } : {};
684
698
 
685
699
  // (#629) Resolve `chant.config.ts`'s `sbom`/`signing`/`vulnPolicy` sections
686
700
  // and fill their defaults into every recognized step (`generate-sbom`,
@@ -700,8 +714,27 @@ export async function runComponents(
700
714
  lexicons: lexiconNames(config.lexicons),
701
715
  }));
702
716
  const env = options.env ?? "local";
703
- const selected = targets.map((c) => c.name);
704
- const resolvedTargets = targets.map((component) => applyConfigDefaults(component, config));
717
+ // #4516: the reads a capability finds in its steps' files (a template's
718
+ // Fn::ImportValue) order a set run as `deriveFanOut` orders a fan-out, so an
719
+ // importer runs after the component whose export it reads. A list takes its
720
+ // closure again with them written in, so a listed importer pulls in its
721
+ // exporter. A single name runs on its own, against seeded outputs.
722
+ let resolvedTargets: DriverComponent[];
723
+ let added = resolved.added ?? [];
724
+ if (resolved.list) {
725
+ const enabled = await withDeclaredReads(resolved.list.enabled.map((component) => applyConfigDefaults(component, config)), registry, path);
726
+ ({ targets: resolvedTargets, added } = dependencyClosure(resolved.list.named, enabled));
727
+ } else {
728
+ const defaulted = resolved.targets.map((component) => applyConfigDefaults(component, config));
729
+ resolvedTargets = selector === "all" ? await withDeclaredReads(defaulted, registry, path) : defaulted;
730
+ }
731
+ const selected = resolvedTargets.map((c) => c.name);
732
+ const narrowed = options.targets ?? {};
733
+ const outside = Object.keys(narrowed).filter((name) => !selected.includes(name));
734
+ if (outside.length > 0) {
735
+ return { success: false, selected: [], error: `--target names ${outside.join(", ")}, which this run does not run` };
736
+ }
737
+ const targeted = Object.keys(narrowed).length > 0 ? { targets: narrowed } : {};
705
738
  // Cross-component/cross-stack outputs to resolve references against. Seeded
706
739
  // from a caller (a downstream CI job passing an upstream job's dumped
707
740
  // outputs via `--seed-outputs`); grows as this run's components complete.
@@ -719,16 +752,38 @@ export async function runComponents(
719
752
 
720
753
  try {
721
754
  if (selector === "all" || isComponentList(selector)) {
755
+ // ws-107 (#4465): the components a capability takes as one plan in their
756
+ // wave and apply as one group, by the rule the pull-request run uses.
757
+ // A cycle or unknown dependency throws here the error the driver would:
758
+ // `componentSetFanOut` drops a dependency outside the set, so the full
759
+ // graph is resolved first.
760
+ resolveComponentGraph(resolvedTargets);
761
+ const grouped = await formRunGroups({ components: resolvedTargets, plan: componentSetFanOut(resolvedTargets), registry, env });
722
762
  if (isComponentList(selector) && options.onPlan) {
723
763
  // Resolved here, before dispatch, so the order is printed before any
724
- // component runs. A cycle or unknown dependency throws the same error
725
- // `runInterpretDriver` would, caught below.
726
- const graph = resolveComponentGraph(resolvedTargets);
727
- options.onPlan({ order: graph.order, waves: graph.waves, added: resolved.added ?? [] });
764
+ // component runs.
765
+ options.onPlan({ order: grouped.order, waves: grouped.waves.filter((w) => w.length > 0), added });
728
766
  }
767
+ const byName = new Map(resolvedTargets.map((c) => [c.name, c]));
768
+ const groups: InterpretGroups | undefined = grouped.groups?.length
769
+ ? {
770
+ waves: grouped.waves,
771
+ groups: grouped.groups,
772
+ run: (group, componentOutputs) =>
773
+ planAndRunComponentGroup({
774
+ group,
775
+ components: byName,
776
+ registry,
777
+ env,
778
+ ctxOf: (name) => deployContext({ env, releaseIdentity, ...targeted }, name),
779
+ componentOutputs,
780
+ }),
781
+ }
782
+ : undefined;
729
783
  const run = await runInterpretDriver(resolvedTargets, registry, {
730
784
  env,
731
785
  releaseIdentity,
786
+ ...(groups ? { groups } : {}),
732
787
  ...targeted,
733
788
  componentOutputs: seedOutputs,
734
789
  ...(emit ? { onRunEvent: emit } : {}),
@@ -740,6 +795,7 @@ export async function runComponents(
740
795
  selected,
741
796
  components: resolvedTargets,
742
797
  buildParams: options.buildParams,
798
+ ...(grouped.groups?.length || grouped.fallbacks?.length ? { grouped } : {}),
743
799
  ...(run.status === "gated" && run.gate
744
800
  ? {
745
801
  gated: {
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Reads a capability finds in its steps' files (#4466): each becomes a
3
+ * `stackOutput()` reference in the reader's step and a `dependsOn` on the
4
+ * upstream, so it orders the fan-out and counts as a read.
5
+ */
6
+
7
+ import { describe, expect, test } from "vitest";
8
+ import { CapabilityRegistry, type DeclaredRead, type DeclaredReadMember } from "./capability";
9
+ import { applyDeclaredReads, declaredReadsOf, withDeclaredReads } from "./declared-reads";
10
+ import { planFanOut } from "./fan-out";
11
+ import { unresolvedWiringReferences } from "./driver";
12
+ import { capabilitySteps } from "./wave-plan";
13
+ import type { DriverComponent } from "./driver";
14
+
15
+ const stack = (name: string, extra: Record<string, unknown> = {}, dependsOn: string[] = []): DriverComponent => ({
16
+ name,
17
+ dependsOn,
18
+ deploy: [{ phase: "Apply", steps: [{ kind: "fake-stack", stack: name, ...extra }] }],
19
+ });
20
+
21
+ function registry(reads: (members: readonly DeclaredReadMember[]) => DeclaredRead[]) {
22
+ const asked: DeclaredReadMember[][] = [];
23
+ const r = new CapabilityRegistry();
24
+ const unused = async (): Promise<never> => {
25
+ throw new Error("not called here");
26
+ };
27
+ r.register({
28
+ kind: "fake-stack",
29
+ run: unused,
30
+ declaredReads(_ctx: unknown, members: readonly DeclaredReadMember[]) {
31
+ asked.push([...members]);
32
+ return reads(members);
33
+ },
34
+ } as never);
35
+ r.register({ kind: "fake-plain", run: unused } as never);
36
+ return { registry: r, asked };
37
+ }
38
+
39
+ describe("declared reads (#4466)", () => {
40
+ test("a read becomes a stackOutput() reference at its place and a dependsOn, so the reader orders after the upstream", async () => {
41
+ const components = [stack("app"), stack("network")];
42
+ const { registry: r, asked } = registry(() => [{ reader: "app", upstream: "network", output: "VpcId", at: "/imports/network-VpcId" }]);
43
+ const out = await withDeclaredReads(components, r, "/project");
44
+ expect(asked[0]!.map((m) => m.component)).toEqual(["app", "network"]);
45
+ const app = out.find((c) => c.name === "app")!;
46
+ expect(app.dependsOn).toEqual(["network"]);
47
+ expect(capabilitySteps(app.deploy)[0]).toEqual({
48
+ kind: "fake-stack",
49
+ stack: "app",
50
+ imports: { "network-VpcId": { stackOutput: { stack: "network", name: "VpcId" } } },
51
+ });
52
+ // The read is now one core sees: no value for network's VpcId yet.
53
+ const { kind: _kind, ...rest } = capabilitySteps(app.deploy)[0]!;
54
+ expect(unresolvedWiringReferences(rest, {}).map((x) => x.ref)).toEqual(["network.VpcId"]);
55
+ // And it orders the fan-out.
56
+ const plan = planFanOut({ components: out, changed: ["app", "network"] });
57
+ expect(plan.waves).toEqual([["network"], ["app"]]);
58
+ // The upstream is untouched.
59
+ expect(out.find((c) => c.name === "network")).toBe(components[1]);
60
+ });
61
+
62
+ test("a place the declaration already fills is left as it is, and adds no dependsOn", () => {
63
+ const declared = stack("app", { imports: { "network-VpcId": "vpc-pinned" } });
64
+ const [out] = applyDeclaredReads([declared], [{ reader: "app", upstream: "network", output: "VpcId", at: "/imports/network-VpcId", index: 0, kind: "fake-stack" }]);
65
+ expect(out!.dependsOn).toEqual([]);
66
+ expect(capabilitySteps(out!.deploy)[0]).toMatchObject({ imports: { "network-VpcId": "vpc-pinned" } });
67
+ });
68
+
69
+ test("a dependsOn already declared is not repeated", () => {
70
+ const declared = stack("app", {}, ["network"]);
71
+ const [out] = applyDeclaredReads([declared], [{ reader: "app", upstream: "network", output: "VpcId", at: "/imports/a", index: 0, kind: "fake-stack" }]);
72
+ expect(out!.dependsOn).toEqual(["network"]);
73
+ });
74
+
75
+ test("the read goes into the step of the reported kind and index, and an escaped pointer unescapes", () => {
76
+ const declared: DriverComponent = {
77
+ name: "app",
78
+ deploy: [
79
+ { phase: "Build", steps: [{ kind: "fake-plain", note: "x" }] },
80
+ { phase: "Apply", steps: [{ kind: "fake-stack", stack: "one" }, { kind: "fake-stack", stack: "two" }] },
81
+ ],
82
+ };
83
+ const [out] = applyDeclaredReads([declared], [{ reader: "app", upstream: "network", output: "Arn", at: "/imports/a~1b~0c", index: 1, kind: "fake-stack" }]);
84
+ const steps = capabilitySteps(out!.deploy);
85
+ expect(steps[0]).toEqual({ kind: "fake-plain", note: "x" });
86
+ expect(steps[1]).toEqual({ kind: "fake-stack", stack: "one" });
87
+ expect(steps[2]).toEqual({ kind: "fake-stack", stack: "two", imports: { "a/b~c": { stackOutput: { stack: "network", name: "Arn" } } } });
88
+ });
89
+
90
+ test("a read of itself, or of a component the project does not have, is dropped; a capability that throws reports none", async () => {
91
+ const components = [stack("app"), stack("network")];
92
+ const { registry: r } = registry(() => [
93
+ { reader: "app", upstream: "app", output: "X", at: "/imports/x" },
94
+ { reader: "app", upstream: "elsewhere", output: "X", at: "/imports/y" },
95
+ ]);
96
+ expect(await declaredReadsOf(components, r, "/project")).toEqual([]);
97
+ const { registry: throwing } = registry(() => {
98
+ throw new Error("unreadable");
99
+ });
100
+ expect(await declaredReadsOf(components, throwing, "/project")).toEqual([]);
101
+ expect(await withDeclaredReads(components, throwing, "/project")).toEqual(components);
102
+ });
103
+ });
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Reads a capability finds in the files its steps name (#4466).
3
+ *
4
+ * A read core can see is a `stackOutput()` or `@<component>.publish.*`
5
+ * reference in a step. A CloudFormation template that imports another
6
+ * stack's export reads that stack too, but the read sits inside the
7
+ * template. A capability that can find such reads says so through
8
+ * `Capability.declaredReads`, and {@link withDeclaredReads} writes each one
9
+ * into the reader's step as the `stackOutput()` reference a person would have
10
+ * written, plus a `dependsOn` on the upstream. From there on it is an ordinary
11
+ * read: it orders the fan-out, it is named under `unknownInputs` while the
12
+ * upstream's plan cannot say the value, and a reader whose plan read another
13
+ * value than the upstream applied is held for a round.
14
+ */
15
+
16
+ import type { CapabilityRegistry, DeclaredRead, DeclaredReadMember } from "./capability";
17
+ import type { DriverComponent, DriverPhase, DriverStep } from "./driver";
18
+ import { capabilitySteps } from "./wave-plan";
19
+
20
+ /** A declared read with the kind of the step it names. */
21
+ export type PlacedRead = DeclaredRead & { index: number; kind: string };
22
+
23
+ /** `pointer`'s segments, unescaped (RFC 6901). */
24
+ function segments(pointer: string): string[] {
25
+ if (pointer === "") return [];
26
+ return pointer
27
+ .split("/")
28
+ .slice(1)
29
+ .map((s) => s.replace(/~1/g, "/").replace(/~0/g, "~"));
30
+ }
31
+
32
+ /** `value` with `ref` placed at `path`, copying each object on the way; `undefined` when something already sits there. */
33
+ function placed(value: Record<string, unknown>, path: readonly string[], ref: unknown): Record<string, unknown> | undefined {
34
+ if (path.length === 0) return undefined;
35
+ const [head, ...rest] = path as [string, ...string[]];
36
+ const here = value[head];
37
+ if (rest.length === 0) return here === undefined ? { ...value, [head]: ref } : undefined;
38
+ if (here !== undefined && (here === null || typeof here !== "object" || Array.isArray(here))) return undefined;
39
+ const inner = placed((here as Record<string, unknown> | undefined) ?? {}, rest, ref);
40
+ return inner ? { ...value, [head]: inner } : undefined;
41
+ }
42
+
43
+ /** `phases` with each step `replace` gives a new value for swapped, in place, nested phases included. */
44
+ function mapSteps(phases: readonly DriverPhase[], replace: (step: DriverStep) => DriverStep): DriverPhase[] {
45
+ const walk = (steps: DriverPhase["steps"]): DriverPhase["steps"] =>
46
+ steps.map((step) => {
47
+ if (typeof (step as DriverPhase).phase === "string" && Array.isArray((step as DriverPhase).steps)) {
48
+ return { ...(step as DriverPhase), steps: walk((step as DriverPhase).steps) };
49
+ }
50
+ if ((step as { kind?: unknown }).kind === "gate") return step;
51
+ return replace(step as DriverStep);
52
+ }) as DriverPhase["steps"];
53
+ return phases.map((phase) => ({ ...phase, steps: walk(phase.steps) }));
54
+ }
55
+
56
+ /**
57
+ * Each capability's declared reads among `components` (#4466): every read a
58
+ * capability with `declaredReads` reports between two components of the
59
+ * project, its own reads of itself left out. A capability that throws
60
+ * reports none.
61
+ */
62
+ export async function declaredReadsOf(
63
+ components: readonly DriverComponent[],
64
+ registry: CapabilityRegistry,
65
+ root: string,
66
+ ): Promise<PlacedRead[]> {
67
+ const names = new Set(components.map((c) => c.name));
68
+ const byKind = new Map<string, DeclaredReadMember[]>();
69
+ for (const component of components) {
70
+ const seen = new Map<string, number>();
71
+ for (const step of capabilitySteps(component.deploy)) {
72
+ if (!registry.has(step.kind)) continue;
73
+ const index = seen.get(step.kind) ?? 0;
74
+ seen.set(step.kind, index + 1);
75
+ const { kind, ...rest } = step;
76
+ (byKind.get(kind) ?? byKind.set(kind, []).get(kind)!).push({ component: component.name, index, step: rest });
77
+ }
78
+ }
79
+ const out: PlacedRead[] = [];
80
+ for (const [kind, members] of byKind) {
81
+ const capability = registry.resolve(kind);
82
+ if (typeof capability.declaredReads !== "function") continue;
83
+ let reported: DeclaredRead[];
84
+ try {
85
+ reported = await capability.declaredReads({ root }, members);
86
+ } catch {
87
+ continue;
88
+ }
89
+ for (const r of reported ?? []) {
90
+ if (r.reader === r.upstream || !names.has(r.reader) || !names.has(r.upstream)) continue;
91
+ out.push({ ...r, index: r.index ?? 0, kind });
92
+ }
93
+ }
94
+ return out.sort((a, b) => a.reader.localeCompare(b.reader) || a.index - b.index || a.at.localeCompare(b.at));
95
+ }
96
+
97
+ /**
98
+ * `components` with each declared read written in (#4466): the reader's
99
+ * step gets `{ stackOutput: { stack: upstream, name: output } }` at the
100
+ * read's place, unless something sits there already, and the reader depends
101
+ * on the upstream. Components no read names come back as they were.
102
+ */
103
+ export function applyDeclaredReads(components: readonly DriverComponent[], reads: readonly PlacedRead[]): DriverComponent[] {
104
+ const byReader = new Map<string, PlacedRead[]>();
105
+ for (const r of reads) (byReader.get(r.reader) ?? byReader.set(r.reader, []).get(r.reader)!).push(r);
106
+ return components.map((component) => {
107
+ const mine = byReader.get(component.name);
108
+ if (!mine) return component;
109
+ const kinds = new Map<string, number>();
110
+ const added = new Set<string>();
111
+ const deploy = mapSteps(component.deploy, (step) => {
112
+ const index = kinds.get(step.kind) ?? 0;
113
+ kinds.set(step.kind, index + 1);
114
+ let next: DriverStep = step;
115
+ for (const r of mine.filter((x) => x.kind === step.kind && x.index === index)) {
116
+ const { kind, ...rest } = next;
117
+ const with_ = placed(rest, segments(r.at), { stackOutput: { stack: r.upstream, name: r.output } });
118
+ if (!with_) continue;
119
+ next = { kind, ...with_ } as DriverStep;
120
+ added.add(r.upstream);
121
+ }
122
+ return next;
123
+ });
124
+ const dependsOn = [...(component.dependsOn ?? [])];
125
+ for (const up of [...added].sort()) if (!dependsOn.includes(up)) dependsOn.push(up);
126
+ return { ...component, dependsOn, deploy };
127
+ });
128
+ }
129
+
130
+ /** {@link declaredReadsOf} and {@link applyDeclaredReads} in one: `components` with their declared reads written in. */
131
+ export async function withDeclaredReads(components: readonly DriverComponent[], registry: CapabilityRegistry, root: string): Promise<DriverComponent[]> {
132
+ const reads = await declaredReadsOf(components, registry, root);
133
+ return reads.length > 0 ? applyDeclaredReads(components, reads) : [...components];
134
+ }
@@ -64,6 +64,10 @@ export const DEPLOY_UNIT_RULES: readonly DeployUnitRule[] = [
64
64
  // A Config Connector manifest applied over REST (#4273). Its unit is the
65
65
  // step's `stack`, the member its plan names.
66
66
  { kind: "gcp-apply", field: "stack", lexicon: "gcp" },
67
+ // Another workspace member's outputs (#4455). Its unit is the member, so a
68
+ // change signal naming the member puts the component in the change. No
69
+ // lexicon observes it: the member's own pipeline reports its status.
70
+ { kind: "member-outputs", field: "member", lexicon: "workspace" },
67
71
  ];
68
72
 
69
73
  /** One resolved unit a component's deploy composition targets. */
@@ -54,7 +54,8 @@ import { GateNotRecordableError } from "../lifecycle/no-record";
54
54
  import { componentPlanDigest } from "./gate-plan";
55
55
  import { componentDeployInputs } from "./deploy-inputs";
56
56
  import type { PendingGateRecord } from "../lifecycle/gate-ledger";
57
- import type { CapabilityRegistry, DeployContext } from "./capability";
57
+ import type { CapabilityRegistry, DeployContext, GroupMemberStatus } from "./capability";
58
+ import type { FanOutGroup } from "./fan-out";
58
59
  import { isPlanMovedError, PLAN_MOVED_EXIT_CODE, TargetsRefusedError, targetRefusal } from "./capability";
59
60
  import { isPromotedArtifact } from "./auto-release";
60
61
  import type { ReleaseIdentity } from "../telemetry-attribution";
@@ -222,6 +223,8 @@ export interface DriverRunResult {
222
223
  * component that ran in an earlier job.
223
224
  */
224
225
  componentOutputs: Record<string, Record<string, unknown>>;
226
+ /** How each member of a group that ran ended, as its capability said (ws-107, #4465), by component. Absent when no group ran. */
227
+ groupOutcomes?: Record<string, GroupMemberStatus>;
225
228
  }
226
229
 
227
230
  // ── Graph: order + parallel-safe waves from `dependsOn` ──────────────────────
@@ -436,6 +439,42 @@ function wiringReferences(input: Record<string, unknown>): Array<WiringReference
436
439
  return [...found.values()].sort((a, b) => a.ref.localeCompare(b.ref));
437
440
  }
438
441
 
442
+ /**
443
+ * Every cross-component reference in `input` with where it sits, as a JSON
444
+ * Pointer into `input` (ws-107), and the output it names (`publish.<field>`
445
+ * for an artifact). A reference made twice is listed at each place. Sorted
446
+ * by `ref`, then by `at`.
447
+ */
448
+ export function wiringReferencesAt(input: Record<string, unknown>): Array<WiringReference & { at: string; output: string }> {
449
+ const found: Array<WiringReference & { at: string; output: string }> = [];
450
+ const pointer = (path: readonly string[]): string => path.map((p) => "/" + p.replace(/~/g, "~0").replace(/\//g, "~1")).join("");
451
+ const walk = (value: unknown, path: string[]): void => {
452
+ if (typeof value === "string") {
453
+ if (PRIOR_STEP_REF.test(value)) return;
454
+ const ref = value.match(COMPONENT_ARTIFACT_REF);
455
+ if (ref) {
456
+ const [, source, field] = ref;
457
+ found.push({ source, ref: `${source}.publish.${field}`, publish: true, at: pointer(path), output: `publish.${field}` });
458
+ }
459
+ return;
460
+ }
461
+ if (Array.isArray(value)) {
462
+ value.forEach((v, i) => walk(v, [...path, String(i)]));
463
+ return;
464
+ }
465
+ if (value && typeof value === "object") {
466
+ if ("stackOutput" in value) {
467
+ const { stack, name } = (value as { stackOutput: { stack: string; name: string } }).stackOutput;
468
+ found.push({ source: stack, ref: `${stack}.${name}`, publish: name === "publish" || name.startsWith("publish."), at: pointer(path), output: name });
469
+ return;
470
+ }
471
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) walk(v, [...path, k]);
472
+ }
473
+ };
474
+ walk(input, []);
475
+ return found.sort((a, b) => a.ref.localeCompare(b.ref) || a.at.localeCompare(b.at));
476
+ }
477
+
439
478
  /**
440
479
  * The cross-component references in `input` that resolve to another value
441
480
  * against `now` than against `read`, sorted (#3999). A pull request's apply
@@ -1148,6 +1187,27 @@ export interface InterpretRunOptions {
1148
1187
  releaseIdentity?: (component: string) => ReleaseIdentity | undefined;
1149
1188
  /** The addresses each component narrows to (#4300), by component: its `DeployContext.targets`. */
1150
1189
  targets?: Readonly<Record<string, readonly string[]>>;
1190
+ /** Groups applied as one (ws-107, #4465). See {@link InterpretGroups}. Absent: every component runs its own deploy. */
1191
+ groups?: InterpretGroups;
1192
+ }
1193
+
1194
+ /**
1195
+ * Groups a run applies as one (ws-107, #4465): a promote under a release and
1196
+ * `run --components` hand them over, already placed by `groupFanOut`. Each
1197
+ * group runs once, through `run`, in the wave its members sit in and in place
1198
+ * of their own deploys. A member that does not end `ok` stops the run after
1199
+ * its wave, as a failed component does.
1200
+ */
1201
+ export interface InterpretGroups {
1202
+ /** The grouped waves, which replace the ones `dependsOn` gives. Every component of the run is in one; an empty wave is passed over. */
1203
+ waves: string[][];
1204
+ /** Each group's members are components of the run, in one wave. */
1205
+ groups: readonly FanOutGroup[];
1206
+ /** Apply one group with the outputs standing, adding each applied member's own: one result per member, and how each ended. */
1207
+ run: (
1208
+ group: FanOutGroup,
1209
+ componentOutputs: Record<string, Record<string, unknown>>,
1210
+ ) => Promise<{ results: DriverComponentResult[]; outcomes: Record<string, GroupMemberStatus> }>;
1151
1211
  }
1152
1212
 
1153
1213
  /**
@@ -1219,8 +1279,20 @@ export async function runInterpretDriver(
1219
1279
  registry: CapabilityRegistry,
1220
1280
  options: InterpretRunOptions,
1221
1281
  ): Promise<DriverRunResult> {
1222
- const { order, waves } = resolveComponentGraph(components);
1282
+ const graph = resolveComponentGraph(components);
1223
1283
  const byName = new Map(components.map((c) => [c.name, c]));
1284
+ // ws-107 (#4465): the grouped waves, and each member's group.
1285
+ const waves = options.groups ? options.groups.waves.map((w) => w.filter((name) => byName.has(name))).filter((w) => w.length > 0) : graph.waves;
1286
+ const order = options.groups ? waves.flat() : graph.order;
1287
+ const placed = new Set(order);
1288
+ const unplaced = components.filter((c) => !placed.has(c.name)).map((c) => c.name);
1289
+ if (unplaced.length > 0) throw new Error(`the grouped waves leave out ${unplaced.join(", ")}, which this run runs`);
1290
+ // A group keeps its members in this run; one left with fewer than two is no group, and its member runs its own deploy.
1291
+ const runGroups = (options.groups?.groups ?? [])
1292
+ .map((g) => ({ ...g, components: g.components.filter((c) => byName.has(c)) }))
1293
+ .filter((g) => g.components.length > 1);
1294
+ const groupOf = new Map(runGroups.flatMap((g) => g.components.map((c) => [c, g] as const)));
1295
+ const groupOutcomes: Record<string, GroupMemberStatus> = {};
1224
1296
  const componentOutputs: Record<string, Record<string, unknown>> = { ...(options.componentOutputs ?? {}) };
1225
1297
  const emit = joinRunEventSinks(options.onRunEvent, progressSinkOf(options.onProgress));
1226
1298
  const gates: GateContext = {
@@ -1239,8 +1311,19 @@ export async function runInterpretDriver(
1239
1311
  waveLoop: for (const [waveIndex, wave] of waves.entries()) {
1240
1312
  const waveNum = waveIndex + 1;
1241
1313
  emit?.({ type: "wave-start", wave: waveNum, components: wave });
1242
- const waveComponents = wave.map((name) => byName.get(name)!);
1243
- const waveResults = await Promise.all(
1314
+ const waveComponents = wave.filter((name) => !groupOf.has(name)).map((name) => byName.get(name)!);
1315
+ const groupsHere = [...new Set(wave.map((name) => groupOf.get(name)).filter((g): g is FanOutGroup => g !== undefined))];
1316
+ // Groups and the wave's other components run side by side, as a wave's components do.
1317
+ const groupResults = Promise.all(
1318
+ groupsHere.map(async (group) => {
1319
+ for (const name of group.components) emit?.({ type: "component-start", wave: waveNum, component: name });
1320
+ const ran = await options.groups!.run(group, componentOutputs);
1321
+ Object.assign(groupOutcomes, ran.outcomes);
1322
+ for (const result of ran.results) emit?.({ type: "component-settle", wave: waveNum, component: result.component, status: progressStatus(result.status) });
1323
+ return ran.results;
1324
+ }),
1325
+ );
1326
+ const ownResults = await Promise.all(
1244
1327
  waveComponents.map(async (component) => {
1245
1328
  emit?.({ type: "component-start", wave: waveNum, component: component.name });
1246
1329
  const result = await runComponentDeploy(
@@ -1256,6 +1339,7 @@ export async function runInterpretDriver(
1256
1339
  return result;
1257
1340
  }),
1258
1341
  );
1342
+ const waveResults = [...ownResults, ...(await groupResults).flat()];
1259
1343
  results.push(...waveResults);
1260
1344
  const stopped = waveResults.find((r) => r.status !== "ok");
1261
1345
  emit?.({ type: "wave-settle", wave: waveNum, status: progressStatus(stopped?.status ?? "ok") });
@@ -1274,6 +1358,7 @@ export async function runInterpretDriver(
1274
1358
  emit?.({ type: "run-settle", status: progressStatus(status), durationMs: null });
1275
1359
  const result: DriverRunResult = {
1276
1360
  order, waves, results, ok, status, failedComponent, componentOutputs,
1361
+ ...(Object.keys(groupOutcomes).length > 0 ? { groupOutcomes } : {}),
1277
1362
  ...(gated
1278
1363
  ? {
1279
1364
  gatedComponent: gated.component,