@intentius/chant-lexicon-terraform 0.124.52 → 0.124.55

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 (641) hide show
  1. package/README.md +14 -10
  2. package/dist/__fixtures__/pr-loop/estate.component.d.ts +7 -0
  3. package/dist/__fixtures__/pr-loop/estate.component.d.ts.map +1 -0
  4. package/dist/__fixtures__/pr-loop/estate.component.js +7 -0
  5. package/dist/__fixtures__/report/generate.d.ts +26 -0
  6. package/dist/__fixtures__/report/generate.d.ts.map +1 -0
  7. package/dist/__fixtures__/report/generate.js +58 -0
  8. package/dist/applied-state.d.ts +27 -0
  9. package/dist/applied-state.d.ts.map +1 -0
  10. package/dist/applied-state.js +44 -0
  11. package/dist/backend-config.d.ts +53 -0
  12. package/dist/backend-config.d.ts.map +1 -0
  13. package/dist/backend-config.js +136 -0
  14. package/dist/behaviour/kinds.d.ts.map +1 -1
  15. package/dist/behaviour/kinds.js +4 -0
  16. package/dist/behaviour/predict.d.ts.map +1 -1
  17. package/dist/behaviour/predict.js +6 -2
  18. package/dist/behaviour/reference-catalog.d.ts +16 -5
  19. package/dist/behaviour/reference-catalog.d.ts.map +1 -1
  20. package/dist/behaviour/reference-catalog.js +16 -5
  21. package/dist/behaviour/request.d.ts.map +1 -1
  22. package/dist/behaviour/request.js +3 -2
  23. package/dist/changed-roots.d.ts +6 -4
  24. package/dist/changed-roots.d.ts.map +1 -1
  25. package/dist/changed-roots.js +14 -18
  26. package/dist/ci-setup.d.ts +11 -0
  27. package/dist/ci-setup.d.ts.map +1 -0
  28. package/dist/ci-setup.js +41 -0
  29. package/dist/commands.d.ts.map +1 -1
  30. package/dist/commands.js +4 -3
  31. package/dist/components/capability-plugin.d.ts +105 -9
  32. package/dist/components/capability-plugin.d.ts.map +1 -1
  33. package/dist/components/capability-plugin.js +357 -36
  34. package/dist/components/compose-group.d.ts +145 -0
  35. package/dist/components/compose-group.d.ts.map +1 -0
  36. package/dist/components/compose-group.js +407 -0
  37. package/dist/components/placeholder-preview.d.ts +78 -0
  38. package/dist/components/placeholder-preview.d.ts.map +1 -0
  39. package/dist/components/placeholder-preview.js +254 -0
  40. package/dist/components/release-inputs.d.ts +29 -0
  41. package/dist/components/release-inputs.d.ts.map +1 -0
  42. package/dist/components/release-inputs.js +57 -0
  43. package/dist/components/trace-facts.d.ts +16 -0
  44. package/dist/components/trace-facts.d.ts.map +1 -0
  45. package/dist/components/trace-facts.js +19 -0
  46. package/dist/compose/apply-events.d.ts +46 -0
  47. package/dist/compose/apply-events.d.ts.map +1 -0
  48. package/dist/compose/apply-events.js +77 -0
  49. package/dist/compose/estate.d.ts +194 -0
  50. package/dist/compose/estate.d.ts.map +1 -0
  51. package/dist/compose/estate.js +363 -0
  52. package/dist/compose/hcl.d.ts +99 -0
  53. package/dist/compose/hcl.d.ts.map +1 -0
  54. package/dist/compose/hcl.js +302 -0
  55. package/dist/compose/module-address.d.ts +24 -0
  56. package/dist/compose/module-address.d.ts.map +1 -0
  57. package/dist/compose/module-address.js +43 -0
  58. package/dist/compose/root.d.ts +127 -0
  59. package/dist/compose/root.d.ts.map +1 -0
  60. package/dist/compose/root.js +586 -0
  61. package/dist/compose/run.d.ts +183 -0
  62. package/dist/compose/run.d.ts.map +1 -0
  63. package/dist/compose/run.js +447 -0
  64. package/dist/compose/slice.d.ts +102 -0
  65. package/dist/compose/slice.d.ts.map +1 -0
  66. package/dist/compose/slice.js +249 -0
  67. package/dist/compose/state-address.d.ts +28 -0
  68. package/dist/compose/state-address.d.ts.map +1 -0
  69. package/dist/compose/state-address.js +154 -0
  70. package/dist/compose/state-merge.d.ts +52 -0
  71. package/dist/compose/state-merge.d.ts.map +1 -0
  72. package/dist/compose/state-merge.js +135 -0
  73. package/dist/compose.d.ts +179 -0
  74. package/dist/compose.d.ts.map +1 -0
  75. package/dist/compose.js +482 -0
  76. package/dist/composites/terraform-apply-op.d.ts.map +1 -1
  77. package/dist/composites/terraform-apply-op.js +7 -3
  78. package/dist/cone.d.ts +53 -0
  79. package/dist/cone.d.ts.map +1 -0
  80. package/dist/cone.js +119 -0
  81. package/dist/config.d.ts +59 -13
  82. package/dist/config.d.ts.map +1 -1
  83. package/dist/config.js +43 -9
  84. package/dist/cost-hold.d.ts +27 -0
  85. package/dist/cost-hold.d.ts.map +1 -0
  86. package/dist/cost-hold.js +21 -0
  87. package/dist/describe-resources.d.ts +74 -12
  88. package/dist/describe-resources.d.ts.map +1 -1
  89. package/dist/describe-resources.js +374 -66
  90. package/dist/drift-read.d.ts +33 -0
  91. package/dist/drift-read.d.ts.map +1 -0
  92. package/dist/drift-read.js +243 -0
  93. package/dist/drift-record.d.ts +147 -0
  94. package/dist/drift-record.d.ts.map +1 -0
  95. package/dist/drift-record.js +383 -0
  96. package/dist/drift-rows.d.ts +61 -0
  97. package/dist/drift-rows.d.ts.map +1 -0
  98. package/dist/drift-rows.js +108 -0
  99. package/dist/exit-codes.d.ts +55 -0
  100. package/dist/exit-codes.d.ts.map +1 -0
  101. package/dist/exit-codes.js +48 -0
  102. package/dist/hcl/edges.d.ts.map +1 -1
  103. package/dist/hcl/edges.js +24 -1
  104. package/dist/hcl/files.d.ts +61 -0
  105. package/dist/hcl/files.d.ts.map +1 -0
  106. package/dist/hcl/files.js +221 -0
  107. package/dist/hcl/instances.d.ts +82 -0
  108. package/dist/hcl/instances.d.ts.map +1 -0
  109. package/dist/hcl/instances.js +390 -0
  110. package/dist/hcl/parse.d.ts +81 -9
  111. package/dist/hcl/parse.d.ts.map +1 -1
  112. package/dist/hcl/parse.js +114 -16
  113. package/dist/hcl/roots.d.ts.map +1 -1
  114. package/dist/hcl/roots.js +5 -0
  115. package/dist/hcl/suppressions.d.ts.map +1 -1
  116. package/dist/hcl/suppressions.js +8 -2
  117. package/dist/index.d.ts +13 -2
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +24 -2
  120. package/dist/integrity.json +8 -5
  121. package/dist/lint/audit-catalog.d.ts.map +1 -1
  122. package/dist/lint/audit-catalog.js +27 -3
  123. package/dist/lint/audit-lineage.d.ts +14 -2
  124. package/dist/lint/audit-lineage.d.ts.map +1 -1
  125. package/dist/lint/post-synth/index.d.ts.map +1 -1
  126. package/dist/lint/post-synth/index.js +4 -0
  127. package/dist/lint/post-synth/record-store.d.ts +54 -0
  128. package/dist/lint/post-synth/record-store.d.ts.map +1 -0
  129. package/dist/lint/post-synth/record-store.js +104 -0
  130. package/dist/lint/post-synth/tf001.d.ts +2 -2
  131. package/dist/lint/post-synth/tf001.js +2 -2
  132. package/dist/lint/post-synth/tf024.d.ts +35 -11
  133. package/dist/lint/post-synth/tf024.d.ts.map +1 -1
  134. package/dist/lint/post-synth/tf024.js +92 -20
  135. package/dist/lint/post-synth/tf046.d.ts +64 -0
  136. package/dist/lint/post-synth/tf046.d.ts.map +1 -0
  137. package/dist/lint/post-synth/tf046.js +175 -0
  138. package/dist/lint/post-synth/tf047.d.ts +36 -0
  139. package/dist/lint/post-synth/tf047.d.ts.map +1 -0
  140. package/dist/lint/post-synth/tf047.js +94 -0
  141. package/dist/live-plan-reasons.d.ts +21 -0
  142. package/dist/live-plan-reasons.d.ts.map +1 -0
  143. package/dist/live-plan-reasons.js +47 -0
  144. package/dist/lock-rows.d.ts +36 -0
  145. package/dist/lock-rows.d.ts.map +1 -0
  146. package/dist/lock-rows.js +60 -0
  147. package/dist/lock-timeout.d.ts +22 -0
  148. package/dist/lock-timeout.d.ts.map +1 -0
  149. package/dist/lock-timeout.js +32 -0
  150. package/dist/lsp/option-keys.d.ts.map +1 -1
  151. package/dist/lsp/option-keys.js +2 -0
  152. package/dist/manifest.json +1 -1
  153. package/dist/module-catalog/index.d.ts +69 -0
  154. package/dist/module-catalog/index.d.ts.map +1 -0
  155. package/dist/module-catalog/index.js +110 -0
  156. package/dist/module-catalog/refresh-cli.d.ts +4 -0
  157. package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
  158. package/dist/module-catalog/refresh-cli.js +64 -0
  159. package/dist/module-catalog/static-tier.d.ts +6 -0
  160. package/dist/module-catalog/static-tier.d.ts.map +1 -0
  161. package/dist/module-catalog/static-tier.js +1650 -0
  162. package/dist/module-catalog/types.d.ts +65 -0
  163. package/dist/module-catalog/types.d.ts.map +1 -0
  164. package/dist/module-catalog/types.js +33 -0
  165. package/dist/okf/index.md +3 -1
  166. package/dist/okf/rules/TF024.md +2 -2
  167. package/dist/okf/rules/TF046.md +11 -0
  168. package/dist/okf/rules/TF047.md +11 -0
  169. package/dist/op/activities/index.d.ts +6 -2
  170. package/dist/op/activities/index.d.ts.map +1 -1
  171. package/dist/op/activities/index.js +5 -1
  172. package/dist/op/activities/live-detect.d.ts +4 -3
  173. package/dist/op/activities/live-detect.d.ts.map +1 -1
  174. package/dist/op/activities/live-detect.js +57 -32
  175. package/dist/op/activities/pin-rollout.d.ts +2 -0
  176. package/dist/op/activities/pin-rollout.d.ts.map +1 -1
  177. package/dist/op/activities/pin-rollout.js +1 -0
  178. package/dist/op/activities/terraform.d.ts +182 -5
  179. package/dist/op/activities/terraform.d.ts.map +1 -1
  180. package/dist/op/activities/terraform.js +224 -24
  181. package/dist/op/activity-contracts.d.ts +27 -0
  182. package/dist/op/activity-contracts.d.ts.map +1 -1
  183. package/dist/op/activity-contracts.js +19 -1
  184. package/dist/op/builders.d.ts +7 -1
  185. package/dist/op/builders.d.ts.map +1 -1
  186. package/dist/op/builders.js +9 -0
  187. package/dist/pin/consumers.d.ts +35 -0
  188. package/dist/pin/consumers.d.ts.map +1 -0
  189. package/dist/pin/consumers.js +70 -0
  190. package/dist/pin/edit.d.ts.map +1 -1
  191. package/dist/pin/edit.js +35 -0
  192. package/dist/pin/rollout.d.ts +2 -0
  193. package/dist/pin/rollout.d.ts.map +1 -1
  194. package/dist/pin/rollout.js +14 -11
  195. package/dist/plan-shape.d.ts +30 -0
  196. package/dist/plan-shape.d.ts.map +1 -0
  197. package/dist/plan-shape.js +125 -0
  198. package/dist/plugin.d.ts.map +1 -1
  199. package/dist/plugin.js +37 -18
  200. package/dist/registry/address.d.ts +63 -0
  201. package/dist/registry/address.d.ts.map +1 -0
  202. package/dist/registry/address.js +65 -0
  203. package/dist/registry/catalog-read.d.ts +14 -0
  204. package/dist/registry/catalog-read.d.ts.map +1 -0
  205. package/dist/registry/catalog-read.js +58 -0
  206. package/dist/registry/deprecated.d.ts +37 -0
  207. package/dist/registry/deprecated.d.ts.map +1 -0
  208. package/dist/registry/deprecated.js +43 -0
  209. package/dist/registry/docs.d.ts +96 -0
  210. package/dist/registry/docs.d.ts.map +1 -0
  211. package/dist/registry/docs.js +210 -0
  212. package/dist/registry/edge/edge.d.ts +46 -0
  213. package/dist/registry/edge/edge.d.ts.map +1 -0
  214. package/dist/registry/edge/edge.js +194 -0
  215. package/dist/registry/edge/index.d.ts +11 -0
  216. package/dist/registry/edge/index.d.ts.map +1 -0
  217. package/dist/registry/edge/index.js +10 -0
  218. package/dist/registry/edge/lambda-edge.d.ts +76 -0
  219. package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
  220. package/dist/registry/edge/lambda-edge.js +95 -0
  221. package/dist/registry/edge/sigv4-web.d.ts +33 -0
  222. package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
  223. package/dist/registry/edge/sigv4-web.js +56 -0
  224. package/dist/registry/edge/token-source.d.ts +73 -0
  225. package/dist/registry/edge/token-source.d.ts.map +1 -0
  226. package/dist/registry/edge/token-source.js +115 -0
  227. package/dist/registry/edge/worker.d.ts +45 -0
  228. package/dist/registry/edge/worker.d.ts.map +1 -0
  229. package/dist/registry/edge/worker.js +26 -0
  230. package/dist/registry/handler.d.ts +25 -0
  231. package/dist/registry/handler.d.ts.map +1 -0
  232. package/dist/registry/handler.js +75 -0
  233. package/dist/registry/headers-file.d.ts +21 -0
  234. package/dist/registry/headers-file.d.ts.map +1 -0
  235. package/dist/registry/headers-file.js +57 -0
  236. package/dist/registry/index.d.ts +31 -0
  237. package/dist/registry/index.d.ts.map +1 -0
  238. package/dist/registry/index.js +30 -0
  239. package/dist/registry/mirror-paths.d.ts +9 -0
  240. package/dist/registry/mirror-paths.d.ts.map +1 -0
  241. package/dist/registry/mirror-paths.js +8 -0
  242. package/dist/registry/mirror.d.ts +130 -0
  243. package/dist/registry/mirror.d.ts.map +1 -0
  244. package/dist/registry/mirror.js +319 -0
  245. package/dist/registry/proxies.d.ts +73 -0
  246. package/dist/registry/proxies.d.ts.map +1 -0
  247. package/dist/registry/proxies.js +111 -0
  248. package/dist/registry/reader.d.ts +57 -0
  249. package/dist/registry/reader.d.ts.map +1 -0
  250. package/dist/registry/reader.js +198 -0
  251. package/dist/registry/semver.d.ts +16 -0
  252. package/dist/registry/semver.d.ts.map +1 -0
  253. package/dist/registry/semver.js +24 -0
  254. package/dist/registry/serve.d.ts +24 -0
  255. package/dist/registry/serve.d.ts.map +1 -0
  256. package/dist/registry/serve.js +64 -0
  257. package/dist/registry/source-forms.d.ts +79 -0
  258. package/dist/registry/source-forms.d.ts.map +1 -0
  259. package/dist/registry/source-forms.js +202 -0
  260. package/dist/registry/targets/artifactory.d.ts +28 -0
  261. package/dist/registry/targets/artifactory.d.ts.map +1 -0
  262. package/dist/registry/targets/artifactory.js +206 -0
  263. package/dist/registry/targets/fake-artifactory.d.ts +30 -0
  264. package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
  265. package/dist/registry/targets/fake-artifactory.js +142 -0
  266. package/dist/registry/targets/fake-gitlab.d.ts +39 -0
  267. package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
  268. package/dist/registry/targets/fake-gitlab.js +136 -0
  269. package/dist/registry/targets/fake-hcp.d.ts +50 -0
  270. package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
  271. package/dist/registry/targets/fake-hcp.js +202 -0
  272. package/dist/registry/targets/fake-http.d.ts +31 -0
  273. package/dist/registry/targets/fake-http.d.ts.map +1 -0
  274. package/dist/registry/targets/fake-http.js +87 -0
  275. package/dist/registry/targets/fake-nexus.d.ts +27 -0
  276. package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
  277. package/dist/registry/targets/fake-nexus.js +137 -0
  278. package/dist/registry/targets/gitlab.d.ts +53 -0
  279. package/dist/registry/targets/gitlab.d.ts.map +1 -0
  280. package/dist/registry/targets/gitlab.js +187 -0
  281. package/dist/registry/targets/hcp.d.ts +29 -0
  282. package/dist/registry/targets/hcp.d.ts.map +1 -0
  283. package/dist/registry/targets/hcp.js +310 -0
  284. package/dist/registry/targets/hosted.d.ts +62 -0
  285. package/dist/registry/targets/hosted.d.ts.map +1 -0
  286. package/dist/registry/targets/hosted.js +74 -0
  287. package/dist/registry/targets/index.d.ts +18 -0
  288. package/dist/registry/targets/index.d.ts.map +1 -0
  289. package/dist/registry/targets/index.js +17 -0
  290. package/dist/registry/targets/module-target.d.ts +68 -0
  291. package/dist/registry/targets/module-target.d.ts.map +1 -0
  292. package/dist/registry/targets/module-target.js +63 -0
  293. package/dist/registry/targets/nexus.d.ts +43 -0
  294. package/dist/registry/targets/nexus.d.ts.map +1 -0
  295. package/dist/registry/targets/nexus.js +250 -0
  296. package/dist/registry/targets/zip.d.ts +55 -0
  297. package/dist/registry/targets/zip.d.ts.map +1 -0
  298. package/dist/registry/targets/zip.js +278 -0
  299. package/dist/registry/writer.d.ts +101 -0
  300. package/dist/registry/writer.d.ts.map +1 -0
  301. package/dist/registry/writer.js +109 -0
  302. package/dist/report.d.ts +15 -6
  303. package/dist/report.d.ts.map +1 -1
  304. package/dist/report.js +28 -55
  305. package/dist/rules/record-store.ts +122 -0
  306. package/dist/rules/tf001.ts +2 -2
  307. package/dist/rules/tf024.ts +105 -22
  308. package/dist/rules/tf046.ts +204 -0
  309. package/dist/rules/tf047.ts +114 -0
  310. package/dist/skills/chant-terraform.md +1 -1
  311. package/dist/state/azure-blob.d.ts +9 -0
  312. package/dist/state/azure-blob.d.ts.map +1 -0
  313. package/dist/state/azure-blob.js +8 -0
  314. package/dist/state/backend.d.ts +158 -0
  315. package/dist/state/backend.d.ts.map +1 -0
  316. package/dist/state/backend.js +406 -0
  317. package/dist/state/exec.d.ts +39 -0
  318. package/dist/state/exec.d.ts.map +1 -0
  319. package/dist/state/exec.js +80 -0
  320. package/dist/state/gcs.d.ts +8 -0
  321. package/dist/state/gcs.d.ts.map +1 -0
  322. package/dist/state/gcs.js +7 -0
  323. package/dist/state/index.d.ts +15 -0
  324. package/dist/state/index.d.ts.map +1 -0
  325. package/dist/state/index.js +14 -0
  326. package/dist/state/locks.d.ts +105 -0
  327. package/dist/state/locks.d.ts.map +1 -0
  328. package/dist/state/locks.js +130 -0
  329. package/dist/state/object-store.d.ts +8 -0
  330. package/dist/state/object-store.d.ts.map +1 -0
  331. package/dist/state/object-store.js +7 -0
  332. package/dist/state/root-states.d.ts +131 -0
  333. package/dist/state/root-states.d.ts.map +1 -0
  334. package/dist/state/root-states.js +241 -0
  335. package/dist/state/s3.d.ts +21 -0
  336. package/dist/state/s3.d.ts.map +1 -0
  337. package/dist/state/s3.js +19 -0
  338. package/dist/state/store.d.ts +51 -0
  339. package/dist/state/store.d.ts.map +1 -0
  340. package/dist/state/store.js +185 -0
  341. package/dist/state-ops.d.ts +255 -0
  342. package/dist/state-ops.d.ts.map +1 -0
  343. package/dist/state-ops.js +305 -0
  344. package/dist/state-reads.d.ts +57 -0
  345. package/dist/state-reads.d.ts.map +1 -0
  346. package/dist/state-reads.js +104 -0
  347. package/dist/terragrunt/affected.d.ts.map +1 -1
  348. package/dist/terragrunt/affected.js +2 -1
  349. package/dist/used-sources.d.ts +54 -0
  350. package/dist/used-sources.d.ts.map +1 -0
  351. package/dist/used-sources.js +157 -0
  352. package/package.json +24 -4
  353. package/src/__fixtures__/ACCEPTANCE.md +69 -7
  354. package/src/__fixtures__/compose/apply-failed-in-app.jsonl +12 -0
  355. package/src/__fixtures__/compose/state-app.json +66 -0
  356. package/src/__fixtures__/compose/state-edge.json +40 -0
  357. package/src/__fixtures__/compose/state-net.json +58 -0
  358. package/src/__fixtures__/compose/three-app.plan.json +184 -0
  359. package/src/__fixtures__/compose/three-composed.plan.json +509 -0
  360. package/src/__fixtures__/compose/three-edge.plan.json +144 -0
  361. package/src/__fixtures__/compose/three-net.plan.json +174 -0
  362. package/src/__fixtures__/compose/unknown-composed.plan.json +228 -0
  363. package/src/__fixtures__/compose/working-after-apply.json +145 -0
  364. package/src/__fixtures__/config-blocks/app/main.tf +61 -0
  365. package/src/__fixtures__/config-blocks/app/modules/network/main.tf +4 -0
  366. package/src/__fixtures__/config-blocks/app-json/main.tf.json +39 -0
  367. package/src/__fixtures__/config-files/shadow/main.tf +3 -0
  368. package/src/__fixtures__/config-files/shadow/main.tofu +3 -0
  369. package/src/__fixtures__/config-files/shadow/other.tf +3 -0
  370. package/src/__fixtures__/config-files/tf/main.tf +11 -0
  371. package/src/__fixtures__/config-files/tf-json/main.tf.json +18 -0
  372. package/src/__fixtures__/config-files/tofu/main.tofu +11 -0
  373. package/src/__fixtures__/config-files/tofu-json/main.tofu.json +18 -0
  374. package/src/__fixtures__/config-files/tofu-only/main.tofu +3 -0
  375. package/src/__fixtures__/drift-record/README.md +16 -0
  376. package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
  377. package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
  378. package/src/__fixtures__/drift-rows/refresh-plan.json +61 -0
  379. package/src/__fixtures__/instance-forms/app/main.tf +110 -0
  380. package/src/__fixtures__/instance-forms/app/modules/svc/main.tf +3 -0
  381. package/src/__fixtures__/instance-vars/app/main.tf +52 -0
  382. package/src/__fixtures__/instance-vars/app/modules/app/main.tf +12 -0
  383. package/src/__fixtures__/instance-vars/app/modules/edge/main.tf +3 -0
  384. package/src/__fixtures__/instance-vars/app/terraform.tfvars +1 -0
  385. package/src/__fixtures__/live-module-keyed/live-graph.json +259 -0
  386. package/src/__fixtures__/live-module-keyed/live-plan.json +87 -0
  387. package/src/__fixtures__/live-module-keyed/root/estate.chdf.hcl +1 -0
  388. package/src/__fixtures__/live-module-keyed/root/main.tf +21 -0
  389. package/src/__fixtures__/live-module-keyed/root/modules/app/main.tf +17 -0
  390. package/src/__fixtures__/live-module-keyed/root/modules/edge/main.tf +7 -0
  391. package/src/__fixtures__/live-module-keyed/root/versions.tf +30 -0
  392. package/src/__fixtures__/live-module-keyed/show-state.json +146 -0
  393. package/src/__fixtures__/pr-loop/README.md +50 -0
  394. package/src/__fixtures__/pr-loop/chant.config.json +13 -0
  395. package/src/__fixtures__/pr-loop/estate.component.ts +10 -0
  396. package/src/__fixtures__/pr-loop/roots/a/main.tf +11 -0
  397. package/src/__fixtures__/pr-loop/roots/app/main.tf +19 -0
  398. package/src/__fixtures__/pr-loop/roots/b/main.tf +7 -0
  399. package/src/__fixtures__/pr-loop/roots/dns/main.tf +3 -0
  400. package/src/__fixtures__/pr-loop/roots/net/main.tf +7 -0
  401. package/src/__fixtures__/report/applied.show.json +51 -0
  402. package/src/__fixtures__/report/generate.ts +63 -0
  403. package/src/__fixtures__/report/plan.show.json +200 -0
  404. package/src/__fixtures__/report/tf-apply/change-set.json +170 -0
  405. package/src/__fixtures__/report/tf-apply/report.json +338 -0
  406. package/src/__fixtures__/report/tf-plan/change-set.json +170 -0
  407. package/src/__fixtures__/report/tf-plan/report.json +320 -0
  408. package/src/__fixtures__/three-cloud-azure.graph.json +290 -0
  409. package/src/__fixtures__/used-sources/root/extra.tofu +1 -0
  410. package/src/__fixtures__/used-sources/root/main.tf +31 -0
  411. package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
  412. package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
  413. package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -0
  414. package/src/applied-state.ts +52 -0
  415. package/src/backend-config.test.ts +105 -0
  416. package/src/backend-config.ts +133 -0
  417. package/src/behaviour/kinds.ts +4 -0
  418. package/src/behaviour/predict.ts +5 -2
  419. package/src/behaviour/reference-catalog.plugin.test.ts +65 -0
  420. package/src/behaviour/reference-catalog.ts +16 -5
  421. package/src/behaviour/request.ts +3 -2
  422. package/src/change-set-instances.test.ts +98 -0
  423. package/src/changed-roots.ts +14 -16
  424. package/src/ci-setup.ts +42 -0
  425. package/src/commands.ts +4 -3
  426. package/src/components/capability-plugin.test.ts +198 -19
  427. package/src/components/capability-plugin.ts +383 -35
  428. package/src/components/change-set-graph.e2e.test.ts +11 -5
  429. package/src/components/compose-group.test.ts +486 -0
  430. package/src/components/compose-group.ts +489 -0
  431. package/src/components/demo-composed-roots.e2e.test.ts +440 -0
  432. package/src/components/member-outputs.test.ts +76 -0
  433. package/src/components/placeholder-preview.test.ts +147 -0
  434. package/src/components/placeholder-preview.ts +257 -0
  435. package/src/components/pr-loop-compose.tofu.e2e.test.ts +363 -0
  436. package/src/components/pr-loop.tofu.test.ts +113 -57
  437. package/src/components/release-inputs.ts +69 -0
  438. package/src/components/trace-facts.test.ts +21 -0
  439. package/src/components/trace-facts.ts +27 -0
  440. package/src/compose/apply-events.test.ts +62 -0
  441. package/src/compose/apply-events.ts +100 -0
  442. package/src/compose/estate.choudoufu.e2e.test.ts +112 -0
  443. package/src/compose/estate.test.ts +421 -0
  444. package/src/compose/estate.ts +443 -0
  445. package/src/compose/hcl.ts +311 -0
  446. package/src/compose/module-address.ts +49 -0
  447. package/src/compose/root.test.ts +416 -0
  448. package/src/compose/root.ts +592 -0
  449. package/src/compose/run.test.ts +440 -0
  450. package/src/compose/run.tofu.test.ts +176 -0
  451. package/src/compose/run.ts +527 -0
  452. package/src/compose/slice.test.ts +184 -0
  453. package/src/compose/slice.ts +290 -0
  454. package/src/compose/state-address.ts +157 -0
  455. package/src/compose/state-merge.test.ts +131 -0
  456. package/src/compose/state-merge.ts +148 -0
  457. package/src/compose.test.ts +248 -0
  458. package/src/compose.ts +627 -0
  459. package/src/composites/terraform-apply-op.test.ts +9 -0
  460. package/src/composites/terraform-apply-op.ts +7 -3
  461. package/src/cone.ts +138 -0
  462. package/src/config-file-listers.test.ts +265 -0
  463. package/src/config.ts +44 -9
  464. package/src/cost-hold.ts +29 -0
  465. package/src/describe-resources.engine-facts.test.ts +411 -0
  466. package/src/describe-resources.live-module-keyed.test.ts +290 -0
  467. package/src/describe-resources.ts +416 -70
  468. package/src/drift-read.ts +284 -0
  469. package/src/drift-record.test.ts +223 -0
  470. package/src/drift-record.ts +455 -0
  471. package/src/drift-rows.test.ts +135 -0
  472. package/src/drift-rows.ts +137 -0
  473. package/src/exit-codes.test.ts +23 -0
  474. package/src/exit-codes.ts +59 -0
  475. package/src/hcl/config-blocks.test.ts +174 -0
  476. package/src/hcl/edges.ts +21 -1
  477. package/src/hcl/files.ts +234 -0
  478. package/src/hcl/instance-forms.test.ts +94 -0
  479. package/src/hcl/instances.test.ts +137 -0
  480. package/src/hcl/instances.ts +422 -0
  481. package/src/hcl/parse.test.ts +139 -1
  482. package/src/hcl/parse.ts +149 -16
  483. package/src/hcl/roots.ts +6 -0
  484. package/src/hcl/suppressions.ts +8 -2
  485. package/src/index.ts +79 -0
  486. package/src/lint/audit-catalog.ts +29 -3
  487. package/src/lint/audit-lineage.ts +14 -2
  488. package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
  489. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
  490. package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
  491. package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
  492. package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
  493. package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
  494. package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
  495. package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
  496. package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
  497. package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
  498. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
  499. package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
  500. package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
  501. package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
  502. package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
  503. package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
  504. package/src/lint/post-synth/index.ts +4 -0
  505. package/src/lint/post-synth/record-store.ts +122 -0
  506. package/src/lint/post-synth/tf001.ts +2 -2
  507. package/src/lint/post-synth/tf024.test.ts +26 -5
  508. package/src/lint/post-synth/tf024.ts +105 -22
  509. package/src/lint/post-synth/tf046.test.ts +137 -0
  510. package/src/lint/post-synth/tf046.ts +204 -0
  511. package/src/lint/post-synth/tf047.test.ts +56 -0
  512. package/src/lint/post-synth/tf047.ts +114 -0
  513. package/src/live-plan-reasons.ts +54 -0
  514. package/src/lock-rows.test.ts +70 -0
  515. package/src/lock-rows.ts +82 -0
  516. package/src/lock-timeout.ts +34 -0
  517. package/src/lsp/completions.test.ts +1 -1
  518. package/src/lsp/option-keys.ts +2 -0
  519. package/src/module-catalog/index.test.ts +93 -0
  520. package/src/module-catalog/index.ts +159 -0
  521. package/src/module-catalog/refresh-cli.ts +68 -0
  522. package/src/module-catalog/static-tier.ts +1659 -0
  523. package/src/module-catalog/types.ts +88 -0
  524. package/src/op/activities/choudoufu-plan-set-op.test.ts +111 -0
  525. package/src/op/activities/index.ts +9 -0
  526. package/src/op/activities/live-detect.ts +55 -26
  527. package/src/op/activities/pin-rollout.ts +3 -0
  528. package/src/op/activities/registry.test.ts +2 -1
  529. package/src/op/activities/terraform.test.ts +55 -0
  530. package/src/op/activities/terraform.ts +312 -27
  531. package/src/op/activity-contracts.test.ts +1 -0
  532. package/src/op/activity-contracts.ts +24 -1
  533. package/src/op/builders.ts +14 -0
  534. package/src/op-wave-step.test.ts +109 -0
  535. package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
  536. package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
  537. package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
  538. package/src/pin/bundle.test.ts +2 -0
  539. package/src/pin/consumers.test.ts +57 -0
  540. package/src/pin/consumers.ts +95 -0
  541. package/src/pin/edit.ts +30 -0
  542. package/src/pin/rollout.ts +16 -11
  543. package/src/plan-shape.test.ts +148 -0
  544. package/src/plan-shape.ts +128 -0
  545. package/src/plugin.ts +39 -19
  546. package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
  547. package/src/registry/__fixtures__/docs/documented/README.md +1 -0
  548. package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
  549. package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
  550. package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
  551. package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
  552. package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
  553. package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
  554. package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
  555. package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
  556. package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
  557. package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
  558. package/src/registry/address.ts +100 -0
  559. package/src/registry/catalog-read.ts +56 -0
  560. package/src/registry/deprecated.test.ts +139 -0
  561. package/src/registry/deprecated.ts +76 -0
  562. package/src/registry/docs.test.ts +140 -0
  563. package/src/registry/docs.ts +299 -0
  564. package/src/registry/edge/bundle.test.ts +50 -0
  565. package/src/registry/edge/edge.test.ts +176 -0
  566. package/src/registry/edge/edge.ts +211 -0
  567. package/src/registry/edge/index.ts +10 -0
  568. package/src/registry/edge/lambda-edge.ts +127 -0
  569. package/src/registry/edge/sigv4-web.ts +79 -0
  570. package/src/registry/edge/token-source.test.ts +258 -0
  571. package/src/registry/edge/token-source.ts +169 -0
  572. package/src/registry/edge/worker.ts +61 -0
  573. package/src/registry/handler.ts +89 -0
  574. package/src/registry/headers-file.ts +61 -0
  575. package/src/registry/index.ts +31 -0
  576. package/src/registry/mirror-paths.ts +10 -0
  577. package/src/registry/mirror.test.ts +198 -0
  578. package/src/registry/mirror.ts +395 -0
  579. package/src/registry/proxies.test.ts +153 -0
  580. package/src/registry/proxies.ts +132 -0
  581. package/src/registry/reader.ts +225 -0
  582. package/src/registry/registry.test.ts +262 -0
  583. package/src/registry/semver.ts +34 -0
  584. package/src/registry/serve.ts +82 -0
  585. package/src/registry/source-forms.test.ts +87 -0
  586. package/src/registry/source-forms.ts +216 -0
  587. package/src/registry/targets/artifactory.test.ts +248 -0
  588. package/src/registry/targets/artifactory.ts +257 -0
  589. package/src/registry/targets/fake-artifactory.ts +158 -0
  590. package/src/registry/targets/fake-gitlab.ts +161 -0
  591. package/src/registry/targets/fake-hcp.ts +233 -0
  592. package/src/registry/targets/fake-http.ts +97 -0
  593. package/src/registry/targets/fake-nexus.ts +152 -0
  594. package/src/registry/targets/gitlab.test.ts +208 -0
  595. package/src/registry/targets/gitlab.ts +223 -0
  596. package/src/registry/targets/hcp.test.ts +230 -0
  597. package/src/registry/targets/hcp.ts +360 -0
  598. package/src/registry/targets/hosted.ts +117 -0
  599. package/src/registry/targets/index.ts +17 -0
  600. package/src/registry/targets/module-target.ts +131 -0
  601. package/src/registry/targets/nexus.test.ts +220 -0
  602. package/src/registry/targets/nexus.ts +314 -0
  603. package/src/registry/targets/zip.test.ts +65 -0
  604. package/src/registry/targets/zip.ts +287 -0
  605. package/src/registry/writer.ts +155 -0
  606. package/src/report.test.ts +82 -1
  607. package/src/report.ts +31 -54
  608. package/src/signal-only-root.test.ts +72 -0
  609. package/src/skills/chant-terraform.md +1 -1
  610. package/src/state/azure-blob.ts +8 -0
  611. package/src/state/backend-verdict.test.ts +187 -0
  612. package/src/state/backend.test.ts +127 -0
  613. package/src/state/backend.ts +488 -0
  614. package/src/state/exec.ts +111 -0
  615. package/src/state/gcs.ts +7 -0
  616. package/src/state/index.ts +15 -0
  617. package/src/state/locks.test.ts +84 -0
  618. package/src/state/locks.ts +201 -0
  619. package/src/state/object-store.ts +7 -0
  620. package/src/state/root-states-floci.e2e.test.ts +66 -0
  621. package/src/state/root-states.test.ts +462 -0
  622. package/src/state/root-states.ts +309 -0
  623. package/src/state/s3.ts +21 -0
  624. package/src/state/store.ts +225 -0
  625. package/src/state-ops.test.ts +182 -0
  626. package/src/state-ops.ts +462 -0
  627. package/src/state-reads.ts +116 -0
  628. package/src/terragrunt/affected.ts +2 -1
  629. package/src/used-sources.test.ts +75 -0
  630. package/src/used-sources.ts +204 -0
  631. package/src/workspace-kinds.test.ts +4 -17
  632. package/workspace-kinds.json +1 -24
  633. package/dist/workspace-member.d.ts +0 -30
  634. package/dist/workspace-member.d.ts.map +0 -1
  635. package/dist/workspace-member.js +0 -91
  636. package/schemas/pulumi.schema.json +0 -34
  637. package/src/testdata/pulumi-member/Pulumi.yaml +0 -2
  638. package/src/testdata/pulumi-member/stack-export.json +0 -53
  639. package/src/testdata/pulumi-unexported/Pulumi.yaml +0 -2
  640. package/src/workspace-member.test.ts +0 -89
  641. package/src/workspace-member.ts +0 -126
@@ -45,12 +45,16 @@ import { tmpdir } from "node:os";
45
45
  import { promisify } from "node:util";
46
46
  import { resolve, dirname, join, relative, sep } from "node:path";
47
47
  import { loadChantConfigUpward } from "@intentius/chant/config";
48
+ import { findWorkspaceRoot } from "@intentius/chant/project-root";
48
49
  import { liveReadKey, memoLiveRead } from "@intentius/chant/live-read-session";
49
50
  import { terraformPlanDigest } from "../../plan-digest";
50
51
  import { choudoufuSetPlanParts, plannerForBinary, terraformChangeSetPart } from "../../change-set";
51
52
  import type { ChangeSetPart } from "@intentius/chant/change-set";
52
53
  import type { TerraformConfig, TerraformRootConfig } from "../../config";
53
54
  import { detectLiveEstate } from "./live-detect";
55
+ import { backendConfigValues } from "../../backend-config";
56
+ import { lockTimeoutArgs } from "../../lock-timeout";
57
+ import { coneArgs, readPlanCone, type PlanCone } from "../../cone";
54
58
  import {
55
59
  parseAdoptionCommands,
56
60
  readAdoptionLedger,
@@ -116,6 +120,36 @@ export const MIN_CHOUDOUFU_VERSION = "0.15.0";
116
120
  */
117
121
  export const MIN_CHOUDOUFU_WAVES_VERSION = "0.22.0";
118
122
 
123
+ /**
124
+ * The choudoufu release a composed run of estates needs (#4445): a root with
125
+ * no `live` block that calls estate directories as modules, planned and
126
+ * applied as one walk, with a `terraform_estate_outputs` read of another
127
+ * estate in the run answered inside it (choudoufu#2059, first released in
128
+ * v0.31.0). Only the composed estate run (`compose/estate.ts`) checks it;
129
+ * below it the members plan wave by wave. A dev build names no release and
130
+ * passes, as for the other floors.
131
+ */
132
+ export const MIN_CHOUDOUFU_ESTATES_VERSION = "0.31.0";
133
+
134
+ /**
135
+ * The choudoufu release whose `live-graph -json -from-plan=<file>` prints the
136
+ * engine graph document (chant#4520's design section 4, choudoufu#2285), the
137
+ * one `describeResources()` reads engine facts from on a live root
138
+ * (chant#4528). choudoufu's changelog lists the command under
139
+ * `v0.32.0 (Unreleased)`, so this is the version it releases as. The chant
140
+ * side of the same contract, the first chant that writes those facts, is
141
+ * `ENGINE_FACTS_FLOOR` in core's engine-facts.ts, which is NEXT_RELEASE
142
+ * until the cut.
143
+ *
144
+ * Not a refusal. Facts are for people and never feed a decision, so a
145
+ * choudoufu without the command (a usage error, see
146
+ * {@link classifyLiveGraphFailure}) leaves `facts` absent with one note
147
+ * naming this version, and the rest of the read is unchanged. The check is
148
+ * the capability, not the version string: a local or dev build names a
149
+ * version below this or none at all, and still carries the command.
150
+ */
151
+ export const MIN_CHOUDOUFU_GRAPH_VERSION = "0.32.0";
152
+
119
153
  /**
120
154
  * Where a choudoufu root's wave files live, relative to the project root:
121
155
  * `<root>/plan-set.json` (the set plan document chant planned and the
@@ -163,6 +197,14 @@ export interface TerraformRootArgs {
163
197
  * `process.cwd()`, which is the project root under `chant run`.
164
198
  */
165
199
  cwd?: string;
200
+ /**
201
+ * The directory the root's files are read from in place of the entry's
202
+ * `dir`, relative to the project root (chant#4542). A promote or rollback
203
+ * under a release sets it to the release's recorded copy of the root; the
204
+ * entry's workspace, var files, backend config and estate still apply, and
205
+ * its var files and backend-config files resolve against this directory.
206
+ */
207
+ dir?: string;
166
208
  }
167
209
 
168
210
  export interface TerraformInitArgs extends TerraformRootArgs {
@@ -191,6 +233,27 @@ export interface TerraformPlanArgs extends TerraformRootArgs {
191
233
  * of that file changes nothing else.
192
234
  */
193
235
  targets?: string[];
236
+ /**
237
+ * `false` skips `show -no-color`, the human-readable render (#4479). The
238
+ * plan then runs one `show` process, `show -json`, whose document gives the
239
+ * digest, the change set and everything a caller derives from `json`, and
240
+ * {@link TerraformPlanResult.text} is `""`. For a caller that never reads
241
+ * the text, such as the `terraform-apply` capability. Default: `true`.
242
+ */
243
+ text?: boolean;
244
+ /**
245
+ * How long the plan waits for another run's state lock (#4545), passed as
246
+ * `-lock-timeout` unless `TF_CLI_ARGS` or `TF_CLI_ARGS_plan` names one.
247
+ * Default: no flag, terraform's own (no wait).
248
+ */
249
+ lockTimeout?: string;
250
+ /**
251
+ * choudoufu only (#4545): plan the cone of the change since this revision
252
+ * (`-cone=<base>`) on a root under a `live` block that is not an estate
253
+ * file. Any other root, binary, or a plan with `targets` or `destroy`,
254
+ * plans in full. {@link TerraformPlanResult.cone} says which it was.
255
+ */
256
+ cone?: string;
194
257
  }
195
258
 
196
259
  export interface TerraformApplyArgs extends TerraformRootArgs {
@@ -211,6 +274,8 @@ export interface TerraformApplyArgs extends TerraformRootArgs {
211
274
  * separate to pass.
212
275
  */
213
276
  planFile?: string;
277
+ /** How long the apply waits for another run's state lock (#4545), as {@link TerraformPlanArgs.lockTimeout}. */
278
+ lockTimeout?: string;
214
279
  }
215
280
 
216
281
  export interface TerraformShowArgs extends TerraformRootArgs {
@@ -280,6 +345,13 @@ export interface TerraformInitResult {
280
345
  dir: string;
281
346
  /** Workspace the run selected via `TF_WORKSPACE`, when the root names one. */
282
347
  workspace?: string;
348
+ /**
349
+ * The root module directory relative to the chant workspace root, with `/`
350
+ * separators (chant#4482), when the root sits inside a workspace. A
351
+ * TerraformApplyOp publishes it as the `TerraformRootPath` run outcome, so a
352
+ * ledger read can name the member whose directory holds the root it applied.
353
+ */
354
+ rootPath?: string;
283
355
  }
284
356
 
285
357
  /** Counts projected out of a plan's `resource_changes`. */
@@ -304,7 +376,7 @@ export interface TerraformPlanResult extends PlanChangeCounts {
304
376
  dir: string;
305
377
  /** `terraform show -json <planFile>`, parsed. */
306
378
  json: unknown;
307
- /** `terraform show -no-color <planFile>` — the human-readable plan. */
379
+ /** `terraform show -no-color <planFile>` — the human-readable plan. `""` when the plan ran with `text: false`. */
308
380
  text: string;
309
381
  /**
310
382
  * This plan's identity (#2300) — {@link terraformPlanDigest} over
@@ -320,6 +392,8 @@ export interface TerraformPlanResult extends PlanChangeCounts {
320
392
  * `composeChangeSet` and bind the run's gate to the document's digest.
321
393
  */
322
394
  changeSet: ChangeSetPart;
395
+ /** Set when the plan was asked for a cone (#4545): the cone it planned, or why it planned the whole estate. */
396
+ cone?: PlanCone;
323
397
  }
324
398
 
325
399
  /**
@@ -537,19 +611,24 @@ export function terraformEnvironment(root?: Pick<TerraformRootConfig, "workspace
537
611
  };
538
612
  }
539
613
 
540
- /** `terraform init`, with the root's `backendConfig` as `-backend-config=k=v` flags. */
614
+ /**
615
+ * `terraform init`, with the root's `backendConfigFiles` as
616
+ * `-backend-config=<path>` flags and then its `backendConfig` as
617
+ * `-backend-config=k=v` flags (chant#4459): init applies later flags over
618
+ * earlier ones, so a pair overrides a file. A path is passed as written; init
619
+ * runs in the root's directory, which a relative one resolves against.
620
+ */
541
621
  export function terraformInitCommand(opts: {
542
622
  binary: string;
543
623
  backendConfig?: Record<string, string>;
624
+ backendConfigFiles?: readonly string[];
544
625
  upgrade?: boolean;
545
626
  reconfigure?: boolean;
546
627
  }): string {
547
628
  const parts = [opts.binary, "init", "-input=false"];
548
629
  if (opts.upgrade) parts.push("-upgrade");
549
630
  if (opts.reconfigure) parts.push("-reconfigure");
550
- for (const [key, value] of Object.entries(opts.backendConfig ?? {})) {
551
- parts.push(`-backend-config=${quoteArg(`${key}=${value}`)}`);
552
- }
631
+ for (const value of backendConfigValues(opts)) parts.push(`-backend-config=${quoteArg(value)}`);
553
632
  return parts.join(" ");
554
633
  }
555
634
 
@@ -565,9 +644,12 @@ export function terraformPlanCommand(opts: {
565
644
  vars?: Record<string, unknown>;
566
645
  destroy?: boolean;
567
646
  targets?: string[];
647
+ /** Extra flags after the fixed ones (#4545): `-lock-timeout`, `-cone`. */
648
+ extra?: readonly string[];
568
649
  }): string {
569
650
  const parts = [opts.binary, "plan", "-input=false", "-detailed-exitcode"];
570
651
  if (opts.destroy) parts.push("-destroy");
652
+ for (const flag of opts.extra ?? []) parts.push(quoteArg(flag));
571
653
  for (const target of [...new Set(opts.targets ?? [])].sort()) parts.push(`-target=${quoteArg(target)}`);
572
654
  for (const varFile of opts.varFiles ?? []) parts.push(`-var-file=${quoteArg(varFile)}`);
573
655
  for (const [name, value] of Object.entries(opts.vars ?? {}).sort(([a], [b]) => a.localeCompare(b))) {
@@ -584,8 +666,9 @@ export function terraformPlanCommand(opts: {
584
666
  * block, and a matched one applies without re-prompting, exactly as stock's
585
667
  * own `apply <planfile>` does, so no `-auto-approve` is needed or passed.
586
668
  */
587
- export function terraformApplyCommand(opts: { binary: string; planFile: string }): string {
588
- return `${opts.binary} apply -input=false ${quoteArg(opts.planFile)}`;
669
+ export function terraformApplyCommand(opts: { binary: string; planFile: string; extra?: readonly string[] }): string {
670
+ const extra = (opts.extra ?? []).map((f) => ` ${quoteArg(f)}`).join("");
671
+ return `${opts.binary} apply -input=false${extra} ${quoteArg(opts.planFile)}`;
589
672
  }
590
673
 
591
674
  /**
@@ -629,6 +712,37 @@ export function choudoufuLivePlanCommand(opts: {
629
712
  return parts.join(" ");
630
713
  }
631
714
 
715
+ /**
716
+ * `live-graph -json -from-plan=<file> [-estate=<estate>]` (choudoufu#2285).
717
+ * No `DIR` argument: like {@link choudoufuLiveCheckCommand}, the activity
718
+ * runs in the root directory, which is DIR's default. `-estate` follows
719
+ * {@link choudoufuLivePlanCommand}'s rule: only for a root that does not
720
+ * name its own estate. `-history` is never passed: record versions take a
721
+ * listing per record and `s3:ListBucketVersions`, and an observation read
722
+ * has no use for them (the #4520 ruling, Q6: opt-in).
723
+ */
724
+ export function choudoufuLiveGraphCommand(opts: { binary: string; fromPlan: string; estate?: string }): string {
725
+ const parts = [opts.binary, "live-graph", "-json", `-from-plan=${quoteArg(opts.fromPlan)}`];
726
+ if (opts.estate !== undefined) parts.push(`-estate=${quoteArg(opts.estate)}`);
727
+ return parts.join(" ");
728
+ }
729
+
730
+ /**
731
+ * Whether a failed `live-graph` run is a choudoufu that has no such command
732
+ * or flag (`"unsupported"`), or a real failure (`"failed"`). Older builds
733
+ * answer an unknown command two ways: the new CLI prints `OpenTofu has no
734
+ * command named "live-graph"` and exits 1 (cmd/choudoufu/command_main.go),
735
+ * and the older mitchellh/cli path prints its usage and exits 127. A build
736
+ * whose `live-graph` predates `-from-plan` fails the flag parse with `flag
737
+ * provided but not defined`.
738
+ */
739
+ export function classifyLiveGraphFailure(code: unknown, output: string): "unsupported" | "failed" {
740
+ if (code === 127) return "unsupported";
741
+ if (/no command named "?live-graph"?/i.test(output)) return "unsupported";
742
+ if (/flag provided but not defined: -(?:from-plan|json|estate)\b/.test(output)) return "unsupported";
743
+ return "failed";
744
+ }
745
+
632
746
  /** `live-ls -estate=<estate> -json [-consistent]`. */
633
747
  export function choudoufuLiveLsCommand(opts: { binary: string; estate: string; consistent?: boolean }): string {
634
748
  const parts = [opts.binary, "live-ls", `-estate=${quoteArg(opts.estate)}`, "-json"];
@@ -854,7 +968,7 @@ async function resolveRoot(args: TerraformRootArgs, signal?: AbortSignal): Promi
854
968
  );
855
969
  }
856
970
  const binary = terraformBinary(namespace);
857
- const dir = resolve(projectRoot, root.dir);
971
+ const dir = resolve(projectRoot, args.dir ?? root.dir);
858
972
  await ensureChoudoufuVersion(binary, signal);
859
973
  const estate = binary === "choudoufu" ? detectLiveEstate(dir) : undefined;
860
974
  // `estate` stays what it has always been: the estate the root's own HCL
@@ -917,8 +1031,8 @@ function report(stdout: string, stderr: string): void {
917
1031
  // ── Activities ──────────────────────────────────────────────────────────────
918
1032
 
919
1033
  /**
920
- * `terraform init` in the named root, with the root's `backendConfig` supplied
921
- * as `-backend-config` flags. Uses the longInfra profile: init downloads
1034
+ * `terraform init` in the named root, with the root's `backendConfigFiles` and
1035
+ * `backendConfig` supplied as `-backend-config` flags. Uses the longInfra profile: init downloads
922
1036
  * providers and modules, a network call of unpredictable size.
923
1037
  */
924
1038
  export async function terraformInit(
@@ -929,19 +1043,55 @@ export async function terraformInit(
929
1043
  const cmd = terraformInitCommand({
930
1044
  binary,
931
1045
  ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}),
1046
+ ...(root.backendConfigFiles ? { backendConfigFiles: root.backendConfigFiles } : {}),
932
1047
  ...(args.upgrade ? { upgrade: true } : {}),
933
1048
  ...(args.reconfigure ? { reconfigure: true } : {}),
934
1049
  });
935
1050
 
936
- const { stdout, stderr } = await run(cmd, dir, terraformEnvironment(root), signal);
937
- report(stdout, stderr);
1051
+ // Known once the root resolves, before init runs (chant#4512), so a failed
1052
+ // init still publishes them and the ledger names the root it was for.
1053
+ const rootPath = workspaceRelativePath(dir);
1054
+ const result: TerraformInitResult = { dir, ...(root.workspace ? { workspace: root.workspace } : {}), ...(rootPath !== undefined ? { rootPath } : {}) };
1055
+
1056
+ let out: { stdout: string; stderr: string };
1057
+ try {
1058
+ out = await run(cmd, dir, terraformEnvironment(root), signal);
1059
+ } catch (err) {
1060
+ throw withKnownOutputs(err, result);
1061
+ }
1062
+ report(out.stdout, out.stderr);
1063
+ return result;
1064
+ }
938
1065
 
939
- return { dir, ...(root.workspace ? { workspace: root.workspace } : {}) };
1066
+ /**
1067
+ * `err` carrying `outputs` as the outputs the activity knew when it failed
1068
+ * (chant#4512): the `knownOutputs` property core's executor reads a failed
1069
+ * step's outcome attributes from (core's `withKnownOutputs`). It is set here
1070
+ * rather than imported so the lexicon needs no newer core; an older executor
1071
+ * reads the error as any other failure.
1072
+ */
1073
+ function withKnownOutputs(err: unknown, outputs: object): Error {
1074
+ const e = err instanceof Error ? err : new Error(String(err));
1075
+ return Object.assign(e, { knownOutputs: outputs });
1076
+ }
1077
+
1078
+ /**
1079
+ * `dir` relative to the chant workspace that holds it, `/`-separated, or
1080
+ * undefined outside a workspace or outside its root (chant#4482). The
1081
+ * workspace root `"."` stands for itself.
1082
+ */
1083
+ export function workspaceRelativePath(dir: string): string | undefined {
1084
+ const found = findWorkspaceRoot(dir);
1085
+ if (!found) return undefined;
1086
+ const rel = relative(found.dir, dir);
1087
+ if (rel.startsWith("..")) return undefined;
1088
+ return rel === "" ? "." : rel.split(sep).join("/");
940
1089
  }
941
1090
 
942
1091
  /**
943
1092
  * `terraform plan -detailed-exitcode -out=<planFile>` in the named root, then
944
- * `terraform show` over the written plan in both `-json` and `-no-color` form.
1093
+ * `terraform show` over the written plan in both `-json` and `-no-color` form
1094
+ * (`-json` alone with `text: false`).
945
1095
  *
946
1096
  * The exit code is the answer, not an error condition: 0 is a plan with no
947
1097
  * changes, 2 is a plan with changes, and anything else, 1 included, is a
@@ -956,9 +1106,13 @@ export async function terraformPlan(
956
1106
  args: TerraformPlanArgs,
957
1107
  signal?: AbortSignal,
958
1108
  ): Promise<TerraformPlanResult> {
959
- const { binary, root, dir } = await resolveRoot(args, signal);
1109
+ const { binary, root, dir, live } = await resolveRoot(args, signal);
960
1110
  const planFile = args.planFile ?? DEFAULT_PLAN_FILE;
961
1111
  const env = terraformEnvironment(root);
1112
+ const lock = args.lockTimeout !== undefined ? lockTimeoutArgs("plan", args.lockTimeout) : [];
1113
+ // #4545: a cone only on a live root that is not an estate file; -target and -destroy plan in full.
1114
+ const fixed = [...(args.destroy ? ["-destroy"] : []), ...((args.targets ?? []).length > 0 ? ["-target"] : [])];
1115
+ const cone = args.cone !== undefined ? coneArgs(binary, args.cone, fixed, live && !existsSync(join(dir, "estate.chdf.hcl"))) : [];
962
1116
  const cmd = terraformPlanCommand({
963
1117
  binary,
964
1118
  planFile,
@@ -966,12 +1120,15 @@ export async function terraformPlan(
966
1120
  ...(args.vars ? { vars: args.vars } : {}),
967
1121
  ...(args.destroy ? { destroy: true } : {}),
968
1122
  ...(args.targets && args.targets.length > 0 ? { targets: args.targets } : {}),
1123
+ ...(cone.length + lock.length > 0 ? { extra: [...cone, ...lock] } : {}),
969
1124
  });
970
1125
 
971
1126
  let changed: boolean;
1127
+ let output = "";
972
1128
  try {
973
1129
  const { stdout, stderr } = await run(cmd, dir, env, signal);
974
1130
  report(stdout, stderr);
1131
+ output = `${stdout}\n${stderr}`;
975
1132
  changed = false;
976
1133
  } catch (err) {
977
1134
  // An abort or a spawn failure carries no numeric exit code. That is not
@@ -985,11 +1142,12 @@ export async function terraformPlan(
985
1142
  );
986
1143
  }
987
1144
  report(failure.stdout ?? "", failure.stderr ?? "");
1145
+ output = `${failure.stdout ?? ""}\n${failure.stderr ?? ""}`;
988
1146
  changed = true;
989
1147
  }
990
1148
 
991
1149
  const jsonRun = await run(terraformShowCommand({ binary, json: true, planFile }), dir, env, signal);
992
- const textRun = await run(terraformShowCommand({ binary, json: false, planFile }), dir, env, signal);
1150
+ const text = args.text === false ? "" : (await run(terraformShowCommand({ binary, json: false, planFile }), dir, env, signal)).stdout;
993
1151
  const json: unknown = JSON.parse(jsonRun.stdout);
994
1152
 
995
1153
  return {
@@ -997,7 +1155,7 @@ export async function terraformPlan(
997
1155
  planFile,
998
1156
  dir,
999
1157
  json,
1000
- text: textRun.stdout,
1158
+ text,
1001
1159
  planDigest: terraformPlanDigest(json),
1002
1160
  changeSet: terraformChangeSetPart({
1003
1161
  member: args.root,
@@ -1006,6 +1164,7 @@ export async function terraformPlan(
1006
1164
  ...((root.estate ?? root.workspace) ? { scope: root.estate ?? root.workspace } : {}),
1007
1165
  }),
1008
1166
  ...countPlanChanges(json),
1167
+ ...(cone.length > 0 ? { cone: readPlanCone(output, join(dir, planFile), args.cone!) } : {}),
1009
1168
  };
1010
1169
  }
1011
1170
 
@@ -1057,7 +1216,8 @@ export async function terraformApply(
1057
1216
  }
1058
1217
 
1059
1218
  const planFile = args.planFile!;
1060
- const cmd = terraformApplyCommand({ binary, planFile });
1219
+ const lock = args.lockTimeout !== undefined ? lockTimeoutArgs("apply", args.lockTimeout) : [];
1220
+ const cmd = terraformApplyCommand({ binary, planFile, ...(lock.length > 0 ? { extra: lock } : {}) });
1061
1221
 
1062
1222
  try {
1063
1223
  const { stdout, stderr } = await run(cmd, dir, terraformEnvironment(root), signal);
@@ -1092,7 +1252,11 @@ export async function terraformOutputs(
1092
1252
  args: TerraformRootArgs,
1093
1253
  signal?: AbortSignal,
1094
1254
  ): Promise<Record<string, unknown>> {
1095
- const { binary, root, dir } = await resolveRoot(args, signal);
1255
+ return outputsIn(await resolveRoot(args, signal), signal);
1256
+ }
1257
+
1258
+ /** `<binary> output -json` in a resolved root, as `{ name: value }`. */
1259
+ async function outputsIn({ binary, root, dir }: ResolvedRoot, signal?: AbortSignal): Promise<Record<string, unknown>> {
1096
1260
  const { stdout } = await run(`${binary} output -json`, dir, terraformEnvironment(root), signal);
1097
1261
  const parsed = JSON.parse(stdout || "{}") as Record<string, { value?: unknown }>;
1098
1262
  return Object.fromEntries(Object.entries(parsed).map(([name, entry]) => [name, entry?.value]));
@@ -1174,7 +1338,7 @@ export async function choudoufuWaveRoot(
1174
1338
  if (resolved.root.varFiles && resolved.root.varFiles.length > 0) {
1175
1339
  return { wave: false, reason: "the root has var files, which live-plan-set does not take" };
1176
1340
  }
1177
- if (resolved.root.backendConfig && Object.keys(resolved.root.backendConfig).length > 0) {
1341
+ if ((resolved.root.backendConfig && Object.keys(resolved.root.backendConfig).length > 0) || (resolved.root.backendConfigFiles?.length ?? 0) > 0) {
1178
1342
  return { wave: false, reason: "the root has backend config, which live-plan-set does not take" };
1179
1343
  }
1180
1344
  const root = choudoufuSetRootPath(resolved.projectRoot, resolved.dir);
@@ -1275,13 +1439,17 @@ export interface ChoudoufuPlanSetResult extends ChoudoufuPlanSet {
1275
1439
  * `live-wave-apply` hands back to choudoufu with the set digest.
1276
1440
  */
1277
1441
  export async function choudoufuPlanSet(args: TerraformRootArgs, signal?: AbortSignal): Promise<ChoudoufuPlanSetResult> {
1278
- const resolved = await resolveRoot(args, signal);
1442
+ return planSetIn(await resolveRoot(args, signal), args.root, signal);
1443
+ }
1444
+
1445
+ /** {@link choudoufuPlanSet} over a resolved root, `key` naming it in errors, in the waves directory and as the change-set member. */
1446
+ async function planSetIn(resolved: ResolvedRoot, key: string, signal?: AbortSignal): Promise<ChoudoufuPlanSetResult> {
1279
1447
  const { binary, root, dir, projectRoot } = resolved;
1280
- if (binary !== "choudoufu") throw new Error(`choudoufuPlanSet: root "${args.root}" runs ${binary}, not choudoufu`);
1448
+ if (binary !== "choudoufu") throw new Error(`choudoufuPlanSet: root "${key}" runs ${binary}, not choudoufu`);
1281
1449
  await ensureChoudoufuWavesVersion(binary, signal);
1282
1450
  const rootPath = choudoufuSetRootPath(projectRoot, dir);
1283
- if (rootPath === undefined) throw new Error(`choudoufuPlanSet: root "${args.root}" (${dir}) is not inside ${projectRoot}`);
1284
- const wavesDir = choudoufuWavesDir(projectRoot, args.root);
1451
+ if (rootPath === undefined) throw new Error(`choudoufuPlanSet: root "${key}" (${dir}) is not inside ${projectRoot}`);
1452
+ const wavesDir = choudoufuWavesDir(projectRoot, key);
1285
1453
  mkdirSync(wavesDir, { recursive: true });
1286
1454
  const cmd = choudoufuPlanSetCommand({ binary, root: rootPath, outDir: fromProject(projectRoot, join(wavesDir, "out")) });
1287
1455
 
@@ -1299,12 +1467,64 @@ export async function choudoufuPlanSet(args: TerraformRootArgs, signal?: AbortSi
1299
1467
  stdout = failure.stdout ?? "";
1300
1468
  }
1301
1469
 
1302
- const parsed = parseChoudoufuPlanSet(stdout, { member: args.root, root: rootPath });
1470
+ const parsed = parseChoudoufuPlanSet(stdout, { member: key, root: rootPath });
1303
1471
  const planSetFile = join(wavesDir, "plan-set.json");
1304
1472
  writeFileSync(planSetFile, stdout);
1305
1473
  return { ...parsed, planSetFile, root: rootPath };
1306
1474
  }
1307
1475
 
1476
+ // ── A choudoufu workspace member's outputs (#4455) ─────────────────────────
1477
+
1478
+ /**
1479
+ * A choudoufu member of the workspace, as `member-outputs` names it: not a
1480
+ * `terraform.roots` entry of the reading project but a directory the
1481
+ * workspace declaration gives. Its binary is `choudoufu`, it has no var
1482
+ * files, workspace or backend config, and it runs from `projectRoot`, a
1483
+ * directory above it (the workspace root), since `live-plan-set` plans only
1484
+ * roots inside the directory it runs in.
1485
+ */
1486
+ export interface ChoudoufuMemberArgs {
1487
+ /** The member's name: the waves directory's name, and the plan's member in errors. */
1488
+ member: string;
1489
+ /** The member's directory, absolute. */
1490
+ dir: string;
1491
+ /** Where `live-plan-set` runs from, absolute: a directory above `dir`. */
1492
+ projectRoot: string;
1493
+ }
1494
+
1495
+ async function resolveMemberRoot(args: ChoudoufuMemberArgs, signal?: AbortSignal): Promise<ResolvedRoot> {
1496
+ const binary = "choudoufu";
1497
+ await ensureChoudoufuVersion(binary, signal);
1498
+ const dir = resolve(args.dir);
1499
+ const estate = detectLiveEstate(dir);
1500
+ return { binary, root: { dir }, dir, ...(estate !== undefined ? { estate } : {}), live: estate !== undefined, projectRoot: resolve(args.projectRoot) };
1501
+ }
1502
+
1503
+ /**
1504
+ * The member's outputs as applied (#4455): `choudoufu init`, then `choudoufu
1505
+ * output -json` in its directory, the read the `terraform-apply` capability's
1506
+ * `outputs` makes for a root. A live estate's outputs come from its record
1507
+ * store.
1508
+ */
1509
+ export async function choudoufuMemberOutputs(args: ChoudoufuMemberArgs, signal?: AbortSignal): Promise<Record<string, unknown>> {
1510
+ const resolved = await resolveMemberRoot(args, signal);
1511
+ const { stdout, stderr } = await run(terraformInitCommand({ binary: resolved.binary }), resolved.dir, terraformEnvironment(resolved.root), signal);
1512
+ report(stdout, stderr);
1513
+ return outputsIn(resolved, signal);
1514
+ }
1515
+
1516
+ /**
1517
+ * The member planned as a one-root set (#4455), the way a choudoufu root in
1518
+ * a gated wave is ({@link choudoufuPlanSet}): `live-plan-set -json` from
1519
+ * `projectRoot`. The document's embedded plan carries `planned_values` and
1520
+ * `output_changes`, which say what the member's outputs will be.
1521
+ */
1522
+ export async function choudoufuMemberPlanSet(args: ChoudoufuMemberArgs, signal?: AbortSignal): Promise<ChoudoufuPlanSetResult> {
1523
+ const resolved = await resolveMemberRoot(args, signal);
1524
+ if (!resolved.live) throw new Error(`choudoufu member "${args.member}" (${resolved.dir}) declares no live estate, so live-plan-set cannot plan it`);
1525
+ return planSetIn(resolved, args.member, signal);
1526
+ }
1527
+
1308
1528
  /**
1309
1529
  * What a choudoufu root keeps in the fan-out's attempt record (#3459):
1310
1530
  * choudoufu's resume file, under the set digest it was written for.
@@ -1514,10 +1734,11 @@ export function terraformPlanReadCommand(opts: { binary: string; planFile: strin
1514
1734
  * The one line a person runs to initialise a root chant refuses to plan,
1515
1735
  * with the root's `backendConfig` carried, so it can be pasted as printed.
1516
1736
  */
1517
- export function terraformInitLine(opts: { binary: string; dir: string; backendConfig?: Record<string, string> }): string {
1737
+ export function terraformInitLine(opts: { binary: string; dir: string; backendConfig?: Record<string, string>; backendConfigFiles?: readonly string[] }): string {
1518
1738
  const init = terraformInitCommand({
1519
1739
  binary: opts.binary,
1520
1740
  ...(opts.backendConfig ? { backendConfig: opts.backendConfig } : {}),
1741
+ ...(opts.backendConfigFiles ? { backendConfigFiles: opts.backendConfigFiles } : {}),
1521
1742
  });
1522
1743
  return init.replace(`${opts.binary} init`, `${opts.binary} -chdir=${quoteArg(opts.dir)} init`);
1523
1744
  }
@@ -1629,7 +1850,12 @@ export async function terraformPlanRead(
1629
1850
  ): Promise<TerraformPlanReadResult> {
1630
1851
  const { binary, root, dir } = await resolveRoot(args, signal);
1631
1852
  const env = terraformEnvironment(root);
1632
- const initLine = terraformInitLine({ binary, dir, ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}) });
1853
+ const initLine = terraformInitLine({
1854
+ binary,
1855
+ dir,
1856
+ ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}),
1857
+ ...(root.backendConfigFiles ? { backendConfigFiles: root.backendConfigFiles } : {}),
1858
+ });
1633
1859
  const notInitialised = (detail: string): TerraformPlanReadError =>
1634
1860
  new TerraformPlanReadError(
1635
1861
  "not-initialised",
@@ -1912,6 +2138,65 @@ export async function choudoufuLivePlan(
1912
2138
  };
1913
2139
  }
1914
2140
 
2141
+ /** What {@link choudoufuLiveGraph} resolved. */
2142
+ export type ChoudoufuLiveGraphResult =
2143
+ | {
2144
+ /** choudoufu printed the engine graph document. */
2145
+ supported: true;
2146
+ /** The document, parsed: a GraphIR v1 with `meta.engineGraph` (chant#4527's schema). */
2147
+ json: unknown;
2148
+ /** Absolute path of the root module directory. */
2149
+ dir: string;
2150
+ }
2151
+ | {
2152
+ /** This choudoufu has no `live-graph -from-plan` (older than {@link MIN_CHOUDOUFU_GRAPH_VERSION}). */
2153
+ supported: false;
2154
+ /** choudoufu's own first line, for the note. */
2155
+ detail: string;
2156
+ dir: string;
2157
+ };
2158
+
2159
+ export interface ChoudoufuLiveGraphArgs extends TerraformRootArgs {
2160
+ /** Absolute path of the `live-plan -json` document this graph takes its facts from. */
2161
+ fromPlan: string;
2162
+ /** The estate (`-estate`), for a root that does not declare its own. */
2163
+ estate?: string;
2164
+ }
2165
+
2166
+ /**
2167
+ * `choudoufu live-graph -json -from-plan=<file>` (choudoufu#2285, read by
2168
+ * chant#4528): the root's graph as one engine graph document, its facts taken
2169
+ * from a `live-plan -json` document this process already read. No second live
2170
+ * read: choudoufu builds the graph from the configuration and that file
2171
+ * alone, starts no provider and calls no cloud, so facts and verdicts come
2172
+ * from the same refresh (choudoufu #1014).
2173
+ *
2174
+ * A usage error ({@link classifyLiveGraphFailure}) resolves `supported:
2175
+ * false` rather than throwing, since the caller degrades to no facts; any
2176
+ * other failure throws with choudoufu's output. Warnings (an empty scope
2177
+ * estate, a plan without `observed_at`) go to stderr and are echoed like
2178
+ * every other activity's.
2179
+ */
2180
+ export async function choudoufuLiveGraph(args: ChoudoufuLiveGraphArgs, signal?: AbortSignal): Promise<ChoudoufuLiveGraphResult> {
2181
+ const resolved = await resolveRoot(args, signal);
2182
+ const { binary, dir } = resolved;
2183
+ const env = terraformEnvironment(resolved.root);
2184
+ const estateFlag: { estate?: string } = resolved.estate === undefined && args.estate !== undefined ? { estate: args.estate } : {};
2185
+ const cmd = choudoufuLiveGraphCommand({ binary, fromPlan: args.fromPlan, ...estateFlag });
2186
+ try {
2187
+ const { stdout, stderr } = await run(cmd, dir, env, signal);
2188
+ if (stderr) console.error(stderr);
2189
+ return { supported: true, json: JSON.parse(liveDocumentFrom(stdout)), dir };
2190
+ } catch (err) {
2191
+ const failure = err as ExecFailure;
2192
+ if (typeof failure.code !== "number") throw err;
2193
+ const output = `${failure.stderr ?? ""}\n${failure.stdout ?? ""}`;
2194
+ const detail = output.trim().split("\n").find((line) => line.trim() !== "")?.trim() ?? `exit ${failure.code}`;
2195
+ if (classifyLiveGraphFailure(failure.code, output) === "unsupported") return { supported: false, detail, dir };
2196
+ throw new Error(`${binary} live-graph failed in ${dir} (exit ${failure.code})${detail ? `\n${output.trim()}` : ""}`);
2197
+ }
2198
+ }
2199
+
1915
2200
  /**
1916
2201
  * `choudoufu live-ls -estate=<estate> -json [-consistent]`: every resource
1917
2202
  * the account holds under the estate, read straight from the Resource Groups
@@ -23,6 +23,7 @@ const ACTIVITY_NAMES = [
23
23
  "choudoufuLiveLs",
24
24
  "choudoufuLiveCheck",
25
25
  "choudoufuAdopt",
26
+ "choudoufuPlanSet",
26
27
  "terraformPinRollout",
27
28
  ];
28
29
 
@@ -66,7 +66,8 @@ export const terraformInitContract = activityContract(
66
66
  upgrade: z.boolean().optional(),
67
67
  reconfigure: z.boolean().optional(),
68
68
  }),
69
- z.object({ dir: z.string(), workspace: z.string().optional() }),
69
+ // `rootPath` (#4482) is what TerraformApplyOp's Init step publishes as `TerraformRootPath`; OPS012 checks it here.
70
+ z.object({ dir: z.string(), workspace: z.string().optional(), rootPath: z.string().optional() }),
70
71
  rootEntity,
71
72
  );
72
73
 
@@ -181,6 +182,28 @@ export const choudoufuAdoptContract = activityContract(
181
182
  rootEntity,
182
183
  );
183
184
 
185
+ /**
186
+ * `choudoufuPlanSet` (#4483): one live choudoufu root planned as a one-root
187
+ * set. `changeSet` is the root's part, the field an Op hands to
188
+ * `composeChangeSet`; `planDigest` is what a gate binds; `planSetFile` and
189
+ * `setDigest` are what a later `live-wave-apply` takes.
190
+ */
191
+ export const choudoufuPlanSetContract = activityContract(
192
+ "choudoufuPlanSet",
193
+ z.strictObject({ ...rootArgs }),
194
+ z.object({
195
+ setDigest: z.string(),
196
+ rootDigest: z.string(),
197
+ planDigest: z.string(),
198
+ changed: z.boolean(),
199
+ json: z.unknown(),
200
+ changeSet: z.object({ member: z.object({ member: z.string(), planDigest: z.string().nullable() }).passthrough(), entries: z.array(z.unknown()) }),
201
+ planSetFile: z.string(),
202
+ root: z.string(),
203
+ }),
204
+ rootEntity,
205
+ );
206
+
184
207
  /** One root of a pin-bump rollout ({@link PinRoot} in `../pin/waves.ts`). */
185
208
  const pinRoot = z.strictObject({ root: z.string(), dependsOn: z.array(z.string()).optional(), tsSource: z.string().optional() });
186
209
 
@@ -31,6 +31,7 @@ import type {
31
31
  ChoudoufuLiveLsArgs,
32
32
  ChoudoufuLiveCheckArgs,
33
33
  ChoudoufuAdoptArgs,
34
+ TerraformRootArgs,
34
35
  } from "./activities/terraform";
35
36
  import type { TerraformPinRolloutArgs } from "./activities/pin-rollout";
36
37
 
@@ -186,6 +187,19 @@ export const terraformShow = (
186
187
  );
187
188
  };
188
189
 
190
+ /**
191
+ * `choudoufuPlanSet` (#4483): `live-plan-set -json` over one live choudoufu
192
+ * root. Hand `step.out.changeSet` from each root's step to `composeChangeSet`
193
+ * to make one change-set document over several roots, such as one per cloud.
194
+ */
195
+ export const choudoufuPlanSet = (
196
+ root: string,
197
+ opts?: WithStepRefs<Omit<TerraformRootArgs, "root">> & StepOpts,
198
+ ): NamedActivityStep => {
199
+ const { args, profile, id } = takeProfileAndId(opts as Record<string, unknown> | undefined);
200
+ return activity("choudoufuPlanSet", { root, ...args }, { profile: profile ?? "longInfra", ...(id ? { id } : {}) });
201
+ };
202
+
189
203
  /**
190
204
  * One run of a pin-bump rollout (#3189). `opts` is
191
205
  * {@link TerraformPinRolloutArgs} itself. It names no root: the rollout finds