@intentius/chant 0.124.52 → 0.124.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (584) hide show
  1. package/dist/audit/discover.d.ts.map +1 -1
  2. package/dist/audit/discover.js +3 -1
  3. package/dist/behaviour.d.ts +1 -1
  4. package/dist/behaviour.d.ts.map +1 -1
  5. package/dist/capabilities/templates.d.ts +65 -0
  6. package/dist/capabilities/templates.d.ts.map +1 -0
  7. package/dist/capabilities/templates.js +193 -0
  8. package/dist/capabilities/templates.json +145 -0
  9. package/dist/change-set-graph.d.ts +35 -3
  10. package/dist/change-set-graph.d.ts.map +1 -1
  11. package/dist/change-set-graph.js +60 -9
  12. package/dist/change-set.d.ts +65 -7
  13. package/dist/change-set.d.ts.map +1 -1
  14. package/dist/change-set.js +23 -12
  15. package/dist/ci-identity.d.ts +7 -2
  16. package/dist/ci-identity.d.ts.map +1 -1
  17. package/dist/ci-identity.js +4 -1
  18. package/dist/cli/commands/import-index.d.ts +52 -0
  19. package/dist/cli/commands/import-index.d.ts.map +1 -0
  20. package/dist/cli/commands/import-index.js +226 -0
  21. package/dist/cli/commands/lint.d.ts.map +1 -1
  22. package/dist/cli/commands/lint.js +4 -2
  23. package/dist/cli/handlers/components.d.ts.map +1 -1
  24. package/dist/cli/handlers/components.js +11 -2
  25. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  26. package/dist/cli/handlers/fan-out.js +16 -3
  27. package/dist/cli/handlers/graph.d.ts.map +1 -1
  28. package/dist/cli/handlers/graph.js +22 -4
  29. package/dist/cli/handlers/index-cloud.d.ts +14 -0
  30. package/dist/cli/handlers/index-cloud.d.ts.map +1 -0
  31. package/dist/cli/handlers/index-cloud.js +22 -0
  32. package/dist/cli/handlers/index-diff.d.ts +18 -0
  33. package/dist/cli/handlers/index-diff.d.ts.map +1 -0
  34. package/dist/cli/handlers/index-diff.js +142 -0
  35. package/dist/cli/handlers/lifecycle-drift.d.ts +25 -0
  36. package/dist/cli/handlers/lifecycle-drift.d.ts.map +1 -0
  37. package/dist/cli/handlers/lifecycle-drift.js +64 -0
  38. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  39. package/dist/cli/handlers/lifecycle.js +188 -28
  40. package/dist/cli/handlers/misc.d.ts +9 -0
  41. package/dist/cli/handlers/misc.d.ts.map +1 -1
  42. package/dist/cli/handlers/misc.js +77 -0
  43. package/dist/cli/handlers/pr.d.ts.map +1 -1
  44. package/dist/cli/handlers/pr.js +15 -2
  45. package/dist/cli/handlers/promote.d.ts.map +1 -1
  46. package/dist/cli/handlers/promote.js +89 -15
  47. package/dist/cli/handlers/query.d.ts.map +1 -1
  48. package/dist/cli/handlers/query.js +28 -4
  49. package/dist/cli/handlers/scenario.d.ts.map +1 -1
  50. package/dist/cli/handlers/scenario.js +8 -7
  51. package/dist/cli/handlers/search.d.ts +34 -4
  52. package/dist/cli/handlers/search.d.ts.map +1 -1
  53. package/dist/cli/handlers/search.js +216 -36
  54. package/dist/cli/main.d.ts.map +1 -1
  55. package/dist/cli/main.js +55 -1
  56. package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -1
  57. package/dist/cli/mcp/lifecycle-tools.js +3 -2
  58. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  59. package/dist/cli/mcp/resource-handlers.js +4 -3
  60. package/dist/cli/mcp/workspace-tools.js +1 -1
  61. package/dist/cli/parse-args.d.ts.map +1 -1
  62. package/dist/cli/parse-args.js +55 -2
  63. package/dist/cli/registry.d.ts +19 -1
  64. package/dist/cli/registry.d.ts.map +1 -1
  65. package/dist/cloud-lister/aws.d.ts +35 -0
  66. package/dist/cloud-lister/aws.d.ts.map +1 -0
  67. package/dist/cloud-lister/aws.js +203 -0
  68. package/dist/cloud-lister/azure.d.ts +34 -0
  69. package/dist/cloud-lister/azure.d.ts.map +1 -0
  70. package/dist/cloud-lister/azure.js +129 -0
  71. package/dist/cloud-lister/common.d.ts +40 -0
  72. package/dist/cloud-lister/common.d.ts.map +1 -0
  73. package/dist/cloud-lister/common.js +83 -0
  74. package/dist/cloud-lister/credentials.d.ts +50 -0
  75. package/dist/cloud-lister/credentials.d.ts.map +1 -0
  76. package/dist/cloud-lister/credentials.js +86 -0
  77. package/dist/cloud-lister/fakes.d.ts +27 -0
  78. package/dist/cloud-lister/fakes.d.ts.map +1 -0
  79. package/dist/cloud-lister/fakes.js +50 -0
  80. package/dist/cloud-lister/gcp.d.ts +42 -0
  81. package/dist/cloud-lister/gcp.d.ts.map +1 -0
  82. package/dist/cloud-lister/gcp.js +143 -0
  83. package/dist/cloud-lister/guard.d.ts +49 -0
  84. package/dist/cloud-lister/guard.d.ts.map +1 -0
  85. package/dist/cloud-lister/guard.js +103 -0
  86. package/dist/cloud-lister/ids.d.ts +23 -0
  87. package/dist/cloud-lister/ids.d.ts.map +1 -0
  88. package/dist/cloud-lister/ids.js +42 -0
  89. package/dist/cloud-lister/index.d.ts +67 -0
  90. package/dist/cloud-lister/index.d.ts.map +1 -0
  91. package/dist/cloud-lister/index.js +102 -0
  92. package/dist/cloud-lister/kubernetes.d.ts +64 -0
  93. package/dist/cloud-lister/kubernetes.d.ts.map +1 -0
  94. package/dist/cloud-lister/kubernetes.js +276 -0
  95. package/dist/cloud-lister/markers.d.ts +31 -0
  96. package/dist/cloud-lister/markers.d.ts.map +1 -0
  97. package/dist/cloud-lister/markers.js +73 -0
  98. package/dist/cloud-lister/mask.d.ts +31 -0
  99. package/dist/cloud-lister/mask.d.ts.map +1 -0
  100. package/dist/cloud-lister/mask.js +74 -0
  101. package/dist/cloud-lister/sigv4.d.ts +29 -0
  102. package/dist/cloud-lister/sigv4.d.ts.map +1 -0
  103. package/dist/cloud-lister/sigv4.js +54 -0
  104. package/dist/cloud-lister/transport.d.ts +55 -0
  105. package/dist/cloud-lister/transport.d.ts.map +1 -0
  106. package/dist/cloud-lister/transport.js +86 -0
  107. package/dist/cloud-lister/types.d.ts +121 -0
  108. package/dist/cloud-lister/types.d.ts.map +1 -0
  109. package/dist/cloud-lister/types.js +24 -0
  110. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  111. package/dist/components/capability-plugin-loader.js +6 -0
  112. package/dist/components/capability-plugin.d.ts +9 -0
  113. package/dist/components/capability-plugin.d.ts.map +1 -1
  114. package/dist/components/capability.d.ts +215 -0
  115. package/dist/components/capability.d.ts.map +1 -1
  116. package/dist/components/cli-support.d.ts +21 -0
  117. package/dist/components/cli-support.d.ts.map +1 -1
  118. package/dist/components/cli-support.js +58 -14
  119. package/dist/components/declared-reads.d.ts +38 -0
  120. package/dist/components/declared-reads.d.ts.map +1 -0
  121. package/dist/components/declared-reads.js +131 -0
  122. package/dist/components/deploy-units.d.ts.map +1 -1
  123. package/dist/components/deploy-units.js +4 -0
  124. package/dist/components/driver.d.ts +34 -1
  125. package/dist/components/driver.d.ts.map +1 -1
  126. package/dist/components/driver.js +66 -3
  127. package/dist/components/fan-out-run.d.ts +23 -1
  128. package/dist/components/fan-out-run.d.ts.map +1 -1
  129. package/dist/components/fan-out-run.js +82 -14
  130. package/dist/components/fan-out-support.d.ts +6 -0
  131. package/dist/components/fan-out-support.d.ts.map +1 -1
  132. package/dist/components/fan-out-support.js +4 -1
  133. package/dist/components/fan-out.d.ts +28 -0
  134. package/dist/components/fan-out.d.ts.map +1 -1
  135. package/dist/components/groups.d.ts +229 -0
  136. package/dist/components/groups.d.ts.map +1 -0
  137. package/dist/components/groups.js +760 -0
  138. package/dist/components/index.d.ts +6 -2
  139. package/dist/components/index.d.ts.map +1 -1
  140. package/dist/components/index.js +4 -0
  141. package/dist/components/plan-shape-conformance.d.ts +48 -0
  142. package/dist/components/plan-shape-conformance.d.ts.map +1 -0
  143. package/dist/components/plan-shape-conformance.js +122 -0
  144. package/dist/components/pr-run.d.ts +41 -4
  145. package/dist/components/pr-run.d.ts.map +1 -1
  146. package/dist/components/pr-run.js +209 -39
  147. package/dist/components/promote-rounds.d.ts +9 -0
  148. package/dist/components/promote-rounds.d.ts.map +1 -1
  149. package/dist/components/promote-rounds.js +23 -2
  150. package/dist/components/promote.d.ts +17 -0
  151. package/dist/components/promote.d.ts.map +1 -1
  152. package/dist/components/promote.js +36 -6
  153. package/dist/components/release-gate.d.ts +23 -1
  154. package/dist/components/release-gate.d.ts.map +1 -1
  155. package/dist/components/release-gate.js +97 -33
  156. package/dist/components/run-groups.d.ts +58 -0
  157. package/dist/components/run-groups.d.ts.map +1 -0
  158. package/dist/components/run-groups.js +78 -0
  159. package/dist/components/starter-plugin.d.ts +1 -0
  160. package/dist/components/starter-plugin.d.ts.map +1 -1
  161. package/dist/components/starter-plugin.js +6 -1
  162. package/dist/components/verbs/index.d.ts +1 -0
  163. package/dist/components/verbs/index.d.ts.map +1 -1
  164. package/dist/components/verbs/index.js +1 -0
  165. package/dist/components/verbs/member-outputs.d.ts +187 -0
  166. package/dist/components/verbs/member-outputs.d.ts.map +1 -0
  167. package/dist/components/verbs/member-outputs.js +218 -0
  168. package/dist/config.d.ts +60 -0
  169. package/dist/config.d.ts.map +1 -1
  170. package/dist/config.js +27 -0
  171. package/dist/deep-observation.d.ts +1 -13
  172. package/dist/deep-observation.d.ts.map +1 -1
  173. package/dist/deep-observation.js +5 -22
  174. package/dist/drift-record.d.ts +542 -0
  175. package/dist/drift-record.d.ts.map +1 -0
  176. package/dist/drift-record.js +886 -0
  177. package/dist/engine-facts.d.ts +144 -0
  178. package/dist/engine-facts.d.ts.map +1 -0
  179. package/dist/engine-facts.js +14 -0
  180. package/dist/graph-ir.d.ts +153 -2
  181. package/dist/graph-ir.d.ts.map +1 -1
  182. package/dist/graph-ir.js +224 -6
  183. package/dist/graph-refs.d.ts +145 -0
  184. package/dist/graph-refs.d.ts.map +1 -1
  185. package/dist/graph-refs.js +305 -0
  186. package/dist/graph-schema/fixtures/adopt.json +15 -0
  187. package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
  188. package/dist/graph-schema/fixtures/index.json +68 -0
  189. package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
  190. package/dist/graph-schema/fixtures.d.ts +23 -0
  191. package/dist/graph-schema/fixtures.d.ts.map +1 -0
  192. package/dist/graph-schema/fixtures.js +246 -0
  193. package/dist/graph-schema/generate.d.ts +15 -0
  194. package/dist/graph-schema/generate.d.ts.map +1 -0
  195. package/dist/graph-schema/generate.js +267 -0
  196. package/dist/graph-schema/graph.schema.json +1387 -0
  197. package/dist/health.schema.json +2 -1
  198. package/dist/identity.d.ts.map +1 -1
  199. package/dist/identity.js +2 -0
  200. package/dist/index.d.ts +1 -0
  201. package/dist/index.d.ts.map +1 -1
  202. package/dist/index.js +2 -0
  203. package/dist/instance-nodes.d.ts +159 -0
  204. package/dist/instance-nodes.d.ts.map +1 -0
  205. package/dist/instance-nodes.js +231 -0
  206. package/dist/lexicon.d.ts +72 -0
  207. package/dist/lexicon.d.ts.map +1 -1
  208. package/dist/lifecycle/adoption-file.d.ts +56 -0
  209. package/dist/lifecycle/adoption-file.d.ts.map +1 -0
  210. package/dist/lifecycle/adoption-file.js +123 -0
  211. package/dist/lifecycle/cloud-index.d.ts +186 -0
  212. package/dist/lifecycle/cloud-index.d.ts.map +1 -0
  213. package/dist/lifecycle/cloud-index.js +618 -0
  214. package/dist/lifecycle/derive-edges.d.ts +59 -0
  215. package/dist/lifecycle/derive-edges.d.ts.map +1 -0
  216. package/dist/lifecycle/derive-edges.js +126 -0
  217. package/dist/lifecycle/git.d.ts +10 -9
  218. package/dist/lifecycle/git.d.ts.map +1 -1
  219. package/dist/lifecycle/git.js +7 -1
  220. package/dist/lifecycle/identity-match.d.ts +133 -0
  221. package/dist/lifecycle/identity-match.d.ts.map +1 -0
  222. package/dist/lifecycle/identity-match.js +147 -0
  223. package/dist/lifecycle/index-diff.d.ts +224 -0
  224. package/dist/lifecycle/index-diff.d.ts.map +1 -0
  225. package/dist/lifecycle/index-diff.js +327 -0
  226. package/dist/lifecycle/index-document.d.ts +130 -0
  227. package/dist/lifecycle/index-document.d.ts.map +1 -0
  228. package/dist/lifecycle/index-document.js +189 -0
  229. package/dist/lifecycle/index-drift.d.ts +121 -0
  230. package/dist/lifecycle/index-drift.d.ts.map +1 -0
  231. package/dist/lifecycle/index-drift.js +178 -0
  232. package/dist/lifecycle/index-import.d.ts +168 -0
  233. package/dist/lifecycle/index-import.d.ts.map +1 -0
  234. package/dist/lifecycle/index-import.js +515 -0
  235. package/dist/lifecycle/index-join.d.ts +129 -0
  236. package/dist/lifecycle/index-join.d.ts.map +1 -0
  237. package/dist/lifecycle/index-join.js +248 -0
  238. package/dist/lifecycle/index.d.ts +4 -0
  239. package/dist/lifecycle/index.d.ts.map +1 -1
  240. package/dist/lifecycle/index.js +4 -0
  241. package/dist/lifecycle/observation-baseline.d.ts +61 -21
  242. package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
  243. package/dist/lifecycle/observation-baseline.js +144 -19
  244. package/dist/lifecycle/observe.d.ts.map +1 -1
  245. package/dist/lifecycle/observe.js +6 -1
  246. package/dist/lifecycle/query.d.ts +45 -4
  247. package/dist/lifecycle/query.d.ts.map +1 -1
  248. package/dist/lifecycle/query.js +89 -7
  249. package/dist/lifecycle/replay.d.ts +10 -3
  250. package/dist/lifecycle/replay.d.ts.map +1 -1
  251. package/dist/lifecycle/replay.js +10 -6
  252. package/dist/lifecycle/scenario.d.ts +4 -3
  253. package/dist/lifecycle/scenario.d.ts.map +1 -1
  254. package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
  255. package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
  256. package/dist/lifecycle/snapshot-store-fake.js +21 -0
  257. package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
  258. package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
  259. package/dist/lifecycle/snapshot-store-s3.js +254 -0
  260. package/dist/lifecycle/snapshot-store.d.ts +170 -0
  261. package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
  262. package/dist/lifecycle/snapshot-store.js +832 -0
  263. package/dist/lifecycle/snapshot.d.ts +11 -0
  264. package/dist/lifecycle/snapshot.d.ts.map +1 -1
  265. package/dist/lifecycle/snapshot.js +18 -5
  266. package/dist/lifecycle/type-listing.d.ts +148 -0
  267. package/dist/lifecycle/type-listing.d.ts.map +1 -0
  268. package/dist/lifecycle/type-listing.js +166 -0
  269. package/dist/lint/component-checks.d.ts +15 -1
  270. package/dist/lint/component-checks.d.ts.map +1 -1
  271. package/dist/lint/component-checks.js +1 -0
  272. package/dist/lint/presets/relaxed.json +2 -1
  273. package/dist/lint/presets/strict.json +2 -1
  274. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
  275. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
  276. package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
  277. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
  278. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
  279. package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
  280. package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
  281. package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
  282. package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
  283. package/dist/lint/rules/comp/index.d.ts +2 -1
  284. package/dist/lint/rules/comp/index.d.ts.map +1 -1
  285. package/dist/lint/rules/comp/index.js +4 -1
  286. package/dist/observation.d.ts +19 -2
  287. package/dist/observation.d.ts.map +1 -1
  288. package/dist/observation.js +18 -1
  289. package/dist/op/activities/activity-contracts.d.ts +1 -1
  290. package/dist/op/activities/activity-contracts.js +1 -1
  291. package/dist/op/activities/change-set.d.ts +6 -4
  292. package/dist/op/activities/change-set.d.ts.map +1 -1
  293. package/dist/op/activities/change-set.js +4 -2
  294. package/dist/op/activity-failure.d.ts +27 -0
  295. package/dist/op/activity-failure.d.ts.map +1 -1
  296. package/dist/op/activity-failure.js +31 -0
  297. package/dist/op/index.d.ts +2 -1
  298. package/dist/op/index.d.ts.map +1 -1
  299. package/dist/op/index.js +1 -1
  300. package/dist/op/local-executor.d.ts +5 -1
  301. package/dist/op/local-executor.d.ts.map +1 -1
  302. package/dist/op/local-executor.js +22 -8
  303. package/dist/pr-loop.d.ts +56 -1
  304. package/dist/pr-loop.d.ts.map +1 -1
  305. package/dist/pr-loop.js +51 -1
  306. package/dist/pulumi-urn.d.ts +101 -0
  307. package/dist/pulumi-urn.d.ts.map +1 -0
  308. package/dist/pulumi-urn.js +131 -0
  309. package/dist/release-floors.d.ts +23 -0
  310. package/dist/release-floors.d.ts.map +1 -0
  311. package/dist/release-floors.js +22 -0
  312. package/dist/sensitive-key.d.ts +23 -0
  313. package/dist/sensitive-key.d.ts.map +1 -0
  314. package/dist/sensitive-key.js +31 -0
  315. package/dist/wave-runtime.mjs +234 -234
  316. package/dist/workspace/adoption-report.d.ts +53 -0
  317. package/dist/workspace/adoption-report.d.ts.map +1 -0
  318. package/dist/workspace/adoption-report.js +76 -0
  319. package/dist/workspace/capabilities-contract.d.ts +7 -0
  320. package/dist/workspace/capabilities-contract.d.ts.map +1 -1
  321. package/dist/workspace/capabilities-contract.js +2 -1
  322. package/dist/workspace/capabilities.schema.json +98 -0
  323. package/dist/workspace/change-set.schema.json +42 -3
  324. package/dist/workspace/ci-cloud-identity.d.ts +13 -1
  325. package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
  326. package/dist/workspace/ci-cloud-identity.js +30 -1
  327. package/dist/workspace/compose-graph.d.ts +10 -1
  328. package/dist/workspace/compose-graph.d.ts.map +1 -1
  329. package/dist/workspace/compose-graph.js +6 -1
  330. package/dist/workspace/conformance/index.d.ts +1 -1
  331. package/dist/workspace/conformance/index.d.ts.map +1 -1
  332. package/dist/workspace/conformance/index.js +6 -1
  333. package/dist/workspace/drift-cli.d.ts +81 -0
  334. package/dist/workspace/drift-cli.d.ts.map +1 -0
  335. package/dist/workspace/drift-cli.js +317 -0
  336. package/dist/workspace/drift.schema.json +672 -0
  337. package/dist/workspace/graph-cli.d.ts +2 -0
  338. package/dist/workspace/graph-cli.d.ts.map +1 -1
  339. package/dist/workspace/graph-cli.js +3 -0
  340. package/dist/workspace/graph.schema.json +72 -0
  341. package/dist/workspace/init.d.ts +5 -1
  342. package/dist/workspace/init.d.ts.map +1 -1
  343. package/dist/workspace/init.js +55 -9
  344. package/dist/workspace/ledger.schema.json +1 -1
  345. package/dist/workspace/nested-graph.d.ts.map +1 -1
  346. package/dist/workspace/nested-graph.js +2 -0
  347. package/dist/workspace/pr-report.schema.json +51 -2
  348. package/dist/workspace/promote-waves.d.ts +112 -0
  349. package/dist/workspace/promote-waves.d.ts.map +1 -0
  350. package/dist/workspace/promote-waves.js +290 -0
  351. package/dist/workspace/promote.d.ts +84 -2
  352. package/dist/workspace/promote.d.ts.map +1 -1
  353. package/dist/workspace/promote.js +347 -61
  354. package/dist/workspace/reason-codes.d.ts +12 -0
  355. package/dist/workspace/reason-codes.d.ts.map +1 -1
  356. package/dist/workspace/reason-codes.js +14 -0
  357. package/dist/workspace/search-contract.d.ts +3 -3
  358. package/dist/workspace/search-contract.d.ts.map +1 -1
  359. package/dist/workspace/search-contract.js +2 -0
  360. package/dist/workspace/search.schema.json +103 -5
  361. package/package.json +14 -1
  362. package/src/__fixtures__/drift-record/README.md +58 -0
  363. package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
  364. package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
  365. package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
  366. package/src/__fixtures__/drift-record/component-status.json +91 -0
  367. package/src/__fixtures__/drift-record/deep-diff.json +259 -0
  368. package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
  369. package/src/__fixtures__/drift-record/live-diff.json +125 -0
  370. package/src/audit/discover.e2e.test.ts +14 -0
  371. package/src/audit/discover.ts +3 -1
  372. package/src/behaviour.ts +1 -1
  373. package/src/capabilities/templates.json +145 -0
  374. package/src/capabilities/templates.test.ts +212 -0
  375. package/src/capabilities/templates.ts +204 -0
  376. package/src/change-set-bundle.test.ts +83 -0
  377. package/src/change-set-graph.ts +80 -9
  378. package/src/change-set.test.ts +24 -0
  379. package/src/change-set.ts +77 -14
  380. package/src/ci-identity-targets.test.ts +23 -0
  381. package/src/ci-identity.ts +10 -4
  382. package/src/cli/commands/import-index.test.ts +117 -0
  383. package/src/cli/commands/import-index.ts +265 -0
  384. package/src/cli/commands/lint.ts +4 -2
  385. package/src/cli/handlers/components.test.ts +9 -0
  386. package/src/cli/handlers/components.ts +12 -2
  387. package/src/cli/handlers/fan-out.ts +17 -3
  388. package/src/cli/handlers/graph.test.ts +42 -0
  389. package/src/cli/handlers/graph.ts +26 -4
  390. package/src/cli/handlers/index-cloud.ts +32 -0
  391. package/src/cli/handlers/index-diff.test.ts +68 -0
  392. package/src/cli/handlers/index-diff.ts +146 -0
  393. package/src/cli/handlers/ledger-root-member.test.ts +161 -0
  394. package/src/cli/handlers/lifecycle-drift.ts +68 -0
  395. package/src/cli/handlers/lifecycle.test.ts +33 -11
  396. package/src/cli/handlers/lifecycle.ts +205 -30
  397. package/src/cli/handlers/misc.ts +73 -0
  398. package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
  399. package/src/cli/handlers/pr.ts +14 -2
  400. package/src/cli/handlers/promote.test.ts +64 -0
  401. package/src/cli/handlers/promote.ts +81 -6
  402. package/src/cli/handlers/query.ts +27 -4
  403. package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
  404. package/src/cli/handlers/run-groups-cli.test.ts +229 -0
  405. package/src/cli/handlers/scenario.test.ts +13 -0
  406. package/src/cli/handlers/scenario.ts +8 -7
  407. package/src/cli/handlers/search-index.test.ts +255 -0
  408. package/src/cli/handlers/search-json.test.ts +1 -1
  409. package/src/cli/handlers/search-ownership.test.ts +194 -0
  410. package/src/cli/handlers/search.ts +271 -38
  411. package/src/cli/main.test.ts +7 -0
  412. package/src/cli/main.ts +56 -1
  413. package/src/cli/mcp/lifecycle-tools.ts +3 -2
  414. package/src/cli/mcp/resource-handlers.ts +4 -3
  415. package/src/cli/mcp/workspace-tools.ts +1 -1
  416. package/src/cli/parse-args.ts +40 -2
  417. package/src/cli/registry.ts +19 -1
  418. package/src/cloud-lister/aws.test.ts +202 -0
  419. package/src/cloud-lister/aws.ts +226 -0
  420. package/src/cloud-lister/azure.test.ts +132 -0
  421. package/src/cloud-lister/azure.ts +142 -0
  422. package/src/cloud-lister/common.ts +115 -0
  423. package/src/cloud-lister/credentials.ts +110 -0
  424. package/src/cloud-lister/fakes.ts +66 -0
  425. package/src/cloud-lister/gcp.test.ts +121 -0
  426. package/src/cloud-lister/gcp.ts +159 -0
  427. package/src/cloud-lister/guard.test.ts +198 -0
  428. package/src/cloud-lister/guard.ts +111 -0
  429. package/src/cloud-lister/ids.ts +54 -0
  430. package/src/cloud-lister/index.ts +134 -0
  431. package/src/cloud-lister/kubernetes.test.ts +160 -0
  432. package/src/cloud-lister/kubernetes.ts +284 -0
  433. package/src/cloud-lister/markers.ts +74 -0
  434. package/src/cloud-lister/mask.ts +77 -0
  435. package/src/cloud-lister/shape.test.ts +81 -0
  436. package/src/cloud-lister/sigv4.ts +70 -0
  437. package/src/cloud-lister/transport.ts +118 -0
  438. package/src/cloud-lister/types.ts +135 -0
  439. package/src/components/capability-plugin-loader.ts +4 -0
  440. package/src/components/capability-plugin.ts +10 -0
  441. package/src/components/capability.ts +229 -0
  442. package/src/components/cli-support.ts +70 -14
  443. package/src/components/declared-reads.test.ts +103 -0
  444. package/src/components/declared-reads.ts +134 -0
  445. package/src/components/deploy-units.ts +4 -0
  446. package/src/components/driver.ts +89 -4
  447. package/src/components/fan-out-run.ts +95 -15
  448. package/src/components/fan-out-support.ts +10 -1
  449. package/src/components/fan-out.ts +30 -0
  450. package/src/components/groups.test.ts +320 -0
  451. package/src/components/groups.ts +880 -0
  452. package/src/components/index.ts +42 -0
  453. package/src/components/plan-shape-conformance.ts +130 -0
  454. package/src/components/pr-env-run-bundle.test.ts +2 -0
  455. package/src/components/pr-run.test.ts +516 -3
  456. package/src/components/pr-run.ts +231 -41
  457. package/src/components/promote-rounds.ts +25 -2
  458. package/src/components/promote.ts +55 -5
  459. package/src/components/registry.test.ts +1 -1
  460. package/src/components/release-gate.ts +115 -30
  461. package/src/components/release-groups.test.ts +362 -0
  462. package/src/components/run-groups.ts +113 -0
  463. package/src/components/starter-plugin.ts +6 -0
  464. package/src/components/verbs/index.ts +1 -0
  465. package/src/components/verbs/member-outputs.test.ts +205 -0
  466. package/src/components/verbs/member-outputs.ts +308 -0
  467. package/src/config.ts +44 -0
  468. package/src/deep-observation.ts +5 -23
  469. package/src/drift-record.test.ts +596 -0
  470. package/src/drift-record.ts +1206 -0
  471. package/src/engine-facts.guard.test.ts +111 -0
  472. package/src/engine-facts.test.ts +178 -0
  473. package/src/engine-facts.ts +154 -0
  474. package/src/gated-waves-bundle.test.ts +2 -0
  475. package/src/graph-ir-live.test.ts +92 -0
  476. package/src/graph-ir.ts +317 -7
  477. package/src/graph-refs.ts +416 -0
  478. package/src/graph-schema/fixtures/adopt.json +15 -0
  479. package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
  480. package/src/graph-schema/fixtures/index.json +68 -0
  481. package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
  482. package/src/graph-schema/fixtures.ts +267 -0
  483. package/src/graph-schema/generate.ts +263 -0
  484. package/src/graph-schema/graph-schema.test.ts +153 -0
  485. package/src/graph-schema/graph.schema.json +1387 -0
  486. package/src/health.schema.json +2 -1
  487. package/src/identity.ts +2 -0
  488. package/src/index.ts +2 -0
  489. package/src/instance-nodes.ts +288 -0
  490. package/src/lexicon.ts +76 -0
  491. package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
  492. package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
  493. package/src/lifecycle/adoption-file.test.ts +100 -0
  494. package/src/lifecycle/adoption-file.ts +154 -0
  495. package/src/lifecycle/cloud-index.test.ts +517 -0
  496. package/src/lifecycle/cloud-index.ts +736 -0
  497. package/src/lifecycle/derive-edges.test.ts +193 -0
  498. package/src/lifecycle/derive-edges.ts +182 -0
  499. package/src/lifecycle/git.ts +10 -4
  500. package/src/lifecycle/identity-match.test.ts +308 -0
  501. package/src/lifecycle/identity-match.ts +257 -0
  502. package/src/lifecycle/index-diff.test.ts +198 -0
  503. package/src/lifecycle/index-diff.ts +476 -0
  504. package/src/lifecycle/index-document.test.ts +64 -0
  505. package/src/lifecycle/index-document.ts +299 -0
  506. package/src/lifecycle/index-drift.test.ts +191 -0
  507. package/src/lifecycle/index-drift.ts +304 -0
  508. package/src/lifecycle/index-import.test.ts +255 -0
  509. package/src/lifecycle/index-import.ts +634 -0
  510. package/src/lifecycle/index-join.test.ts +250 -0
  511. package/src/lifecycle/index-join.ts +304 -0
  512. package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
  513. package/src/lifecycle/index.ts +4 -0
  514. package/src/lifecycle/member-ledger.test.ts +25 -7
  515. package/src/lifecycle/observation-baseline.test.ts +72 -1
  516. package/src/lifecycle/observation-baseline.ts +190 -27
  517. package/src/lifecycle/observe.ts +6 -1
  518. package/src/lifecycle/query.test.ts +69 -1
  519. package/src/lifecycle/query.ts +103 -10
  520. package/src/lifecycle/replay.test.ts +9 -5
  521. package/src/lifecycle/replay.ts +9 -5
  522. package/src/lifecycle/scenario.ts +4 -3
  523. package/src/lifecycle/snapshot-store-fake.ts +29 -0
  524. package/src/lifecycle/snapshot-store-s3.ts +316 -0
  525. package/src/lifecycle/snapshot-store.test.ts +385 -0
  526. package/src/lifecycle/snapshot-store.ts +981 -0
  527. package/src/lifecycle/snapshot.test.ts +13 -0
  528. package/src/lifecycle/snapshot.ts +23 -6
  529. package/src/lifecycle/type-listing.test.ts +166 -0
  530. package/src/lifecycle/type-listing.ts +309 -0
  531. package/src/lint/component-checks.ts +17 -1
  532. package/src/lint/config.test.ts +3 -0
  533. package/src/lint/presets/relaxed.json +2 -1
  534. package/src/lint/presets/strict.json +2 -1
  535. package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
  536. package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
  537. package/src/lint/rules/comp/comp.test.ts +2 -1
  538. package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
  539. package/src/lint/rules/comp/comp008.test.ts +77 -0
  540. package/src/lint/rules/comp/index.ts +4 -1
  541. package/src/observation-edges.test.ts +33 -0
  542. package/src/observation.ts +36 -1
  543. package/src/op/activities/activity-contracts.ts +1 -1
  544. package/src/op/activities/change-set.ts +8 -5
  545. package/src/op/activity-failure.ts +36 -0
  546. package/src/op/index.ts +2 -1
  547. package/src/op/local-executor.test.ts +80 -0
  548. package/src/op/local-executor.ts +27 -9
  549. package/src/pr-loop-bundle.test.ts +5 -2
  550. package/src/pr-loop.test.ts +43 -0
  551. package/src/pr-loop.ts +111 -2
  552. package/src/pulumi-urn.ts +151 -0
  553. package/src/release-floors.ts +22 -0
  554. package/src/sensitive-key.ts +34 -0
  555. package/src/workspace/adoption-report.test.ts +61 -0
  556. package/src/workspace/adoption-report.ts +103 -0
  557. package/src/workspace/capabilities-contract.test.ts +4 -0
  558. package/src/workspace/capabilities-contract.ts +9 -1
  559. package/src/workspace/capabilities.schema.json +98 -0
  560. package/src/workspace/change-set.schema.json +42 -3
  561. package/src/workspace/ci-cloud-identity.ts +31 -1
  562. package/src/workspace/compose-graph.test.ts +12 -0
  563. package/src/workspace/compose-graph.ts +12 -1
  564. package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
  565. package/src/workspace/conformance/index.ts +6 -1
  566. package/src/workspace/drift-cli.test.ts +65 -0
  567. package/src/workspace/drift-cli.ts +365 -0
  568. package/src/workspace/drift.schema.json +672 -0
  569. package/src/workspace/graph-cli.ts +5 -0
  570. package/src/workspace/graph.schema.json +72 -0
  571. package/src/workspace/init.test.ts +38 -1
  572. package/src/workspace/init.ts +50 -8
  573. package/src/workspace/intent-gaps.test.ts +18 -2
  574. package/src/workspace/ledger.schema.json +1 -1
  575. package/src/workspace/nested-graph.ts +1 -0
  576. package/src/workspace/pr-report.schema.json +51 -2
  577. package/src/workspace/promote-waves.test.ts +296 -0
  578. package/src/workspace/promote-waves.ts +340 -0
  579. package/src/workspace/promote.test.ts +158 -4
  580. package/src/workspace/promote.ts +398 -57
  581. package/src/workspace/reason-codes.test.ts +4 -1
  582. package/src/workspace/reason-codes.ts +14 -0
  583. package/src/workspace/search-contract.ts +2 -0
  584. package/src/workspace/search.schema.json +103 -5
@@ -0,0 +1,229 @@
1
+ /**
2
+ * `chant components fan-out` and `chant run --components` form composed
3
+ * groups (ws-107, #4465), driven through their CLI handlers (chant#4505).
4
+ * `release-groups.test.ts` covers `runFanOut` and `runInterpretDriver` given
5
+ * a grouped plan; this drives the commands that form the plan themselves, the
6
+ * path that went red at round 1's epic-ready (`run --components` forming its
7
+ * groups before the graph was resolved).
8
+ *
9
+ * Discovery, config, the capability registry and the gate ledger are fakes.
10
+ * The fake `cfn-deploy` creates one stack whose `id` is known only after it
11
+ * applies; with the group pair it declares `planGroup` and `runGroup`. b reads
12
+ * a's id and c reads b's, so a, b and c form one group.
13
+ */
14
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
15
+ import type { Component } from "../../components/component";
16
+ import type { ParsedArgs } from "../registry";
17
+
18
+ const affectedStacksMock = vi.fn();
19
+ const discoverComponentsMock = vi.fn();
20
+ const buildCapabilityRegistryMock = vi.fn();
21
+
22
+ vi.mock("../../lifecycle/affected", () => ({
23
+ affectedStacks: (...args: unknown[]) => affectedStacksMock(...args),
24
+ }));
25
+
26
+ vi.mock("../../components/discover", () => ({
27
+ discoverComponents: (...args: unknown[]) => discoverComponentsMock(...args),
28
+ }));
29
+
30
+ vi.mock("../../config", async () => {
31
+ const actual = await vi.importActual<typeof import("../../config")>("../../config");
32
+ return { ...actual, loadChantConfig: async () => ({ config: { lexicons: ["aws"] } }) };
33
+ });
34
+
35
+ vi.mock("../../components/capability-plugin-loader", () => ({
36
+ buildCapabilityRegistry: (...args: unknown[]) => buildCapabilityRegistryMock(...args),
37
+ }));
38
+
39
+ /** The real gate ledger is this repository's `chant/lifecycle` branch; a test never appends to it. */
40
+ vi.mock("../../op/gate", async () => {
41
+ const actual = await vi.importActual<typeof import("../../op/gate")>("../../op/gate");
42
+ return { ...actual, gitGateLedgerPort: () => actual.memoryGateLedgerPort(), gateIsSealed: async () => false };
43
+ });
44
+
45
+ /** The release ledger, for the same reason. */
46
+ vi.mock("../../components/auto-release", async () => {
47
+ const actual = await vi.importActual<typeof import("../../components/auto-release")>("../../components/auto-release");
48
+ return { ...actual, maybeRecordAutoRelease: async () => ({ recorded: false, reason: "opted-out" }) };
49
+ });
50
+
51
+ /** `run --components` on the local runtime: the real `runComponents`, with the test's registry. */
52
+ vi.mock("../../op/runtimes/local", async () => {
53
+ const { runComponents } = await import("../../components/cli-support");
54
+ const { memoryGateLedgerPort } = await import("../../op/gate");
55
+ return {
56
+ createLocalOpRuntime: () => ({
57
+ name: "local",
58
+ runComponents: async (path: string, selector: string, options: object) =>
59
+ runComponents(path, selector, { ...options, registry: await buildCapabilityRegistryMock(), config: {}, revision: null, gates: memoryGateLedgerPort() }),
60
+ }),
61
+ };
62
+ });
63
+
64
+ const { CapabilityRegistry } = await import("../../components/capability");
65
+ const { computePlanDigest } = await import("../../lifecycle/plan-digest");
66
+ const { runComponentsFanOut } = await import("./fan-out");
67
+ const { runOpComponents } = await import("./run");
68
+
69
+ interface StackInput {
70
+ stack: string;
71
+ vars?: Record<string, unknown>;
72
+ }
73
+
74
+ const so = (stack: string, name: string) => ({ stackOutput: { stack, name } });
75
+ const stack = (name: string, dependsOn: string[] = [], vars?: Record<string, unknown>): Component => ({
76
+ name,
77
+ dependsOn,
78
+ deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", stack: name, ...(vars ? { vars } : {}) }] }],
79
+ });
80
+ /** a; b reads a's new id; c reads b's. */
81
+ const ABC = [stack("a"), stack("b", ["a"], { upstream: so("a", "id") }), stack("c", ["b"], { upstream: so("b", "id") })];
82
+
83
+ let group = true;
84
+ let planned: string[] = [];
85
+ let applied: string[] = [];
86
+ let planGroupCalls: string[][] = [];
87
+ let runGroupCalls: string[][] = [];
88
+ let stderrLines: string[] = [];
89
+
90
+ function registry() {
91
+ const r = new CapabilityRegistry();
92
+ const planOf = (input: StackInput) => {
93
+ const planDigest = computePlanDigest("cfn-deploy", { stack: input.stack, vars: input.vars ?? null });
94
+ return {
95
+ member: input.stack,
96
+ planDigest,
97
+ artifact: { planDigest },
98
+ outputs: { known: {}, unknown: ["id"] },
99
+ changeSet: {
100
+ member: { member: input.stack, lexicon: "aws", planner: "tofu" as const, status: "planned" as const, planDigest, holes: [] },
101
+ entries: [{ member: input.stack, lexicon: "aws", planner: "tofu" as const, address: "res.x", type: "res", action: "create" as const, attributes: [] }],
102
+ },
103
+ };
104
+ };
105
+ r.register({
106
+ kind: "cfn-deploy",
107
+ async plan(_ctx: unknown, input: StackInput) {
108
+ planned.push(input.stack);
109
+ return planOf(input);
110
+ },
111
+ async run(_ctx: unknown, input: StackInput) {
112
+ applied.push(input.stack);
113
+ return { outputs: { id: `${input.stack}-id` } };
114
+ },
115
+ ...(group
116
+ ? {
117
+ async planGroup(_ctx: unknown, members: ReadonlyArray<{ component: string; input?: StackInput }>) {
118
+ planGroupCalls.push(members.map((m) => m.component));
119
+ return {
120
+ members: Object.fromEntries(members.map((m) => [m.component, planOf(m.input!)])),
121
+ digest: computePlanDigest("fake-composition", members.map((m) => m.component)),
122
+ artifact: { composed: members.map((m) => m.component) },
123
+ };
124
+ },
125
+ async runGroup(_ctx: unknown, _run: unknown, members: ReadonlyArray<{ component: string }>) {
126
+ runGroupCalls.push(members.map((m) => m.component));
127
+ return { members: Object.fromEntries(members.map((m) => [m.component, { status: "applied" as const, outputs: { id: `${m.component}-id` } }])) };
128
+ },
129
+ }
130
+ : {}),
131
+ } as never);
132
+ return r;
133
+ }
134
+
135
+ const baseArgs = { format: "", fix: false, watch: false, verbose: false, help: false, live: false };
136
+ const fanOutCtx = (overrides: Partial<ParsedArgs> = {}) => ({ args: { command: "components", path: "fan-out", ...baseArgs, ...overrides } as ParsedArgs, plugins: [], serializers: [] }) as never;
137
+ const runCtx = (selector: string) => ({ args: { command: "run", path: selector, components: true, on: "local", ...baseArgs } as ParsedArgs, plugins: [] }) as never;
138
+ const stderr = () => stderrLines.join("\n");
139
+
140
+ beforeEach(() => {
141
+ vi.clearAllMocks();
142
+ group = true;
143
+ planned = [];
144
+ applied = [];
145
+ planGroupCalls = [];
146
+ runGroupCalls = [];
147
+ stderrLines = [];
148
+ discoverComponentsMock.mockResolvedValue({
149
+ components: new Map(ABC.map((c) => [c.name, { component: c, exportName: c.name, filePath: `${c.name}.component.ts` }])),
150
+ sourceFiles: [],
151
+ errors: [],
152
+ });
153
+ buildCapabilityRegistryMock.mockImplementation(async () => registry());
154
+ affectedStacksMock.mockResolvedValue({ changed: ["a"], dependents: [], indeterminate: [] });
155
+ vi.spyOn(console, "error").mockImplementation((...args: unknown[]) => {
156
+ stderrLines.push(args.map(String).join(" "));
157
+ });
158
+ vi.spyOn(console, "log").mockImplementation(() => {});
159
+ vi.spyOn(process.stderr, "write").mockImplementation((chunk: unknown) => {
160
+ stderrLines.push(String(chunk));
161
+ return true;
162
+ });
163
+ vi.spyOn(process.stdout, "write").mockImplementation(() => true);
164
+ });
165
+
166
+ afterEach(() => {
167
+ vi.restoreAllMocks();
168
+ });
169
+
170
+ describe("chant components fan-out forms groups (#4465, chant#4505)", () => {
171
+ test("a changed a pulls in b and c; the three plan with one planGroup and apply with one runGroup, and the report names the group", async () => {
172
+ const exit = await runComponentsFanOut(fanOutCtx({ base: "main" }));
173
+ expect(exit).toBe(0);
174
+ expect(planGroupCalls).toEqual([["a", "b", "c"]]);
175
+ expect(runGroupCalls).toEqual([["a", "b", "c"]]);
176
+ // No member ran its own deploy.
177
+ expect(applied).toEqual([]);
178
+ expect(stderr()).toContain("a, b and c plan and apply as one (cfn-deploy, wave 1");
179
+ expect(stderr()).toContain("a applied; b applied; c applied");
180
+ });
181
+
182
+ test("without the group pair each component applies by itself, as before", async () => {
183
+ group = false;
184
+ const exit = await runComponentsFanOut(fanOutCtx({ base: "main" }));
185
+ expect(exit).toBe(0);
186
+ expect(planGroupCalls).toEqual([]);
187
+ expect(runGroupCalls).toEqual([]);
188
+ expect(applied).toEqual(["a", "b", "c"]);
189
+ expect(stderr()).not.toContain("plan and apply as one");
190
+ });
191
+ });
192
+
193
+ describe("chant run --components forms groups (#4465, chant#4505)", () => {
194
+ test("all: one planGroup and one runGroup over a, b and c, and no member's own deploy", async () => {
195
+ expect(await runOpComponents(runCtx("all"))).toBe(0);
196
+ expect(planGroupCalls).toEqual([["a", "b", "c"]]);
197
+ expect(runGroupCalls).toEqual([["a", "b", "c"]]);
198
+ expect(applied).toEqual([]);
199
+ });
200
+
201
+ test("a list that pulls in its dependency groups the whole set the run resolved", async () => {
202
+ expect(await runOpComponents(runCtx("b,c"))).toBe(0);
203
+ expect(planGroupCalls).toEqual([["a", "b", "c"]]);
204
+ expect(runGroupCalls).toEqual([["a", "b", "c"]]);
205
+ expect(applied).toEqual([]);
206
+ });
207
+
208
+ test("a dependency outside the discovered set fails as it does without groups, before any group plans (round 1's red)", async () => {
209
+ const withUnknown = [...ABC, stack("d", ["missing"])];
210
+ discoverComponentsMock.mockResolvedValue({
211
+ components: new Map(withUnknown.map((c) => [c.name, { component: c, exportName: c.name, filePath: `${c.name}.component.ts` }])),
212
+ sourceFiles: [],
213
+ errors: [],
214
+ });
215
+ const exit = await runOpComponents(runCtx("all")).catch(() => 1);
216
+ expect(exit).not.toBe(0);
217
+ expect(stderr()).toContain("missing");
218
+ expect(planGroupCalls).toEqual([]);
219
+ expect(runGroupCalls).toEqual([]);
220
+ expect(applied).toEqual([]);
221
+ });
222
+
223
+ test("without the group pair each component applies by itself, in order", async () => {
224
+ group = false;
225
+ expect(await runOpComponents(runCtx("all"))).toBe(0);
226
+ expect(planGroupCalls).toEqual([]);
227
+ expect(applied).toEqual(["a", "b", "c"]);
228
+ });
229
+ });
@@ -24,6 +24,19 @@ vi.mock("../../lifecycle/git", () => ({
24
24
  fetchLifecycle: () => fetchLifecycleMock(),
25
25
  readEnvironmentSnapshots: (...args: unknown[]) => readEnvironmentSnapshotsMock(...args),
26
26
  }));
27
+ // `snapshot(env)` reads the index store (#4519); this one hands the read to the mocks above.
28
+ vi.mock("../../lifecycle/snapshot-store", async () => {
29
+ const { fakeSnapshotStore } = await import("../../lifecycle/snapshot-store-fake");
30
+ const store = fakeSnapshotStore({
31
+ fetch: async () => {
32
+ await fetchLifecycleMock();
33
+ },
34
+ });
35
+ return {
36
+ resolveSnapshotStore: async () => store,
37
+ readEnvironmentSnapshotsFrom: async (_store: unknown, environment: string) => readEnvironmentSnapshotsMock(environment),
38
+ };
39
+ });
27
40
  vi.mock("../../config", async () => {
28
41
  const actual = await vi.importActual<typeof import("../../config")>("../../config");
29
42
  return { ...actual, loadChantConfig: (...args: unknown[]) => loadChantConfigMock(...args) };
@@ -2,7 +2,7 @@ import { resolve } from "node:path";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { commandBuildParams } from "../build-params-cli";
4
4
  import { build, type BuildResult } from "../../build";
5
- import { readEnvironmentSnapshots, fetchLifecycle } from "../../lifecycle/git";
5
+ import { readEnvironmentSnapshotsFrom, resolveSnapshotStore } from "../../lifecycle/snapshot-store";
6
6
  import { buildChangeSet, type ChangeSet, type ChangeSetEntry } from "../../lifecycle/change-set";
7
7
  import {
8
8
  planReceipts,
@@ -88,11 +88,11 @@ export async function runScenarioCheck(ctx: CommandContext): Promise<number> {
88
88
  // of any one scenario's fixture.
89
89
  const receipts = collectEffectReceipts(buildResult.entities);
90
90
 
91
- // `snapshot(env)` reads the chant/lifecycle orphan branch — fetch once,
92
- // up front, the same pre-read `runLifecyclePlan` does, rather than once per
93
- // scenario that needs it.
91
+ // `snapshot(env)` reads the index store (#4519) — fetch once, up front,
92
+ // the same pre-read `runLifecyclePlan` does, rather than once per scenario
93
+ // that needs it.
94
94
  if ([...scenarios.values()].some((s) => s.given.kind === "env")) {
95
- await fetchLifecycle();
95
+ await (await resolveSnapshotStore()).fetch();
96
96
  }
97
97
 
98
98
  const results: Array<{ entityName: string; scenario: ScenarioDeclaration; verdict: ScenarioVerdict; env: string }> = [];
@@ -234,9 +234,10 @@ async function resolveGiven(given: ScenarioGiven): Promise<GivenResolution> {
234
234
  };
235
235
  }
236
236
 
237
- const stored = await readEnvironmentSnapshots(given.env);
237
+ const store = await resolveSnapshotStore();
238
+ const stored = await readEnvironmentSnapshotsFrom(store, given.env);
238
239
  if (stored.size === 0) {
239
- const error = `no recorded snapshot for environment "${given.env}" on chant/lifecycle — record one with \`chant lifecycle snapshot ${given.env}\``;
240
+ const error = `no recorded snapshot for environment "${given.env}" in ${store.where} — record one with \`chant lifecycle snapshot ${given.env}\`, or name a checked-in fixture`;
240
241
  return { env: given.env, perLexicon: new Map(), error, behaviour: { missing: error } };
241
242
  }
242
243
  const perLexicon = new Map<string, LifecycleSnapshot>();
@@ -0,0 +1,255 @@
1
+ import { describe, test, expect, vi, beforeEach } from "vitest";
2
+ import type { ParsedArgs } from "../registry";
3
+ import { DECLARABLE_MARKER, type Declarable } from "../../declarable";
4
+ import { createMockPlugin } from "../../../../test-utils/src/mock-plugin";
5
+ import { NO_LEXICON_DETECTED_MESSAGE } from "../../detectLexicon";
6
+ import type { DeclaredIdentity, IndexIdentity } from "../../lifecycle/identity-match";
7
+ import type { IndexDocument } from "../../lifecycle/index-document";
8
+ import { typeListing, type ListedResource, type ListingResult } from "../../lifecycle/type-listing";
9
+ import { fakeSnapshotStore } from "../../lifecycle/snapshot-store-fake";
10
+
11
+ // #4525: `chant search --index <ref>` answers from an index version in the
12
+ // store: the declared rows joined to it, each with its verdict, or the index
13
+ // alone with no project. No cloud is read; the store is a fake holding one
14
+ // fixture version typed the way behold#1026 writes one.
15
+
16
+ const discoverMock = vi.fn();
17
+ vi.mock("../../discovery/index", () => ({
18
+ discover: (...a: unknown[]) => discoverMock(...a),
19
+ }));
20
+ const loadPluginsMock = vi.fn();
21
+ const resolveLexMock = vi.fn();
22
+ vi.mock("../plugins", async () => {
23
+ const actual = await vi.importActual<typeof import("../plugins")>("../plugins");
24
+ return {
25
+ ...actual,
26
+ loadPlugins: (...a: unknown[]) => loadPluginsMock(...a),
27
+ resolveProjectLexicons: (...a: unknown[]) => resolveLexMock(...a),
28
+ };
29
+ });
30
+ const loadChantConfigMock = vi.fn();
31
+ vi.mock("../../config", async () => {
32
+ const actual = await vi.importActual<typeof import("../../config")>("../../config");
33
+ return { ...actual, loadChantConfig: (...a: unknown[]) => loadChantConfigMock(...a) };
34
+ });
35
+ const storeMock = vi.fn();
36
+ vi.mock("../../lifecycle/snapshot-store", async () => {
37
+ const actual = await vi.importActual<typeof import("../../lifecycle/snapshot-store")>("../../lifecycle/snapshot-store");
38
+ return { ...actual, resolveSnapshotStore: (...a: unknown[]) => Promise.resolve(storeMock(...a)) };
39
+ });
40
+ const liveRead = vi.fn();
41
+
42
+ import searchSchema from "../../workspace/search.schema.json";
43
+ import { contract } from "../../workspace/__fixtures__/contract-repo";
44
+ const { expectValid } = contract(searchSchema);
45
+ const { runSearch } = await import("./search");
46
+
47
+ const PARTS: Record<string, string[]> = { "Store::Bucket": ["BucketName"] };
48
+ const identity: IndexIdentity = {
49
+ substrate: "aws",
50
+ declaredIdentity(entityType, props): DeclaredIdentity {
51
+ const parts = PARTS[entityType];
52
+ if (entityType === "Queue::Queue") return { kind: "unmatchable", reason: "no-binding", detail: "the queue URL is assigned by the service" };
53
+ if (!parts) return { kind: "unmatchable", reason: "unsupported-kind", detail: `${entityType} has no primary identifier on record` };
54
+ const v = props[parts[0]];
55
+ if (typeof v !== "string") return { kind: "unmatchable", reason: "no-binding", detail: "identity is not in the declaration" };
56
+ return { kind: "identifier", listedType: entityType, identifier: v, global: false };
57
+ },
58
+ };
59
+
60
+ const r = (listedType: string, nativeId: string, properties: Record<string, unknown>, tags: Record<string, string> = {}): ListedResource => ({
61
+ listedType,
62
+ location: "us-east-1",
63
+ nativeId,
64
+ cloudIds: [{ kind: "import-id", value: nativeId }],
65
+ tags,
66
+ properties,
67
+ observedAt: "2026-10-26T09:00:01.000Z",
68
+ });
69
+ const listing: ListingResult = {
70
+ listing: "v1",
71
+ source: { name: "behold-lister", version: "0.0.0-test" },
72
+ scope: { substrate: "aws", scopeId: "000000000000", locations: ["us-east-1"], types: ["Store::Bucket", "Queue::Queue"] },
73
+ startedAt: "2026-10-26T09:00:00.000Z",
74
+ completedAt: "2026-10-26T09:00:04.000Z",
75
+ resources: [
76
+ r("Store::Bucket", "shop-assets", { BucketName: "shop-assets", Arn: "arn:aws:s3:::shop-assets" }, { team: "web" }),
77
+ r("Store::Bucket", "made-by-hand", { BucketName: "made-by-hand" }, { team: "data" }),
78
+ r("Queue::Queue", "https://sqs.us-east-1.amazonaws.com/000000000000/orders", { QueueName: "orders" }),
79
+ ],
80
+ coverage: [
81
+ { listedType: "Store::Bucket", location: "us-east-1", verdict: "listed" },
82
+ { listedType: "Queue::Queue", location: "us-east-1", verdict: "listed" },
83
+ ],
84
+ notes: [],
85
+ };
86
+ const doc: IndexDocument = typeListing(listing, [{ name: "fake", listedTypes: { substrate: "aws", kindFor: (t) => t } }]);
87
+ const KEY = doc.stamp.scopeKey;
88
+ const indexStore = fakeSnapshotStore({
89
+ readIndexLatest: async (k) => (k === KEY ? doc : null),
90
+ readIndexAt: async () => null,
91
+ });
92
+
93
+ function decl<T extends object>(base: T): Declarable & T {
94
+ return { [DECLARABLE_MARKER]: true, ...base } as Declarable & T;
95
+ }
96
+
97
+ function makeArgs(overrides: Partial<ParsedArgs> = {}): ParsedArgs {
98
+ return {
99
+ command: "search", path: "kind:Bucket",
100
+ format: "", fix: false, watch: false, verbose: false, help: false, live: false, index: KEY,
101
+ ...overrides,
102
+ };
103
+ }
104
+
105
+ const plugin = { ...createMockPlugin({ name: "fake", describeResources: async (...a: unknown[]) => liveRead(...a) }), indexIdentity: identity };
106
+
107
+ let out: string[];
108
+ let err: string[];
109
+ beforeEach(() => {
110
+ out = [];
111
+ err = [];
112
+ vi.spyOn(console, "log").mockImplementation((s: string) => { out.push(s); });
113
+ vi.spyOn(console, "error").mockImplementation((s: string) => { err.push(s); });
114
+ discoverMock.mockReset();
115
+ loadPluginsMock.mockReset();
116
+ resolveLexMock.mockReset();
117
+ loadChantConfigMock.mockReset();
118
+ storeMock.mockReset();
119
+ storeMock.mockReturnValue(indexStore);
120
+ liveRead.mockReset();
121
+ });
122
+
123
+ describe("search --index with no project", () => {
124
+ beforeEach(() => {
125
+ loadChantConfigMock.mockResolvedValue({ config: {} });
126
+ resolveLexMock.mockRejectedValue(new Error(NO_LEXICON_DETECTED_MESSAGE));
127
+ loadPluginsMock.mockResolvedValue([]);
128
+ });
129
+
130
+ test("answers from the index alone, each row an indexed node, with the stamp on the provenance line", async () => {
131
+ expect(await runSearch({ args: makeArgs(), plugins: [], serializers: [] })).toBe(0);
132
+ const stdout = out.join("\n");
133
+ expect(stdout).toContain("aws/000000000000/us-east-1/Store::Bucket/made-by-hand Store::Bucket made-by-hand");
134
+ expect(stdout).toContain("aws/000000000000/us-east-1/Store::Bucket/shop-assets Store::Bucket shop-assets");
135
+ expect(stdout).not.toContain("Queue::Queue");
136
+ expect(stdout).toMatch(/— index only, no project · cloud\/aws\/000000000000\/[0-9a-f]{12}@20261026T090000Z-[0-9a-f]{12} taken 2026-10-26T09:00:00.000Z by behold-lister 0.0.0-test · 2 rows/);
137
+ expect(discoverMock).not.toHaveBeenCalled();
138
+ });
139
+
140
+ test("a tag: term reads an indexed node's tag map", async () => {
141
+ expect(await runSearch({ args: makeArgs({ path: "tag:team=data" }), plugins: [], serializers: [] })).toBe(0);
142
+ const stdout = out.join("\n");
143
+ expect(stdout).toContain("made-by-hand");
144
+ expect(stdout).not.toContain("shop-assets ");
145
+ });
146
+
147
+ test("--json is a search document whose source is the index version", async () => {
148
+ expect(await runSearch({ args: makeArgs({ json: true }), plugins: [], serializers: [] })).toBe(0);
149
+ const json = JSON.parse(out.join(""));
150
+ expectValid(json);
151
+ expect(json.source).toMatchObject({ kind: "index", scopeKey: KEY, observedAt: "2026-10-26T09:00:00.000Z", digest: doc.stamp.digest, project: false });
152
+ expect(json.matches.map((m: { id: string }) => m.id)).toEqual([
153
+ "aws/000000000000/us-east-1/Store::Bucket/made-by-hand",
154
+ "aws/000000000000/us-east-1/Store::Bucket/shop-assets",
155
+ ]);
156
+ expect(json.matches[0].displayId).toBe(json.matches[0].id);
157
+ expect(json.matches[0].join).toBeUndefined();
158
+ });
159
+
160
+ test("an empty answer still names the version it read", async () => {
161
+ expect(await runSearch({ args: makeArgs({ path: "kind:Bucket attr:BucketName=nothing-like-this" }), plugins: [], serializers: [] })).toBe(0);
162
+ const stdout = out.join("\n");
163
+ expect(stdout).toContain("(no matches)");
164
+ expect(stdout).toContain("— index only, no project");
165
+ });
166
+ });
167
+
168
+ describe("search --index with a project", () => {
169
+ const east = new Map<string, Declarable>([
170
+ ["assets", decl({ lexicon: "fake", entityType: "Store::Bucket", props: { BucketName: "shop-assets" } })],
171
+ ["archive", decl({ lexicon: "fake", entityType: "Store::Bucket", props: { BucketName: "never-made" } })],
172
+ ["orders", decl({ lexicon: "fake", entityType: "Queue::Queue", props: { QueueName: "orders" } })],
173
+ ]);
174
+
175
+ beforeEach(() => {
176
+ loadChantConfigMock.mockResolvedValue({ config: { stacks: [{ name: "east", region: "us-east-1", src: "east" }] } });
177
+ resolveLexMock.mockResolvedValue(["fake"]);
178
+ loadPluginsMock.mockResolvedValue([plugin]);
179
+ discoverMock.mockResolvedValue({ entities: east, errors: [], sourceFiles: [] });
180
+ });
181
+
182
+ test("declared rows join through the lexicon's index match, each row showing its verdict, and nothing reads live", async () => {
183
+ expect(await runSearch({ args: makeArgs(), plugins: [], serializers: [] })).toBe(0);
184
+ const stdout = out.join("\n");
185
+ expect(stdout).toContain("assets Store::Bucket shop-assets");
186
+ expect(stdout).toContain("archive Store::Bucket (absent)");
187
+ // An indexed bucket no declaration binds is not a declared row.
188
+ expect(stdout).not.toContain("made-by-hand");
189
+ expect(stdout).toMatch(/— observed from index cloud\/aws\/000000000000\/[0-9a-f]{12}@20261026T090000Z-[0-9a-f]{12} taken 2026-10-26T09:00:00.000Z · bound 1\/2 · absent 1/);
190
+ expect(liveRead).not.toHaveBeenCalled();
191
+ });
192
+
193
+ test("a declaration with no identity to match is no-binding, never absent", async () => {
194
+ expect(await runSearch({ args: makeArgs({ path: "kind:Queue" }), plugins: [], serializers: [] })).toBe(0);
195
+ const stdout = out.join("\n");
196
+ expect(stdout).toContain("orders Queue::Queue (no-binding)");
197
+ expect(stdout).toContain("bound 0/1 · no-binding 1");
198
+ });
199
+
200
+ test("--json carries each row's join and validates against the search schema", async () => {
201
+ expect(await runSearch({ args: makeArgs({ path: "kind:Store::Bucket", json: true, show: "Arn" }), plugins: [], serializers: [] })).toBe(0);
202
+ const json = JSON.parse(out.join(""));
203
+ expectValid(json);
204
+ expect(json.source).toMatchObject({ kind: "index", project: true });
205
+ const byId = Object.fromEntries(json.matches.map((m: { id: string }) => [m.id, m]));
206
+ expect(byId["east::assets"]).toMatchObject({
207
+ physicalId: "shop-assets",
208
+ show: { Arn: "arn:aws:s3:::shop-assets" },
209
+ join: { verdict: "bound", node: "aws/000000000000/us-east-1/Store::Bucket/shop-assets", nativeId: "shop-assets" },
210
+ });
211
+ expect(byId["east::archive"]).toMatchObject({ join: { verdict: "absent", identifier: "never-made", location: "us-east-1" } });
212
+ expect(byId["east::archive"].physicalId).toBeUndefined();
213
+ expect(json.provenance).toContain("bound 1/2 · absent 1");
214
+ });
215
+ });
216
+
217
+ describe("search --index refusals", () => {
218
+ beforeEach(() => {
219
+ loadChantConfigMock.mockResolvedValue({ config: {} });
220
+ resolveLexMock.mockRejectedValue(new Error(NO_LEXICON_DETECTED_MESSAGE));
221
+ loadPluginsMock.mockResolvedValue([]);
222
+ });
223
+
224
+ test("--index with --live, --snapshot or --ambient is refused before anything is read", async () => {
225
+ for (const extra of [{ live: true, env: "dev" }, { snapshot: "latest", env: "dev" }, { ambient: true }] as Partial<ParsedArgs>[]) {
226
+ out = [];
227
+ expect(await runSearch({ args: makeArgs({ ...extra, json: true }), plugins: [], serializers: [] })).toBe(1);
228
+ const json = JSON.parse(out.join(""));
229
+ expectValid(json);
230
+ expect(json.error.code).toBe("usage-invalid");
231
+ expect(json.error.message).toContain("--index or");
232
+ }
233
+ expect(storeMock).not.toHaveBeenCalled();
234
+ });
235
+
236
+ test("a ref that is not a scope key is usage-invalid, with the shape in the hint", async () => {
237
+ expect(await runSearch({ args: makeArgs({ index: "my-account", json: true }), plugins: [], serializers: [] })).toBe(1);
238
+ const json = JSON.parse(out.join(""));
239
+ expectValid(json);
240
+ expect(json.error).toMatchObject({ code: "usage-invalid", hint: expect.stringContaining("scope key") });
241
+ });
242
+
243
+ test("a scope key the store holds nothing under is index-unavailable", async () => {
244
+ expect(await runSearch({ args: makeArgs({ index: "cloud/aws/111111111111/0123456789ab", json: true }), plugins: [], serializers: [] })).toBe(1);
245
+ const json = JSON.parse(out.join(""));
246
+ expectValid(json);
247
+ expect(json.error.code).toBe("index-unavailable");
248
+ expect(json.error.message).toContain("cloud/aws/111111111111/0123456789ab");
249
+ });
250
+
251
+ test("a version the store does not hold is index-unavailable", async () => {
252
+ expect(await runSearch({ args: makeArgs({ index: `${KEY}@20261026T090000Z-ffffffffffff` }), plugins: [], serializers: [] })).toBe(1);
253
+ expect(err.join("\n")).toContain("no index version 20261026T090000Z-ffffffffffff");
254
+ });
255
+ });
@@ -229,7 +229,7 @@ describe("search --json refusals are a document with error, exit 1 (#4367)", ()
229
229
  error: {
230
230
  code: "query-invalid",
231
231
  message: "\"effectiveIngress:tcp:22\" is not a term — there is no \"effectiveIngress:\" prefix",
232
- hint: "for an attribute, say attr:effectiveIngress=tcp:22; the prefixes are kind:, attr:, tag:, and ->/<- for edges",
232
+ hint: "for an attribute, say attr:effectiveIngress=tcp:22; the prefixes are kind:, attr:, tag:, id:, and ->/<- for edges",
233
233
  },
234
234
  });
235
235
  expect(err.join("\n")).toContain("is not a term");