@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,248 @@
1
+ /**
2
+ * `chant search --index <ref>` (#4525): an index version as the observed side
3
+ * of a search, joined to the declared rows.
4
+ *
5
+ * Two halves, both pure apart from {@link readIndexRef}'s store read:
6
+ *
7
+ * - a ref names one index version in the store (#4519, ./index-document.ts):
8
+ * `<scope key>`, `<scope key>@latest`, `<scope key>@<version>` or
9
+ * `<scope key>/<version>.json`, where the scope key is `stamp.scopeKey`
10
+ * (`cloud/aws/<account>/<scope digest>`) and a version is a name the store
11
+ * lists (`20261026T091403Z-<digest12>`, with or without `.json`);
12
+ * - {@link joinDeclaredToIndex} gives every declared node a join verdict
13
+ * and lays the bound indexed nodes over the declared canvas, the way
14
+ * `--live` lays a live read over it (`sourceOverlayGraphs`).
15
+ *
16
+ * The join goes through each lexicon's own index match, its plugin's
17
+ * `indexIdentity` (#4523, ./identity-match.ts). aws has one; a lexicon with
18
+ * none gets `not-observed`, reason `unsupported-kind`, on every row, never a
19
+ * guessed match. The verdicts:
20
+ *
21
+ * - `bound`: one indexed node carries the identity the declaration spells;
22
+ * - `absent`: none does, and the index's coverage says it listed that type
23
+ * and location;
24
+ * - `not-observed`: the index did not look (coverage `filtered`,
25
+ * `not-reached`, a read failure), the lexicon has no index match, or the
26
+ * index is of another substrate. Always with the reason and a detail;
27
+ * - `no-binding`: the declaration does not spell an identity (a
28
+ * server-assigned id, a Ref), or the entity names no region and nothing
29
+ * matched;
30
+ * - `ambiguous`: two or more indexed nodes carry the identity. None is bound.
31
+ *
32
+ * Indexed nodes no declared row binds stay out of a project's answer, as an
33
+ * undeclared resource stays out of a `--live` one; with no project the index
34
+ * is the whole graph ({@link indexOnlyGraph}).
35
+ */
36
+ import { sourceOverlayGraphs } from "../graph-ir.js";
37
+ import { matchDeclaredToIndex } from "./identity-match.js";
38
+ import { indexVersionName, isIndexVersionName, validateIndexDocument } from "./index-document.js";
39
+ const SOURCE_PREFIXES = ["cloud/", "engine/", "chant/"];
40
+ const VERSION_ID = /^\d{8}T\d{6}Z-[0-9a-f]{12}(\.json)?$/;
41
+ const REF_HINT = "name an index version as <scope key>[@latest|@<version>], where the scope key is the index's stamp.scopeKey (cloud/aws/<account>/<scope digest>) and a version is a name the store lists, such as 20261026T091403Z-0123456789ab";
42
+ /** Parse `--index <ref>`, or say why it is not one. */
43
+ export function parseIndexRef(ref) {
44
+ const text = ref.trim();
45
+ if (text === "")
46
+ return { error: "--index needs an index ref", hint: REF_HINT };
47
+ let scopeKey = text;
48
+ let version = "latest";
49
+ const at = text.lastIndexOf("@");
50
+ if (at >= 0) {
51
+ scopeKey = text.slice(0, at);
52
+ version = text.slice(at + 1);
53
+ if (version !== "latest" && !VERSION_ID.test(version)) {
54
+ return { error: `--index ${text}: "${version}" is not an index version`, hint: REF_HINT };
55
+ }
56
+ }
57
+ else {
58
+ const slash = text.lastIndexOf("/");
59
+ const last = text.slice(slash + 1);
60
+ if (slash > 0 && last.endsWith(".json") && VERSION_ID.test(last)) {
61
+ scopeKey = text.slice(0, slash);
62
+ version = last;
63
+ }
64
+ }
65
+ if (version !== "latest" && !version.endsWith(".json"))
66
+ version = `${version}.json`;
67
+ if (!SOURCE_PREFIXES.some((p) => scopeKey.startsWith(p)) || scopeKey.split("/").some((s) => s === "" || s === "." || s === "..")) {
68
+ return { error: `--index ${text}: "${scopeKey}" is not an index scope key`, hint: REF_HINT };
69
+ }
70
+ return { scopeKey, version };
71
+ }
72
+ /** The ref as one string, version resolved: `<scope key>@<version name without .json>`. */
73
+ export function formatIndexRef(scopeKey, versionName) {
74
+ return `${scopeKey}@${versionName.replace(/\.json$/, "")}`;
75
+ }
76
+ /**
77
+ * Read the version a ref names. A stamp that disagrees with itself or with
78
+ * the key it was read under is refused: the answer would be about another
79
+ * scope than the one asked for.
80
+ */
81
+ export async function readIndexRef(store, ref) {
82
+ let doc;
83
+ try {
84
+ doc = ref.version === "latest" ? await store.readIndexLatest(ref.scopeKey) : await store.readIndexAt(ref.scopeKey, ref.version);
85
+ }
86
+ catch (err) {
87
+ return { error: `could not read the index ${ref.scopeKey} from ${store.where}: ${err instanceof Error ? err.message : String(err)}` };
88
+ }
89
+ if (!doc) {
90
+ return {
91
+ error: ref.version === "latest"
92
+ ? `no index version is recorded under ${ref.scopeKey} in ${store.where}`
93
+ : `no index version ${ref.version.replace(/\.json$/, "")} is recorded under ${ref.scopeKey} in ${store.where}`,
94
+ hint: "a scope key ends in its 12-hex scope digest, as the index's stamp.scopeKey spells it; the store is the project's `snapshots` store, or CHANT_SNAPSHOT_STORE and its companions",
95
+ };
96
+ }
97
+ try {
98
+ validateIndexDocument(doc);
99
+ }
100
+ catch (err) {
101
+ return { error: `the index at ${ref.scopeKey} is not one chant can read: ${err instanceof Error ? err.message : String(err)}` };
102
+ }
103
+ if (doc.stamp.scopeKey !== ref.scopeKey) {
104
+ return { error: `the index read under ${ref.scopeKey} is stamped ${doc.stamp.scopeKey}, so it is not the scope asked for` };
105
+ }
106
+ const version = indexVersionName(doc.stamp.observedAt, doc.stamp.digest);
107
+ if (ref.version !== "latest" && isIndexVersionName(ref.version) && ref.version !== version) {
108
+ return { error: `the index version ${ref.version} under ${ref.scopeKey} is stamped as ${version}, so it is not the version asked for` };
109
+ }
110
+ return { doc, ref, version };
111
+ }
112
+ /** Every verdict, in the order a summary lists them. */
113
+ export const INDEX_JOIN_VERDICTS = ["bound", "absent", "not-observed", "no-binding", "ambiguous"];
114
+ function fromMatch(m) {
115
+ const base = {
116
+ ...("listedType" in m && m.listedType !== undefined ? { listedType: m.listedType } : {}),
117
+ ...("identifier" in m && m.identifier !== undefined ? { identifier: m.identifier } : {}),
118
+ ...("location" in m && m.location !== undefined && m.location !== "" ? { location: m.location } : {}),
119
+ };
120
+ switch (m.verdict) {
121
+ case "bound":
122
+ return { ...base, verdict: "bound", node: m.node, nativeId: m.nativeId };
123
+ case "absent":
124
+ return { ...base, verdict: "absent" };
125
+ case "refused":
126
+ return { ...base, verdict: "ambiguous", candidates: m.candidates, detail: m.detail };
127
+ case "not-observed":
128
+ return m.reason === "no-binding"
129
+ ? { ...base, verdict: "no-binding", detail: m.detail }
130
+ : { ...base, verdict: "not-observed", reason: m.reason, detail: m.detail };
131
+ }
132
+ }
133
+ /**
134
+ * Join every declared node to an index version. Pure: the lexicons come in
135
+ * as data. The canvas keeps every declared node and edge; a bound node takes
136
+ * the indexed node's physical id, attributes and facts, as `--live` gives a
137
+ * node its read's.
138
+ */
139
+ export function joinDeclaredToIndex(declared, doc, lexicons, options = {}) {
140
+ const joins = {};
141
+ const scope = doc.stamp.scope;
142
+ const byLexicon = new Map();
143
+ for (const n of declared.nodes)
144
+ (byLexicon.get(n.lexicon) ?? byLexicon.set(n.lexicon, []).get(n.lexicon)).push(n);
145
+ for (const [lexicon, nodes] of byLexicon) {
146
+ const identity = lexicons.find((l) => l.name === lexicon)?.indexIdentity;
147
+ if (!identity) {
148
+ for (const n of nodes) {
149
+ joins[n.id] = {
150
+ verdict: "not-observed",
151
+ reason: "unsupported-kind",
152
+ detail: `the ${lexicon} lexicon has no index match, so a declared ${n.kind} is not joined to an indexed resource`,
153
+ };
154
+ }
155
+ continue;
156
+ }
157
+ if (scope.kind !== "cloud") {
158
+ for (const n of nodes) {
159
+ joins[n.id] = {
160
+ verdict: "not-observed",
161
+ reason: "filtered",
162
+ detail: `the index ${doc.stamp.scopeKey} is a ${scope.kind} source (${doc.stamp.source.name}); declared rows join only to a cloud read`,
163
+ };
164
+ }
165
+ continue;
166
+ }
167
+ if (scope.substrate !== identity.substrate) {
168
+ for (const n of nodes) {
169
+ joins[n.id] = {
170
+ verdict: "not-observed",
171
+ reason: "filtered",
172
+ detail: `the index ${doc.stamp.scopeKey} reads ${scope.substrate}, and a ${lexicon} entity lives in ${identity.substrate}`,
173
+ };
174
+ }
175
+ continue;
176
+ }
177
+ const entities = nodes.map((n) => {
178
+ const region = options.regionOf?.(n.id);
179
+ return { name: n.id, entityType: n.kind, props: (n.attrs ?? {}), ...(region ? { region } : {}) };
180
+ });
181
+ const { matches } = matchDeclaredToIndex(entities, [doc], identity);
182
+ for (const [id, m] of Object.entries(matches))
183
+ joins[id] = fromMatch(m);
184
+ }
185
+ // The bound indexed nodes, renamed to the declared node they bind, are the
186
+ // observed side of the overlay. Nothing else of the index is.
187
+ const indexed = new Map(doc.graph.nodes.map((n) => [n.id, n]));
188
+ const live = [];
189
+ const unobserved = {};
190
+ for (const n of declared.nodes) {
191
+ const j = joins[n.id];
192
+ if (!j)
193
+ continue;
194
+ if (j.verdict === "bound") {
195
+ const hit = indexed.get(j.node);
196
+ if (hit)
197
+ live.push({ ...hit, id: n.id, physicalId: hit.physicalId ?? j.nativeId });
198
+ }
199
+ else if (j.verdict === "not-observed" || j.verdict === "no-binding") {
200
+ unobserved[n.id] = { type: n.kind, reason: j.verdict === "no-binding" ? "no-binding" : j.reason, detail: j.detail };
201
+ }
202
+ }
203
+ live.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
204
+ let ir = sourceOverlayGraphs(declared, { ...declared, nodes: live, edges: [], exports: [] }, { unobserved });
205
+ // An ambiguous row is neither pending nor unread: the index holds candidates
206
+ // and chant will not pick one. Paint it as unknown, with no `_unobserved`.
207
+ ir = {
208
+ ...ir,
209
+ nodes: ir.nodes.map((n) => (joins[n.id]?.verdict === "ambiguous" ? { ...n, attrs: { ...n.attrs, _status: "neutral" } } : n)),
210
+ };
211
+ return { ir, joins };
212
+ }
213
+ /**
214
+ * The index alone, for a search with no project: every indexed node, each
215
+ * with its native id as its physical id.
216
+ */
217
+ export function indexOnlyGraph(doc) {
218
+ return {
219
+ ...doc.graph,
220
+ nodes: doc.graph.nodes.map((n) => (n.physicalId === undefined && n.nativeId !== undefined ? { ...n, physicalId: n.nativeId } : n)),
221
+ edges: doc.graph.edges ?? [],
222
+ };
223
+ }
224
+ /** How many rows carry each verdict, for the provenance line. */
225
+ export function countJoins(joins, ids) {
226
+ const out = Object.fromEntries(INDEX_JOIN_VERDICTS.map((v) => [v, 0]));
227
+ for (const id of ids) {
228
+ const j = joins[id];
229
+ if (j)
230
+ out[j.verdict] += 1;
231
+ }
232
+ return out;
233
+ }
234
+ /** A row's verdict where a physical id would go: `(absent)`, `(not observed: filtered)`, `(ambiguous: 2 candidates)`. */
235
+ export function joinCell(j) {
236
+ switch (j.verdict) {
237
+ case "bound":
238
+ return undefined;
239
+ case "absent":
240
+ return "(absent)";
241
+ case "not-observed":
242
+ return `(not observed: ${j.reason})`;
243
+ case "no-binding":
244
+ return "(no-binding)";
245
+ case "ambiguous":
246
+ return `(ambiguous: ${j.candidates.length} candidates)`;
247
+ }
248
+ }
@@ -2,6 +2,10 @@ export * from "./types.js";
2
2
  export * from "./git.js";
3
3
  export * from "./digest.js";
4
4
  export * from "./snapshot.js";
5
+ export * from "./snapshot-store.js";
6
+ export * from "./snapshot-store-s3.js";
7
+ export * from "./index-document.js";
8
+ export * from "./index-diff.js";
5
9
  export * from "./live-diff.js";
6
10
  export * from "./deep-diff.js";
7
11
  export * from "./deep-observe.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lifecycle/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9K,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lifecycle/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9K,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
@@ -2,6 +2,10 @@ export * from "./types.js";
2
2
  export * from "./git.js";
3
3
  export * from "./digest.js";
4
4
  export * from "./snapshot.js";
5
+ export * from "./snapshot-store.js";
6
+ export * from "./snapshot-store-s3.js";
7
+ export * from "./index-document.js";
8
+ export * from "./index-diff.js";
5
9
  export * from "./live-diff.js";
6
10
  export * from "./deep-diff.js";
7
11
  export * from "./deep-observe.js";
@@ -23,19 +23,29 @@
23
23
  *
24
24
  * ## Where it lives
25
25
  *
26
- * `<environment>/observation-baseline.json` on the `chant/lifecycle` orphan
27
- * branch — the epic's named candidate home, and the same storage the snapshots
28
- * (`<env>/<lexicon>.json`), the release ledger (`<env>/releases.jsonl`) and the
29
- * build archive (`_builds/<digest>.json`) already use, through the same
30
- * `writeBlobToPath`/`readBlobFromPath` plumbing. One env-keyed namespace for
31
- * everything chant records *about* an environment rather than *for* it.
26
+ * A file in the repository, `.chant/observation-baseline/<env>.json` under
27
+ * the project (#4519). An acceptance is then a commit in an ordinary pull
28
+ * request, reviewed with the change that needed it. It is not derivable and
29
+ * not critical, and it is meant to be reviewed: that is the rule that puts a
30
+ * record here rather than in the index store.
32
31
  *
33
- * The parse/serialize/update half below is pure and storage-free, so the
34
- * decision is one function call deep if a repo-committed file (`.chant/`) turns
35
- * out to be the better review surface.
32
+ * Other homes, through `observationBaseline` in chant.config.ts or
33
+ * `CHANT_OBSERVATION_BASELINE_STORE`: `git-ref` (`refs/chant/observation-baseline`),
34
+ * `s3` (`<prefix>/observation-baseline/<env>.json`, with the s3 settings of
35
+ * `observationBaseline.s3`, else of `snapshots`), or `none`.
36
+ *
37
+ * A baseline written before #4519, `<environment>/observation-baseline.json`
38
+ * on the `chant/lifecycle` orphan branch, is still read when the configured
39
+ * home has none. It is never written there again.
40
+ *
41
+ * The parse/serialize/update half below is pure and storage-free.
36
42
  */
37
- /** The file name under `<environment>/` on the orphan branch. */
43
+ /** The file name under `<environment>/` on the orphan branch, where a baseline written before #4519 is read from. */
38
44
  export declare const OBSERVATION_BASELINE_FILE = "observation-baseline.json";
45
+ /** The directory, relative to the project, that holds one baseline file per environment (#4519). */
46
+ export declare const OBSERVATION_BASELINE_DIR = ".chant/observation-baseline";
47
+ /** The ref a `git-ref` baseline is written to. */
48
+ export declare const OBSERVATION_BASELINE_REF = "refs/chant/observation-baseline";
39
49
  /** One deviation somebody accepted, bound to the value they accepted. */
40
50
  export interface AcceptedDeviation {
41
51
  /** Property path within the entity's normalized tree (`Tags[0].Value`, `Policy.Statement[1].Effect`). */
@@ -120,17 +130,47 @@ export interface AcceptDeviationsOptions {
120
130
  export declare function acceptDeviations(baseline: ObservationBaseline, lexicon: string, deviations: readonly DeviationToAccept[], opts?: AcceptDeviationsOptions): ObservationBaseline;
121
131
  /** Total accepted deviations across every lexicon — for the "N accepted" line. */
122
132
  export declare function countAccepted(baseline: ObservationBaseline | null | undefined): number;
133
+ export type ObservationBaselineStoreKind = "file" | "git-ref" | "s3" | "none";
134
+ /** `observationBaseline` in chant.config.ts. */
135
+ export interface ObservationBaselineStoreConfig {
136
+ store: ObservationBaselineStoreKind;
137
+ /** `file`: the directory, relative to the project. Default {@link OBSERVATION_BASELINE_DIR}. */
138
+ dir?: string;
139
+ /** `git-ref`: the ref. Default {@link OBSERVATION_BASELINE_REF}. */
140
+ ref?: string;
141
+ /** `s3`: where. Default: the `snapshots` store's s3 settings. */
142
+ s3?: import("./snapshot-store-s3.js").S3StoreSettings;
143
+ }
144
+ /** Where to read and write a baseline: the project directory, and its config when the caller has it loaded. */
145
+ export interface ObservationBaselineLocation {
146
+ cwd?: string;
147
+ /** The project's loaded chant.config. Loaded from `cwd` upward when absent. */
148
+ config?: {
149
+ observationBaseline?: unknown;
150
+ snapshots?: unknown;
151
+ };
152
+ /** Overrides the configured home. */
153
+ store?: ObservationBaselineStoreConfig;
154
+ }
155
+ /** The home in effect: `CHANT_OBSERVATION_BASELINE_STORE` over `observationBaseline` over `file`. */
156
+ export declare function observationBaselineStoreConfig(configured?: unknown, env?: Record<string, string | undefined>): ObservationBaselineStoreConfig;
157
+ /** Where a baseline for `environment` lives in the configured home, for messages. */
158
+ export declare function observationBaselineWhere(environment: string, opts?: ObservationBaselineLocation): Promise<string>;
123
159
  /**
124
- * Read the accepted baseline for an environment. Returns `null` when the branch,
125
- * the environment, or the file does not exist — every one of which means
126
- * "nothing accepted yet", the normal state before anyone runs
127
- * `--update-baseline`.
160
+ * Read the accepted baseline for an environment. Returns `null` when there is
161
+ * none, the normal state before anyone runs `--update-baseline`. When the
162
+ * configured home has none, a baseline written before #4519 on the
163
+ * `chant/lifecycle` branch is read instead.
128
164
  */
129
- export declare function readObservationBaseline(environment: string, opts?: {
130
- cwd?: string;
131
- }): Promise<ObservationBaseline | null>;
132
- /** Write the accepted baseline to the orphan branch. Returns the new commit SHA. */
133
- export declare function writeObservationBaseline(baseline: ObservationBaseline, opts?: {
134
- cwd?: string;
135
- }): Promise<string>;
165
+ export declare function readObservationBaseline(environment: string, opts?: ObservationBaselineLocation): Promise<ObservationBaseline | null>;
166
+ /** What {@link saveObservationBaseline} did. `file` means written in the working tree, for the caller to commit. */
167
+ export interface SavedObservationBaseline {
168
+ where: string;
169
+ published: "file" | "pushed" | "local" | "failed" | "none";
170
+ detail?: string;
171
+ }
172
+ /** Write the accepted baseline to its home, and push it when the home is a ref. */
173
+ export declare function saveObservationBaseline(baseline: ObservationBaseline, opts?: ObservationBaselineLocation): Promise<SavedObservationBaseline>;
174
+ /** Write the accepted baseline to its home. Returns where it went. */
175
+ export declare function writeObservationBaseline(baseline: ObservationBaseline, opts?: ObservationBaselineLocation): Promise<string>;
136
176
  //# sourceMappingURL=observation-baseline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observation-baseline.d.ts","sourceRoot":"","sources":["../../src/lifecycle/observation-baseline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"observation-baseline.d.ts","sourceRoot":"","sources":["../../src/lifecycle/observation-baseline.ts"],"names":[],"mappings":"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"}
@@ -23,22 +23,35 @@
23
23
  *
24
24
  * ## Where it lives
25
25
  *
26
- * `<environment>/observation-baseline.json` on the `chant/lifecycle` orphan
27
- * branch — the epic's named candidate home, and the same storage the snapshots
28
- * (`<env>/<lexicon>.json`), the release ledger (`<env>/releases.jsonl`) and the
29
- * build archive (`_builds/<digest>.json`) already use, through the same
30
- * `writeBlobToPath`/`readBlobFromPath` plumbing. One env-keyed namespace for
31
- * everything chant records *about* an environment rather than *for* it.
26
+ * A file in the repository, `.chant/observation-baseline/<env>.json` under
27
+ * the project (#4519). An acceptance is then a commit in an ordinary pull
28
+ * request, reviewed with the change that needed it. It is not derivable and
29
+ * not critical, and it is meant to be reviewed: that is the rule that puts a
30
+ * record here rather than in the index store.
32
31
  *
33
- * The parse/serialize/update half below is pure and storage-free, so the
34
- * decision is one function call deep if a repo-committed file (`.chant/`) turns
35
- * out to be the better review surface.
32
+ * Other homes, through `observationBaseline` in chant.config.ts or
33
+ * `CHANT_OBSERVATION_BASELINE_STORE`: `git-ref` (`refs/chant/observation-baseline`),
34
+ * `s3` (`<prefix>/observation-baseline/<env>.json`, with the s3 settings of
35
+ * `observationBaseline.s3`, else of `snapshots`), or `none`.
36
+ *
37
+ * A baseline written before #4519, `<environment>/observation-baseline.json`
38
+ * on the `chant/lifecycle` orphan branch, is still read when the configured
39
+ * home has none. It is never written there again.
40
+ *
41
+ * The parse/serialize/update half below is pure and storage-free.
36
42
  */
37
- import { readBlobFromPath, writeBlobToPath } from "./git.js";
43
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
44
+ import { dirname, join, resolve } from "node:path";
45
+ import { ledgerDir, readBlobFromPath } from "./git.js";
46
+ import { encodeIndexSegment } from "./index-document.js";
38
47
  import { sortedJsonReplacer } from "../utils.js";
39
48
  import { EFFECT_RECEIPT_ENTITY_TYPE } from "../effect-receipt.js";
40
- /** The file name under `<environment>/` on the orphan branch. */
49
+ /** The file name under `<environment>/` on the orphan branch, where a baseline written before #4519 is read from. */
41
50
  export const OBSERVATION_BASELINE_FILE = "observation-baseline.json";
51
+ /** The directory, relative to the project, that holds one baseline file per environment (#4519). */
52
+ export const OBSERVATION_BASELINE_DIR = ".chant/observation-baseline";
53
+ /** The ref a `git-ref` baseline is written to. */
54
+ export const OBSERVATION_BASELINE_REF = "refs/chant/observation-baseline";
42
55
  /** An environment with nothing accepted yet. */
43
56
  export function emptyBaseline(environment) {
44
57
  return { baseline: "v1", environment, lexicons: {} };
@@ -139,17 +152,129 @@ export function countAccepted(baseline) {
139
152
  }
140
153
  return n;
141
154
  }
142
- // ── Storage (chant/lifecycle orphan branch) ─────────────────────────────────
155
+ const BASELINE_KINDS = ["file", "git-ref", "s3", "none"];
156
+ /** The home in effect: `CHANT_OBSERVATION_BASELINE_STORE` over `observationBaseline` over `file`. */
157
+ export function observationBaselineStoreConfig(configured, env = process.env) {
158
+ const base = (configured && typeof configured === "object" ? configured : {});
159
+ const kind = (env.CHANT_OBSERVATION_BASELINE_STORE?.trim() || base.store || "file");
160
+ if (!BASELINE_KINDS.includes(kind)) {
161
+ throw new Error(`observation baseline store "${kind}" is not one of ${BASELINE_KINDS.join(", ")}`);
162
+ }
163
+ return { ...(base.store === kind ? base : {}), store: kind };
164
+ }
165
+ async function resolveLocation(opts) {
166
+ const cwd = resolve(opts?.cwd ?? process.cwd());
167
+ const { findProjectConfigPastFragments, loadChantConfigUpward } = await import("../config.js");
168
+ let config = opts?.config;
169
+ if (!opts?.store && config === undefined) {
170
+ try {
171
+ config = (await loadChantConfigUpward(cwd)).config;
172
+ }
173
+ catch {
174
+ config = undefined;
175
+ }
176
+ }
177
+ // The `file` home is relative to the project, not to wherever the command
178
+ // ran: a run from a member's subdirectory writes under that member's own
179
+ // directory, as every other store writes under its member path (#2538).
180
+ const projectDir = findProjectConfigPastFragments(cwd).dir;
181
+ return { cwd, projectDir, store: opts?.store ?? observationBaselineStoreConfig(config?.observationBaseline), snapshots: config?.snapshots };
182
+ }
183
+ /** The `file` home's path for one environment, under the project directory. */
184
+ function baselineFile(projectDir, store, env) {
185
+ return join(projectDir, store.dir ?? OBSERVATION_BASELINE_DIR, `${env}.json`);
186
+ }
187
+ async function s3For(store, snapshots) {
188
+ const { S3Medium } = await import("./snapshot-store-s3.js");
189
+ const { snapshotStoreConfig } = await import("./snapshot-store.js");
190
+ let settings = store.s3;
191
+ if (!settings) {
192
+ const snap = snapshotStoreConfig(snapshots);
193
+ if (snap.store !== "s3")
194
+ throw new Error("the s3 observation baseline store needs observationBaseline.s3, or a snapshots store that is s3");
195
+ const { store: _s, ...rest } = snap;
196
+ settings = rest;
197
+ }
198
+ return new S3Medium(settings);
199
+ }
200
+ /** Where a baseline for `environment` lives in the configured home, for messages. */
201
+ export async function observationBaselineWhere(environment, opts) {
202
+ const { cwd, store, snapshots } = await resolveLocation(opts);
203
+ switch (store.store) {
204
+ case "file":
205
+ return join(store.dir ?? OBSERVATION_BASELINE_DIR, `${encodeIndexSegment(environment)}.json`);
206
+ case "git-ref":
207
+ return `${store.ref ?? OBSERVATION_BASELINE_REF}:${await ledgerDir(encodeIndexSegment(environment), { cwd })}.json`;
208
+ case "s3":
209
+ return `${(await s3For(store, snapshots)).where}/observation-baseline/${await ledgerDir(encodeIndexSegment(environment), { cwd })}.json`;
210
+ case "none":
211
+ return "nowhere (the observation baseline store is none)";
212
+ }
213
+ }
143
214
  /**
144
- * Read the accepted baseline for an environment. Returns `null` when the branch,
145
- * the environment, or the file does not exist — every one of which means
146
- * "nothing accepted yet", the normal state before anyone runs
147
- * `--update-baseline`.
215
+ * Read the accepted baseline for an environment. Returns `null` when there is
216
+ * none, the normal state before anyone runs `--update-baseline`. When the
217
+ * configured home has none, a baseline written before #4519 on the
218
+ * `chant/lifecycle` branch is read instead.
148
219
  */
149
220
  export async function readObservationBaseline(environment, opts) {
150
- return parseBaseline(await readBlobFromPath(environment, OBSERVATION_BASELINE_FILE, opts));
221
+ const { cwd, projectDir, store, snapshots } = await resolveLocation(opts);
222
+ let text = null;
223
+ switch (store.store) {
224
+ case "none":
225
+ return null;
226
+ case "file":
227
+ try {
228
+ text = readFileSync(baselineFile(projectDir, store, encodeIndexSegment(environment)), "utf8");
229
+ }
230
+ catch {
231
+ text = null;
232
+ }
233
+ break;
234
+ case "git-ref": {
235
+ const { getRuntime } = await import("../runtime-adapter.js");
236
+ const path = `${await ledgerDir(encodeIndexSegment(environment), { cwd })}.json`;
237
+ const r = await getRuntime().spawn(["git", "cat-file", "blob", `${store.ref ?? OBSERVATION_BASELINE_REF}:${path}`], { cwd });
238
+ text = r.exitCode === 0 ? r.stdout : null;
239
+ break;
240
+ }
241
+ case "s3":
242
+ text = await (await s3For(store, snapshots)).get(`observation-baseline/${await ledgerDir(encodeIndexSegment(environment), { cwd })}.json`);
243
+ break;
244
+ }
245
+ return parseBaseline(text ?? (await readBlobFromPath(environment, OBSERVATION_BASELINE_FILE, { cwd })));
246
+ }
247
+ /** Write the accepted baseline to its home, and push it when the home is a ref. */
248
+ export async function saveObservationBaseline(baseline, opts) {
249
+ const { cwd, projectDir, store, snapshots } = await resolveLocation(opts);
250
+ const where = await observationBaselineWhere(baseline.environment, { cwd, store, ...(snapshots !== undefined ? { config: { snapshots } } : {}) });
251
+ const content = serializeBaseline(baseline);
252
+ const env = encodeIndexSegment(baseline.environment);
253
+ switch (store.store) {
254
+ case "none":
255
+ return { where, published: "none" };
256
+ case "file": {
257
+ const file = baselineFile(projectDir, store, env);
258
+ mkdirSync(dirname(file), { recursive: true });
259
+ const tmp = `${file}.${process.pid}.tmp`;
260
+ writeFileSync(tmp, content);
261
+ renameSync(tmp, file);
262
+ return { where, published: "file" };
263
+ }
264
+ case "git-ref": {
265
+ const { commitFilesToRef, publishRef } = await import("./snapshot-store.js");
266
+ const ref = store.ref ?? OBSERVATION_BASELINE_REF;
267
+ const path = `${await ledgerDir(env, { cwd })}.json`;
268
+ await commitFilesToRef(ref, [{ path, content }], "Accepted observation baseline", { cwd });
269
+ const pushed = await publishRef(ref, new Map([[path, content]]), "Accepted observation baseline", { cwd });
270
+ return { where, published: pushed.status === "pushed" ? "pushed" : pushed.status === "local" ? "local" : "failed", ...(pushed.detail ? { detail: pushed.detail } : {}) };
271
+ }
272
+ case "s3":
273
+ await (await s3For(store, snapshots)).put(`observation-baseline/${await ledgerDir(env, { cwd })}.json`, content);
274
+ return { where, published: "pushed" };
275
+ }
151
276
  }
152
- /** Write the accepted baseline to the orphan branch. Returns the new commit SHA. */
277
+ /** Write the accepted baseline to its home. Returns where it went. */
153
278
  export async function writeObservationBaseline(baseline, opts) {
154
- return writeBlobToPath(baseline.environment, OBSERVATION_BASELINE_FILE, serializeBaseline(baseline), "Accepted observation baseline", opts);
279
+ return (await saveObservationBaseline(baseline, opts)).where;
155
280
  }
@@ -1 +1 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/lifecycle/observe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/lifecycle/observe.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,8 @@ function qualifyObservation(obs, stackName) {
38
38
  notes: obs.notes,
39
39
  // Exports are already keyed by stack (#1279); nothing to qualify.
40
40
  ...(obs.stackExports ? { stackExports: obs.stackExports } : {}),
41
+ // Edge endpoints are resource ids, so they re-key with them (#4528).
42
+ ...(obs.edges ? { edges: obs.edges.map((e) => ({ ...e, from: `${stackName}::${e.from}`, to: `${stackName}::${e.to}` })) } : {}),
41
43
  };
42
44
  }
43
45
  /**
@@ -383,13 +385,16 @@ function pushObservation(observations, warnings, lexicon, observed, environment,
383
385
  // Dependencies ride alongside the managed resources so they become nodes, and
384
386
  // carry `referencedBy` so every consumer can still tell the two apart.
385
387
  const hasDependencies = Object.keys(dependencies.resources).length > 0;
388
+ // The read's own edges (#4528, an engine's graph) and the dependency
389
+ // closure's ride together; `buildLiveGraphIr` dedupes them.
390
+ const edges = [...(observed.edges ?? []), ...dependencies.edges];
386
391
  observations.push({
387
392
  lexicon,
388
393
  resources: hasDependencies
389
394
  ? { ...observed.resources, ...dependencies.resources }
390
395
  : observed.resources,
391
396
  ...(unobservedNames.length > 0 ? { unobserved: observed.unobserved } : {}),
392
- ...(dependencies.edges.length > 0 ? { edges: dependencies.edges } : {}),
397
+ ...(edges.length > 0 ? { edges } : {}),
393
398
  ...(observed.stackExports && Object.keys(observed.stackExports).length > 0
394
399
  ? { stackExports: observed.stackExports }
395
400
  : {}),