@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
@@ -120,6 +120,14 @@
120
120
  * not-observed, then foreign, then adoptable, then owned, and a block whose
121
121
  * instances are all absent is absent. Partial knowledge is not knowledge,
122
122
  * which is the same rule choudoufu applies to its own `INCOMPLETE_BLOCK`.
123
+ * The precedence is core's `TERRAFORM_INSTANCE_ROLLUP` and the rollup is
124
+ * core's `rollupInstances` (`@intentius/chant/instance-nodes`, #4454), the
125
+ * one place it is written.
126
+ *
127
+ * The block's verdict is not the whole answer. A present block also carries
128
+ * each instance's own verdict on `instances`, keyed as Terraform prints it
129
+ * (`[0]`, `["a"]`), which the live graph draws as an instance node under the
130
+ * block node (#4454). The state read does the same with the rows it found.
123
131
  *
124
132
  * ## What is readable at all
125
133
  *
@@ -195,6 +203,31 @@
195
203
  * The cost is two refreshes of the estate per read, run one after the other
196
204
  * so they never share the root's cache file.
197
205
  *
206
+ * ## Engine facts on a live root (#4528)
207
+ *
208
+ * After `live-plan -json` answers, the same document is handed to `choudoufu
209
+ * live-graph -json -from-plan=<file>` (choudoufu#2285), which builds the
210
+ * root's graph from the configuration and that file alone: no second live
211
+ * read, so the facts and the verdicts above come from one refresh (choudoufu
212
+ * #1014, the #4520 ruling Q3). `<file>` is the `<root>.live-plan.json`
213
+ * written under {@link TERRAFORM_LIVE_PLAN_DIR_ENV} when the caller asked
214
+ * for it, and otherwise a copy in a temporary directory removed afterwards.
215
+ *
216
+ * The document's node ids are choudoufu's addresses (the ruling's Q2). A
217
+ * block node maps to its entity with {@link entityKeyFor}; an instance node
218
+ * (`instanceOf` set) maps to its block's entity, and its facts go on that
219
+ * block's `instances` entry of the same address, from which core's
220
+ * `observedInstanceNodeId` draws the instance node. Each node's `facts` land
221
+ * on `ResourceMetadata.facts` or `InstanceObservation.facts`, and edges with
222
+ * a grain land on the observation's `edges`, endpoints mapped the same way
223
+ * ({@link readEngineGraph}). Facts are never read for a decision: ownership
224
+ * and the change set keep reading the verdicts above.
225
+ *
226
+ * A choudoufu without the command, older than
227
+ * `MIN_CHOUDOUFU_GRAPH_VERSION`, answers with a usage error: `facts` stay
228
+ * absent, the root gets one note, and the rest of the read is unchanged. Any
229
+ * other failure of the graph read degrades the same way, with its own note.
230
+ *
198
231
  * ## Nothing from `values` is surfaced
199
232
  *
200
233
  * A state row's `values` are the resource's full attribute set, secrets
@@ -204,16 +237,20 @@
204
237
  * `id`, which is the physical id every observation carries.
205
238
  */
206
239
  import { mergeObservations, normalizeObservation, observation, observeEntities, } from "@intentius/chant/observation";
207
- import { mkdirSync, writeFileSync } from "node:fs";
240
+ import { observedInstanceNodeId, rollupInstances, terraformInstancePath, TERRAFORM_INSTANCE_ROLLUP, } from "@intentius/chant/instance-nodes";
241
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
242
+ import { tmpdir } from "node:os";
208
243
  import { isAbsolute, join } from "node:path";
209
- import { choudoufuLiveLs, choudoufuLivePlanDocument, terraformInit, terraformPlanRead, terraformShow, TerraformPlanReadError, } from "./op/activities/terraform.js";
244
+ import { choudoufuLiveGraph, choudoufuLiveLs, choudoufuLivePlanDocument, MIN_CHOUDOUFU_GRAPH_VERSION, terraformInit, terraformPlanRead, terraformShow, TerraformPlanReadError, } from "./op/activities/terraform.js";
210
245
  import { blockAddressOf, indexPlan, planAttributesFor, plannedAttributesFor } from "./plan-observation.js";
246
+ import { LIVE_PLAN_OMISSION_REASONS, readsAsCredentials } from "./live-plan-reasons.js";
211
247
  import { DATA_TYPE, MODULE_TYPE, RESOURCE_TYPE, callersOfKey } from "./hcl/parse.js";
212
248
  // The channel keys live in their own module so `plugin.ts` can declare
213
249
  // `ownershipChannel` without loading this reader. Re-exported here because
214
250
  // this is where they are used.
215
251
  export { TERRAFORM_STATE_OWNERSHIP_KEYS } from "./state-ownership.js";
216
252
  export { TERRAFORM_LIVE_MARKER_KEYS } from "./live-ownership.js";
253
+ export { LIVE_PLAN_OMISSION_REASONS } from "./live-plan-reasons.js";
217
254
  function asRecord(value) {
218
255
  return typeof value === "object" && value !== null && !Array.isArray(value)
219
256
  ? value
@@ -273,12 +310,26 @@ export function indexStateResources(showJson) {
273
310
  export function classifyStateOwnership(address, index) {
274
311
  return index.rows.has(address) ? "owned" : "unknown";
275
312
  }
313
+ /**
314
+ * True when `instance` is the module call `address` itself or sits beneath it,
315
+ * at any instance key. `address` is a declared module address with no keys
316
+ * (`module.app`, `module.outer.module.inner`); a state or live-plan address
317
+ * carries them (`module.app[0]`, `module.outer["a"].module.inner[1].aws_eip.x`),
318
+ * so the keys come off before the prefix test (#4513).
319
+ */
320
+ function underModule(instance, address) {
321
+ const block = blockAddressOf(instance);
322
+ return block === address || block.startsWith(`${address}.`);
323
+ }
276
324
  /** True when the state carries at least one resource beneath `module.<name>`. */
277
325
  function moduleIsLive(address, index) {
278
- if (index.modules.has(address))
279
- return true;
326
+ // A keyed call's child_modules[] entries are `module.app[0]`, never
327
+ // `module.app`, and so are its rows' prefixes (#4513).
328
+ for (const module of index.modules)
329
+ if (underModule(module, address))
330
+ return true;
280
331
  for (const key of index.rows.keys())
281
- if (key.startsWith(`${address}.`))
332
+ if (underModule(key, address))
282
333
  return true;
283
334
  return false;
284
335
  }
@@ -370,39 +421,6 @@ export function indexLivePlan(document) {
370
421
  ].sort();
371
422
  return index;
372
423
  }
373
- /**
374
- * choudoufu's omission reason codes, mapped onto chant's total vocabulary
375
- * (`internal/live/projection/result.go`). `"absent"` is not an
376
- * {@link UnobservedReason}: it is the tri-state's OBSERVED-ABSENT, the one
377
- * omission that is a claim of knowledge rather than of ignorance.
378
- *
379
- * A code this table does not know maps to `read-failed`, which is the safe
380
- * direction: no NOT-OBSERVED verdict ever becomes a create or a delete.
381
- */
382
- export const LIVE_PLAN_OMISSION_REASONS = {
383
- // The provider was asked and reported, normally, that nothing is there.
384
- ABSENT: "absent",
385
- // The provider errored, or could not be reached in a usable order.
386
- FAILED: "read-failed",
387
- PARENT_UNAVAILABLE: "read-failed",
388
- CYCLE: "read-failed",
389
- INCOMPLETE_BLOCK: "read-failed",
390
- LISTED_NOT_IMPORTABLE: "read-failed",
391
- // Something exists at the identity and does not carry this estate's marker.
392
- // Normally answered by the `unowned[]` section before this table is read;
393
- // this entry is what happens if a document ever carries the omission alone.
394
- UNOWNED: "read-failed",
395
- // The mode cannot name, list or tag this instance at all: a server-assigned
396
- // identity with no marker to find it by, a resource with no cloud object to
397
- // read, or an address another declared instance has taken over.
398
- NEEDS_DISCOVERY: "unsupported-kind",
399
- UNREADABLE: "unsupported-kind",
400
- SUPERSEDED: "unsupported-kind",
401
- };
402
- /** Does this text name a credentials or authorization failure rather than any other error? */
403
- function readsAsCredentials(text) {
404
- return /\b(no valid credential|credentials?|not authorized|unauthorized|accessdenied|access denied|expiredtoken|invalidclienttokenid|signature)\b/i.test(text);
405
- }
406
424
  /** The chant verdict for one omission: `no-credentials` when its detail says so. */
407
425
  function omissionVerdict(row) {
408
426
  const mapped = LIVE_PLAN_OMISSION_REASONS[row.reason] ?? "read-failed";
@@ -416,11 +434,15 @@ function omissionVerdict(row) {
416
434
  * #2360), which places the same blocks against the same document.
417
435
  */
418
436
  export function instancesOf(address, index) {
419
- return index.addresses.filter((a) => a === address || a.startsWith(`${address}[`));
437
+ // `blockAddressOf` strips every key, so an instance under an expanded
438
+ // module call (`module.app[0].aws_eip.pool[1]`) is the block's too (#4488).
439
+ return index.addresses.filter((a) => a === address || a.startsWith(`${address}[`) || blockAddressOf(a) === address);
420
440
  }
421
441
  /** True when the document names any instance under `module.<name>`, at any depth. */
422
442
  function liveModuleMembers(address, index) {
423
- return index.addresses.filter((a) => a === address || a.startsWith(`${address}.`) || a.startsWith(`${address}[`));
443
+ // Keys stripped first, so a nested call under a keyed one
444
+ // (`module.outer[0].module.inner["a"].…`) is a member too (#4513).
445
+ return index.addresses.filter((a) => underModule(a, address));
424
446
  }
425
447
  /**
426
448
  * Classify one instance address against the document, `unowned[]` first: a
@@ -459,6 +481,53 @@ export function classifyLiveInstance(address, index) {
459
481
  }
460
482
  return { kind: "absent" };
461
483
  }
484
+ /**
485
+ * Where one instance address of the declared block `address` sits: its own
486
+ * key and the expanded module path (`@intentius/chant/instance-nodes`), or
487
+ * undefined when it is the block's unexpanded address or another block's.
488
+ * An instance under an expanded module call carries `scope` (#4488), so the
489
+ * live graph draws it under the same id the declared graph lists.
490
+ */
491
+ function instanceKeyAndScope(address, instance) {
492
+ if (instance === address || blockAddressOf(instance) !== address)
493
+ return undefined;
494
+ const path = terraformInstancePath(instance);
495
+ if (!path)
496
+ return undefined;
497
+ return { ...(path.key !== undefined ? { key: path.key } : {}), ...(path.scope !== undefined ? { scope: path.scope } : {}) };
498
+ }
499
+ /**
500
+ * Each instance's own verdict (#4454), keyed as Terraform prints it, for the
501
+ * block's `instances`. An instance under an expanded module call carries its
502
+ * module path as `scope` (#4488) and its own key when the block is expanded too.
503
+ */
504
+ function liveInstanceObservations(address, instances, verdicts) {
505
+ const out = [];
506
+ instances.forEach((a, i) => {
507
+ const at = instanceKeyAndScope(address, a);
508
+ if (at === undefined)
509
+ return;
510
+ const v = verdicts[i];
511
+ switch (v.kind) {
512
+ case "owned":
513
+ out.push({ ...at, address: a, verdict: "present", ownership: "owned", status: "bound", ...(v.row.identity ? { physicalId: v.row.identity } : {}) });
514
+ break;
515
+ case "foreign":
516
+ out.push({ ...at, address: a, verdict: "present", ownership: "foreign", status: "unowned", ...(v.row.identity ? { physicalId: v.row.identity } : {}) });
517
+ break;
518
+ case "adoptable":
519
+ out.push({ ...at, address: a, verdict: "present", ownership: "unknown", status: "adoptable", ...(v.row.identity ? { physicalId: v.row.identity } : {}) });
520
+ break;
521
+ case "absent":
522
+ out.push({ ...at, address: a, verdict: "absent" });
523
+ break;
524
+ case "unobserved":
525
+ out.push({ ...at, address: a, verdict: "not-observed", reason: v.reason, detail: v.detail });
526
+ break;
527
+ }
528
+ });
529
+ return out;
530
+ }
462
531
  /**
463
532
  * The declared block's verdict, aggregated over its instances in the
464
533
  * precedence the module doc states: not-observed, then foreign, then
@@ -473,16 +542,20 @@ function readLiveResource(entity, index, root, queried) {
473
542
  return { absent: true, queried };
474
543
  const verdicts = instances.map((a) => classifyLiveInstance(a, index));
475
544
  const attributes = { address, root, estate: index.estate };
476
- if (instances.length > 1 || instances[0] !== address)
545
+ const expanded = instances.length > 1 || instances[0] !== address;
546
+ if (expanded)
477
547
  attributes.instances = instances;
478
- const unobserved = verdicts.find((v) => v.kind === "unobserved");
479
- if (unobserved && unobserved.kind === "unobserved") {
480
- return { unobserved: { reason: unobserved.reason, detail: unobserved.detail }, queried };
548
+ const perInstance = expanded ? liveInstanceObservations(address, instances, verdicts) : [];
549
+ const withInstances = perInstance.length > 0 ? { instances: perInstance } : {};
550
+ const top = rollupInstances(verdicts, TERRAFORM_INSTANCE_ROLLUP);
551
+ if (top?.kind === "unobserved") {
552
+ return { unobserved: { reason: top.reason, detail: top.detail }, queried };
481
553
  }
482
- const foreign = verdicts.find((v) => v.kind === "foreign");
483
- if (foreign && foreign.kind === "foreign") {
554
+ const foreign = top;
555
+ if (foreign?.kind === "foreign") {
484
556
  return {
485
557
  present: {
558
+ ...withInstances,
486
559
  type: entity.type,
487
560
  physicalId: foreign.row.identity ?? address,
488
561
  status: "unowned",
@@ -496,8 +569,8 @@ function readLiveResource(entity, index, root, queried) {
496
569
  queried,
497
570
  };
498
571
  }
499
- const adoptable = verdicts.find((v) => v.kind === "adoptable");
500
- if (adoptable && adoptable.kind === "adoptable") {
572
+ const adoptable = top;
573
+ if (adoptable?.kind === "adoptable") {
501
574
  // `unknown`, not `foreign`: the resource matches the declaration and one
502
575
  // tag write claims it. The two values that write carries are the whole
503
576
  // adoption proposal #2105 builds its Op on, so they ride the metadata
@@ -505,6 +578,7 @@ function readLiveResource(entity, index, root, queried) {
505
578
  // surfaced, because nothing was read off this resource: it carries none.
506
579
  return {
507
580
  present: {
581
+ ...withInstances,
508
582
  type: entity.type,
509
583
  physicalId: adoptable.row.identity ?? address,
510
584
  status: "adoptable",
@@ -526,13 +600,14 @@ function readLiveResource(entity, index, root, queried) {
526
600
  queried,
527
601
  };
528
602
  }
529
- const owned = verdicts.filter((v) => v.kind === "owned");
530
- if (owned.length === 0)
603
+ if (top?.kind !== "owned")
531
604
  return { absent: true, queried };
605
+ const owned = verdicts.filter((v) => v.kind === "owned");
532
606
  const sources = [...new Set(owned.map((v) => v.row.source ?? "unknown"))].sort();
533
607
  const byMarker = sources.length === 1 && sources[0] === "marker";
534
608
  return {
535
609
  present: {
610
+ ...withInstances,
536
611
  type: entity.type,
537
612
  physicalId: owned.length === 1 ? (owned[0].row.identity ?? address) : address,
538
613
  status: "bound",
@@ -572,17 +647,23 @@ export const TERRAFORM_LIVE_PLAN_DIR_ENV = "CHANT_TERRAFORM_LIVE_PLAN_DIR";
572
647
  export function livePlanDocumentFile(root) {
573
648
  return `${root.replace(/[^A-Za-z0-9._-]/g, "_")}.live-plan.json`;
574
649
  }
575
- /** Write a live root's document where {@link TERRAFORM_LIVE_PLAN_DIR_ENV} asks, if it asks. */
650
+ /**
651
+ * Write a live root's document where {@link TERRAFORM_LIVE_PLAN_DIR_ENV} asks,
652
+ * if it asks. The path written, or undefined when nothing was.
653
+ */
576
654
  function handLivePlanDocument(root, json) {
577
655
  const out = process.env[TERRAFORM_LIVE_PLAN_DIR_ENV];
578
656
  if (!out || !isAbsolute(out))
579
- return;
657
+ return undefined;
580
658
  try {
581
659
  mkdirSync(out, { recursive: true });
582
- writeFileSync(join(out, livePlanDocumentFile(root)), JSON.stringify(json));
660
+ const file = join(out, livePlanDocumentFile(root));
661
+ writeFileSync(file, JSON.stringify(json));
662
+ return file;
583
663
  }
584
664
  catch {
585
665
  // Best effort: the read's answer does not depend on the copy.
666
+ return undefined;
586
667
  }
587
668
  }
588
669
  /**
@@ -590,15 +671,20 @@ function handLivePlanDocument(root, json) {
590
671
  * the document. `terraform show` is never run here, and there is no state
591
672
  * file to run it over.
592
673
  */
593
- function liveAdapter(root, cwd, deps) {
674
+ function liveAdapter(root, cwd, deps, handoff = {}) {
594
675
  const where = cwd ? { cwd } : {};
595
676
  let dir;
596
677
  return {
597
678
  async bind() {
598
679
  const planned = await deps.livePlan({ root, ...where });
599
680
  dir = planned.dir;
600
- handLivePlanDocument(root, planned.json);
601
- return indexLivePlan(planned.json);
681
+ const file = handLivePlanDocument(root, planned.json);
682
+ const index = indexLivePlan(planned.json);
683
+ handoff.json = planned.json;
684
+ handoff.estate = planned.estate ?? index.estate;
685
+ if (file)
686
+ handoff.file = file;
687
+ return index;
602
688
  },
603
689
  classifyBindFailure(err) {
604
690
  const message = err instanceof Error ? err.message.split("\n")[0] : String(err);
@@ -651,6 +737,189 @@ function liveAdapter(root, cwd, deps) {
651
737
  },
652
738
  };
653
739
  }
740
+ const GRAINS = new Set(["block", "instance", "property"]);
741
+ const OWNERSHIPS = new Set(["bound", "unowned", "pending", "foreign"]);
742
+ const OWNED_BY = new Set(["marker", "record", "name"]);
743
+ /**
744
+ * One node's `facts`, keeping only the fields of chant's `EngineFacts` and
745
+ * only values of the shape it declares. Undefined when nothing is left.
746
+ */
747
+ function engineFactsOf(value) {
748
+ const f = asRecord(value);
749
+ const out = {};
750
+ const ownership = asString(f.ownership);
751
+ if (ownership && OWNERSHIPS.has(ownership))
752
+ out.ownership = ownership;
753
+ const ownedBy = asString(f.ownedBy);
754
+ if (ownedBy && OWNED_BY.has(ownedBy))
755
+ out.ownedBy = ownedBy;
756
+ const markers = asRecord(f.markers);
757
+ const encoding = asString(markers.encoding);
758
+ const source = asString(markers.source);
759
+ if ((encoding === "plain" || encoding === "escaped" || encoding === "digest") && (source === "live" || source === "record")) {
760
+ out.markers = {
761
+ ...(asString(markers.owner) ? { owner: asString(markers.owner) } : {}),
762
+ ...(asString(markers.address) ? { address: asString(markers.address) } : {}),
763
+ encoding,
764
+ source,
765
+ };
766
+ }
767
+ const cloudIds = asArray(f.cloudIds)
768
+ .map((c) => ({ kind: asString(asRecord(c).kind), value: asString(asRecord(c).value) }))
769
+ .filter((c) => c.value !== undefined && ["import-id", "arn", "provider-id", "self-link", "k8s-uid"].includes(c.kind ?? ""));
770
+ if (cloudIds.length > 0)
771
+ out.cloudIds = cloudIds;
772
+ const history = asRecord(f.recordVersions);
773
+ if (typeof history.kept === "boolean") {
774
+ out.recordVersions = {
775
+ kept: history.kept,
776
+ versions: asArray(history.versions).flatMap((v) => {
777
+ const r = asRecord(v);
778
+ const id = asString(r.id);
779
+ const writtenAt = asString(r.writtenAt);
780
+ return id && writtenAt ? [{ id, writtenAt, current: r.current === true, deleted: r.deleted === true }] : [];
781
+ }),
782
+ };
783
+ }
784
+ const observedAt = asString(f.observedAt);
785
+ if (observedAt)
786
+ out.observedAt = observedAt;
787
+ return Object.keys(out).length > 0 ? out : undefined;
788
+ }
789
+ /**
790
+ * Read an engine graph document (`choudoufu live-graph -json`, chant#4527's
791
+ * schema) for one root into chant's id space. Node ids are choudoufu's
792
+ * addresses (the #4520 ruling, Q2): a block node is the entity
793
+ * {@link entityKeyFor} names; an instance node (`instanceOf`) belongs to its
794
+ * block's entity, and as an edge endpoint it is the live instance node id
795
+ * core's `observedInstanceNodeId` gives. A node with no address (an estate
796
+ * the configuration reads, `estate:<name>`) has no chant entity, so its
797
+ * edges are left out. Edges keep their grain; one without a grain is left out.
798
+ *
799
+ * Throws when the document is not an engine graph document.
800
+ */
801
+ export function readEngineGraph(root, document) {
802
+ const doc = asRecord(document);
803
+ const engineGraph = asRecord(asRecord(doc.meta).engineGraph);
804
+ if (doc.version !== 1 || engineGraph.format !== 1 || !Array.isArray(doc.nodes)) {
805
+ throw new Error("not an engine graph document (want GraphIR version 1 with meta.engineGraph format 1)");
806
+ }
807
+ const blocks = new Map();
808
+ const instances = new Map();
809
+ const nodeIds = new Map();
810
+ for (const entry of doc.nodes) {
811
+ const node = asRecord(entry);
812
+ const id = asString(node.id);
813
+ const address = asString(asRecord(node.attrs).address);
814
+ if (!id || !address)
815
+ continue;
816
+ const facts = engineFactsOf(node.facts);
817
+ const instanceOf = asString(node.instanceOf);
818
+ if (instanceOf) {
819
+ const block = entityKeyFor(root, instanceOf);
820
+ const key = asString(node.instanceKey);
821
+ const scope = asString(node.instanceScope);
822
+ const liveId = observedInstanceNodeId(block, {
823
+ address: id,
824
+ ...(key !== undefined ? { key } : {}),
825
+ ...(scope !== undefined ? { scope } : {}),
826
+ });
827
+ if (liveId)
828
+ nodeIds.set(id, liveId);
829
+ if (facts) {
830
+ let byAddress = instances.get(block);
831
+ if (!byAddress)
832
+ instances.set(block, (byAddress = new Map()));
833
+ byAddress.set(id, facts);
834
+ }
835
+ continue;
836
+ }
837
+ const key = entityKeyFor(root, id);
838
+ nodeIds.set(id, key);
839
+ if (facts)
840
+ blocks.set(key, facts);
841
+ }
842
+ const edges = [];
843
+ const seen = new Set();
844
+ for (const entry of asArray(doc.edges)) {
845
+ const edge = asRecord(entry);
846
+ const grain = asString(edge.grain);
847
+ if (!grain || !GRAINS.has(grain))
848
+ continue;
849
+ const from = nodeIds.get(asString(edge.from) ?? "");
850
+ const to = nodeIds.get(asString(edge.to) ?? "");
851
+ if (!from || !to || from === to)
852
+ continue;
853
+ const viaAttr = asString(edge.viaAttr);
854
+ const key = `${from}\0${to}\0${viaAttr ?? ""}\0${grain}`;
855
+ if (seen.has(key))
856
+ continue;
857
+ seen.add(key);
858
+ edges.push({ from, to, kind: "ref", ...(viaAttr ? { viaAttr } : {}), grain: grain });
859
+ }
860
+ return { blocks, instances, edges };
861
+ }
862
+ /**
863
+ * `resources` with {@link readEngineGraph}'s facts on them: a block's on
864
+ * `ResourceMetadata.facts`, an instance's on its `instances` entry of the
865
+ * same address. Nothing else changes, and nothing is added for an entity the
866
+ * read did not return.
867
+ */
868
+ export function withEngineFacts(resources, read) {
869
+ const out = {};
870
+ for (const [name, meta] of Object.entries(resources)) {
871
+ const facts = read.blocks.get(name);
872
+ const byAddress = read.instances.get(name);
873
+ const instances = byAddress && meta.instances
874
+ ? meta.instances.map((inst) => {
875
+ const f = byAddress.get(inst.address);
876
+ return f ? { ...inst, facts: f } : inst;
877
+ })
878
+ : meta.instances;
879
+ out[name] = {
880
+ ...meta,
881
+ ...(facts ? { facts } : {}),
882
+ ...(instances ? { instances } : {}),
883
+ };
884
+ }
885
+ return out;
886
+ }
887
+ /**
888
+ * Run `choudoufu live-graph -json -from-plan=<file>` over the document a
889
+ * live root's bind handed off, and read it (#4528). Never throws: facts are
890
+ * extra, so a failure is a note and no facts.
891
+ */
892
+ async function engineGraphFor(root, cwd, deps, handoff) {
893
+ if (!deps.liveGraph || handoff.json === undefined)
894
+ return {};
895
+ let scratch;
896
+ try {
897
+ let file = handoff.file;
898
+ if (!file) {
899
+ scratch = mkdtempSync(join(tmpdir(), "chant-live-graph-"));
900
+ file = join(scratch, livePlanDocumentFile(root));
901
+ writeFileSync(file, JSON.stringify(handoff.json));
902
+ }
903
+ const graph = await deps.liveGraph({ root, ...(cwd ? { cwd } : {}), fromPlan: file, ...(handoff.estate ? { estate: handoff.estate } : {}) });
904
+ if (!graph.supported) {
905
+ return {
906
+ note: `terraform.roots.${root}: engine facts are absent: this choudoufu has no \`live-graph -from-plan\` ` +
907
+ `(${graph.detail}), which needs v${MIN_CHOUDOUFU_GRAPH_VERSION} or later; the rest of the read is unchanged`,
908
+ };
909
+ }
910
+ return { read: readEngineGraph(root, graph.json) };
911
+ }
912
+ catch (err) {
913
+ const message = (err instanceof Error ? err.message : String(err)).split("\n")[0];
914
+ return {
915
+ note: `terraform.roots.${root}: engine facts are absent: \`choudoufu live-graph -json -from-plan\` failed (${message}); the rest of the read is unchanged`,
916
+ };
917
+ }
918
+ finally {
919
+ if (scratch)
920
+ rmSync(scratch, { recursive: true, force: true });
921
+ }
922
+ }
654
923
  /** Parse a `live-ls -json` document. A missing section reads as empty, never as a throw. */
655
924
  export function readLiveLs(document) {
656
925
  const doc = asRecord(document);
@@ -731,7 +1000,8 @@ export function ambientKinds() {
731
1000
  return [RESOURCE_TYPE];
732
1001
  }
733
1002
  /**
734
- * Every live root the project declares, in config order. Reads the project
1003
+ * Every live root the project declares, in config order, leaving out a
1004
+ * `signalOnly` one (#4484). Reads the project
735
1005
  * config the same way the activities do, so `terraform.roots` is read once
736
1006
  * and the same answer reaches both.
737
1007
  */
@@ -748,6 +1018,9 @@ export async function liveRootNames(cwd) {
748
1018
  return [];
749
1019
  const live = [];
750
1020
  for (const [name, root] of Object.entries(namespace.roots ?? {})) {
1021
+ // A `signalOnly` root (#4484) is another project's: it only brings this one into a change.
1022
+ if (root.signalOnly === true)
1023
+ continue;
751
1024
  const estate = detectLiveEstate(resolve(projectRoot, root.dir));
752
1025
  if (estate !== undefined)
753
1026
  live.push({ root: name, estate });
@@ -875,6 +1148,7 @@ const REAL_DEPS = {
875
1148
  show: terraformShow,
876
1149
  livePlan: choudoufuLivePlanDocument,
877
1150
  liveLs: choudoufuLiveLs,
1151
+ liveGraph: choudoufuLiveGraph,
878
1152
  planRead: terraformPlanRead,
879
1153
  };
880
1154
  /**
@@ -965,6 +1239,22 @@ function blockRows(address, index) {
965
1239
  return [exact];
966
1240
  return [...index.rows.values()].filter((row) => blockAddressOf(row.address) === address);
967
1241
  }
1242
+ /**
1243
+ * Each row of an expanded block as an instance (#4454), keyed as Terraform
1244
+ * prints it. A state row is a recorded instance, owned by membership. A row
1245
+ * under an expanded module call carries its module path as `scope`, as on
1246
+ * the live read (#4488).
1247
+ */
1248
+ function stateInstanceObservations(address, rows) {
1249
+ const out = [];
1250
+ for (const r of rows) {
1251
+ const at = instanceKeyAndScope(address, r.address);
1252
+ if (at === undefined)
1253
+ continue;
1254
+ out.push({ ...at, address: r.address, verdict: "present", ownership: "owned", status: r.mode ?? "managed", ...(r.id ? { physicalId: r.id } : {}) });
1255
+ }
1256
+ return out.sort((a, b) => a.address.localeCompare(b.address));
1257
+ }
968
1258
  /**
969
1259
  * One declared entity against a state index: the state read's whole verdict,
970
1260
  * and the plan depth's too, since a plan's `prior_state` is the same shape.
@@ -973,14 +1263,18 @@ function blockRows(address, index) {
973
1263
  function readStateEntity(index, entity, root, queried, extra) {
974
1264
  const { address } = entity;
975
1265
  if (entity.type === RESOURCE_TYPE || entity.type === DATA_TYPE) {
976
- // The exact row first, which is the state read's rule since #2087; the
977
- // plan depth also finds a `count` or `for_each` block by its instances.
978
- const rows = extra ? blockRows(address, index) : index.rows.get(address) ? [index.rows.get(address)] : [];
1266
+ // The exact row first, which is the state read's rule since #2087, then
1267
+ // a `count` or `for_each` block by its instances: the plan depth did
1268
+ // that from the start, and the state read does since #4454, which is
1269
+ // what gives a drift row for `aws_eip.pool[0]` a block to roll up to.
1270
+ const rows = blockRows(address, index);
979
1271
  if (rows.length === 0)
980
1272
  return { absent: true, queried };
981
1273
  const row = rows[0];
1274
+ const instances = stateInstanceObservations(address, rows);
982
1275
  return {
983
1276
  present: {
1277
+ ...(instances.length > 0 ? { instances } : {}),
984
1278
  type: entity.type,
985
1279
  physicalId: rows.length === 1 ? (row.id ?? address) : address,
986
1280
  status: row.mode ?? "managed",
@@ -1125,9 +1419,9 @@ function planAdapter(root, cwd, deps, outcome) {
1125
1419
  * read, unchanged, with the plan's attributes added to every block it bound.
1126
1420
  * See "On a live root" in the module doc.
1127
1421
  */
1128
- function livePlanDepthAdapter(root, cwd, deps, outcome) {
1422
+ function livePlanDepthAdapter(root, cwd, deps, outcome, handoff = {}) {
1129
1423
  const where = cwd ? { cwd } : {};
1130
- const live = liveAdapter(root, cwd, deps);
1424
+ const live = liveAdapter(root, cwd, deps, handoff);
1131
1425
  const planRead = deps.planRead ?? terraformPlanRead;
1132
1426
  const firstLine = (err) => (err instanceof Error ? err.message : String(err)).split("\n")[0];
1133
1427
  let command = "";
@@ -1241,6 +1535,17 @@ export async function describeResources(options, deps = REAL_DEPS) {
1241
1535
  // an overlay exists to prevent.
1242
1536
  const sources = {};
1243
1537
  const rootReads = {};
1538
+ const edges = [];
1539
+ // A live root's observation with the engine graph's facts and edges on it (#4528).
1540
+ const withEngineGraph = async (root, handoff, observed) => {
1541
+ const graph = await engineGraphFor(root, options.cwd, deps, handoff);
1542
+ if (graph.note)
1543
+ notes.push(graph.note);
1544
+ if (!graph.read)
1545
+ return observed;
1546
+ edges.push(...graph.read.edges);
1547
+ return { ...observed, resources: withEngineFacts(observed.resources, graph.read) };
1548
+ };
1244
1549
  const depthOf = byRoot.size > 0 ? await observeDepths(options, notes) : () => "state";
1245
1550
  for (const [root, declared] of byRoot) {
1246
1551
  // One fact decides the whole read, and the parse already recorded it on
@@ -1255,19 +1560,22 @@ export async function describeResources(options, deps = REAL_DEPS) {
1255
1560
  // Both reads (#4371): `live-plan` for ownership, the plan for the
1256
1561
  // attributes on what it bound. `sources` is known only after the read.
1257
1562
  const outcome = { answered: "live", note: "" };
1258
- const read = await observeEntities(declared, livePlanDepthAdapter(root, options.cwd, deps, outcome));
1563
+ const handoff = {};
1564
+ const read = await observeEntities(declared, livePlanDepthAdapter(root, options.cwd, deps, outcome, handoff));
1259
1565
  for (const entity of declared)
1260
1566
  sources[entity.name] = outcome.answered;
1261
1567
  if (outcome.read)
1262
1568
  rootReads[`terraform.roots.${root}`] = outcome.read;
1263
1569
  if (outcome.note)
1264
1570
  notes.push(outcome.note);
1265
- parts.push(normalizeObservation(read));
1571
+ parts.push(await withEngineGraph(root, handoff, normalizeObservation(read)));
1266
1572
  continue;
1267
1573
  }
1268
1574
  for (const entity of declared)
1269
1575
  sources[entity.name] = "live";
1270
- parts.push(normalizeObservation(await observeEntities(declared, liveAdapter(root, options.cwd, deps))));
1576
+ const handoff = {};
1577
+ const read = await observeEntities(declared, liveAdapter(root, options.cwd, deps, handoff));
1578
+ parts.push(await withEngineGraph(root, handoff, normalizeObservation(read)));
1271
1579
  continue;
1272
1580
  }
1273
1581
  if (depth === "plan") {
@@ -1319,5 +1627,5 @@ export async function describeResources(options, deps = REAL_DEPS) {
1319
1627
  }
1320
1628
  // A planned create's plan attributes (#4372) ride beside the verdict, never
1321
1629
  // in `resources`: the block stays absent.
1322
- return observation(resources, unobserved, merged.queried, merged.notes, undefined, sources, merged.annotations, rootReads);
1630
+ return observation(resources, unobserved, merged.queried, merged.notes, undefined, sources, merged.annotations, rootReads, edges);
1323
1631
  }