@intentius/chant 0.100.0 → 0.102.0

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 (608) hide show
  1. package/dist/agents/checks.d.ts.map +1 -1
  2. package/dist/attrref.d.ts.map +1 -1
  3. package/dist/audit/discover.d.ts.map +1 -1
  4. package/dist/audit/nginx.d.ts.map +1 -1
  5. package/dist/audit/prior-art.d.ts +48 -0
  6. package/dist/audit/prior-art.d.ts.map +1 -1
  7. package/dist/behaviour-kinds.d.ts.map +1 -1
  8. package/dist/build.d.ts +6 -0
  9. package/dist/build.d.ts.map +1 -1
  10. package/dist/builder.d.ts.map +1 -1
  11. package/dist/cli/build-options.d.ts +2 -0
  12. package/dist/cli/build-options.d.ts.map +1 -1
  13. package/dist/cli/commands/build.d.ts.map +1 -1
  14. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  15. package/dist/cli/commands/check-lexicon.d.ts +16 -0
  16. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  17. package/dist/cli/commands/import.d.ts +8 -0
  18. package/dist/cli/commands/import.d.ts.map +1 -1
  19. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  20. package/dist/cli/commands/init.d.ts +19 -0
  21. package/dist/cli/commands/init.d.ts.map +1 -1
  22. package/dist/cli/commands/onboard.d.ts +9 -0
  23. package/dist/cli/commands/onboard.d.ts.map +1 -1
  24. package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
  25. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
  26. package/dist/cli/handlers/dev.d.ts.map +1 -1
  27. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  28. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  29. package/dist/cli/handlers/misc.d.ts.map +1 -1
  30. package/dist/cli/handlers/operator.d.ts +9 -0
  31. package/dist/cli/handlers/operator.d.ts.map +1 -1
  32. package/dist/cli/handlers/run.d.ts.map +1 -1
  33. package/dist/cli/lsp/server.d.ts.map +1 -1
  34. package/dist/cli/main.d.ts.map +1 -1
  35. package/dist/cli/mcp/server.d.ts.map +1 -1
  36. package/dist/cli/mcp/tools/build.d.ts.map +1 -1
  37. package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
  38. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  39. package/dist/cli/mcp/tools/import.d.ts.map +1 -1
  40. package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
  41. package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
  42. package/dist/cli/mcp/tools/search.d.ts.map +1 -1
  43. package/dist/cli/mcp/workspace-tools.d.ts +9 -1
  44. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  45. package/dist/cli/registry.d.ts +33 -2
  46. package/dist/cli/registry.d.ts.map +1 -1
  47. package/dist/codegen/fetch.d.ts.map +1 -1
  48. package/dist/codegen/naming.d.ts.map +1 -1
  49. package/dist/codegen/pinned-upgrade.d.ts +67 -3
  50. package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
  51. package/dist/codegen/registry.d.ts.map +1 -1
  52. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  53. package/dist/components/capability.d.ts.map +1 -1
  54. package/dist/components/driver.d.ts.map +1 -1
  55. package/dist/components/fan-out.d.ts.map +1 -1
  56. package/dist/components/starter-plugin.d.ts.map +1 -1
  57. package/dist/components/verbs/process-runner.d.ts.map +1 -1
  58. package/dist/components/verbs/r2-sync.d.ts.map +1 -1
  59. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  60. package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
  61. package/dist/components/verbs/sign.d.ts.map +1 -1
  62. package/dist/components/verbs/verify.d.ts.map +1 -1
  63. package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
  64. package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
  65. package/dist/components/verbs/wrangler.d.ts.map +1 -1
  66. package/dist/composite.d.ts.map +1 -1
  67. package/dist/config.d.ts +30 -7
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/declarable.d.ts +16 -0
  70. package/dist/declarable.d.ts.map +1 -1
  71. package/dist/discovery/cache.d.ts.map +1 -1
  72. package/dist/discovery/fold-import.d.ts +15 -5
  73. package/dist/discovery/fold-import.d.ts.map +1 -1
  74. package/dist/discovery/param-deps.d.ts +16 -0
  75. package/dist/discovery/param-deps.d.ts.map +1 -1
  76. package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
  77. package/dist/discovery/specifier-resolve.d.ts +37 -0
  78. package/dist/discovery/specifier-resolve.d.ts.map +1 -0
  79. package/dist/errors.d.ts.map +1 -1
  80. package/dist/fold/fold.d.ts +3 -1
  81. package/dist/fold/fold.d.ts.map +1 -1
  82. package/dist/graph-detail.d.ts.map +1 -1
  83. package/dist/graph-ir.d.ts.map +1 -1
  84. package/dist/graph-layout.d.ts.map +1 -1
  85. package/dist/import/base-parser.d.ts.map +1 -1
  86. package/dist/import/embedded.d.ts +7 -0
  87. package/dist/import/embedded.d.ts.map +1 -1
  88. package/dist/import/parser-options.d.ts +14 -0
  89. package/dist/import/parser-options.d.ts.map +1 -0
  90. package/dist/import/parser.d.ts +17 -0
  91. package/dist/import/parser.d.ts.map +1 -1
  92. package/dist/kubectl-context.d.ts.map +1 -1
  93. package/dist/lexicon-output.d.ts.map +1 -1
  94. package/dist/lexicon-schema.d.ts +3 -3
  95. package/dist/lexicon.d.ts +78 -3
  96. package/dist/lexicon.d.ts.map +1 -1
  97. package/dist/lifecycle/assert-live.d.ts.map +1 -1
  98. package/dist/lifecycle/gate-ledger.d.ts +15 -0
  99. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  100. package/dist/lifecycle/git.d.ts.map +1 -1
  101. package/dist/lifecycle/member-ledger.d.ts.map +1 -1
  102. package/dist/lifecycle/observe.d.ts.map +1 -1
  103. package/dist/lifecycle/plan-digest.d.ts +26 -5
  104. package/dist/lifecycle/plan-digest.d.ts.map +1 -1
  105. package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
  106. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  107. package/dist/lifecycle/scenario.d.ts.map +1 -1
  108. package/dist/lifecycle/status.d.ts.map +1 -1
  109. package/dist/lifecycle/work-lease.d.ts +61 -2
  110. package/dist/lifecycle/work-lease.d.ts.map +1 -1
  111. package/dist/lint/config.d.ts +4 -4
  112. package/dist/lint/engine.d.ts +6 -0
  113. package/dist/lint/engine.d.ts.map +1 -1
  114. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  115. package/dist/op/activities/activity-contracts.d.ts +12 -11
  116. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  117. package/dist/op/activities/apply.d.ts +79 -9
  118. package/dist/op/activities/apply.d.ts.map +1 -1
  119. package/dist/op/activities/decide.d.ts.map +1 -1
  120. package/dist/op/activities/index.d.ts +1 -1
  121. package/dist/op/activities/index.d.ts.map +1 -1
  122. package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
  123. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
  124. package/dist/op/activities/propose-upgrade.d.ts +2 -0
  125. package/dist/op/activities/propose-upgrade.d.ts.map +1 -1
  126. package/dist/op/activity-contract.d.ts +1 -1
  127. package/dist/op/activity-failure.d.ts.map +1 -1
  128. package/dist/op/activity-profiles.d.ts.map +1 -1
  129. package/dist/op/builders.d.ts.map +1 -1
  130. package/dist/op/composites/apply-op.d.ts +12 -5
  131. package/dist/op/composites/apply-op.d.ts.map +1 -1
  132. package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
  133. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
  134. package/dist/op/decide-backend.d.ts.map +1 -1
  135. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  136. package/dist/op/emulator-lifecycle.d.ts +21 -2
  137. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  138. package/dist/op/gate-approval.d.ts +5 -1
  139. package/dist/op/gate-approval.d.ts.map +1 -1
  140. package/dist/op/gate-wait.d.ts.map +1 -1
  141. package/dist/op/gate.d.ts +19 -3
  142. package/dist/op/gate.d.ts.map +1 -1
  143. package/dist/op/index.d.ts +3 -1
  144. package/dist/op/index.d.ts.map +1 -1
  145. package/dist/op/local-executor.d.ts +6 -0
  146. package/dist/op/local-executor.d.ts.map +1 -1
  147. package/dist/op/operator.d.ts.map +1 -1
  148. package/dist/op/run-context.d.ts +39 -0
  149. package/dist/op/run-context.d.ts.map +1 -0
  150. package/dist/op/run-live.d.ts.map +1 -1
  151. package/dist/op/runtimes/local.d.ts.map +1 -1
  152. package/dist/op/source-archive.d.ts.map +1 -1
  153. package/dist/op/steward-beside.d.ts.map +1 -1
  154. package/dist/op/steward-points.d.ts.map +1 -1
  155. package/dist/op/steward.d.ts.map +1 -1
  156. package/dist/op/types.d.ts +8 -3
  157. package/dist/op/types.d.ts.map +1 -1
  158. package/dist/op/work-lease-run.d.ts +24 -4
  159. package/dist/op/work-lease-run.d.ts.map +1 -1
  160. package/dist/project-root.d.ts.map +1 -1
  161. package/dist/provenance.d.ts +21 -0
  162. package/dist/provenance.d.ts.map +1 -1
  163. package/dist/pseudo-parameter.d.ts.map +1 -1
  164. package/dist/reconcile.d.ts.map +1 -1
  165. package/dist/secret-materialization.d.ts.map +1 -1
  166. package/dist/serializer.d.ts +8 -0
  167. package/dist/serializer.d.ts.map +1 -1
  168. package/dist/telemetry-attribution.d.ts +77 -0
  169. package/dist/telemetry-attribution.d.ts.map +1 -0
  170. package/dist/terraform/emit-source.d.ts.map +1 -1
  171. package/dist/terraform/parse.d.ts.map +1 -1
  172. package/dist/testing.d.ts.map +1 -1
  173. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  174. package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
  175. package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
  176. package/dist/workspace/agent-cli.d.ts +88 -0
  177. package/dist/workspace/agent-cli.d.ts.map +1 -0
  178. package/dist/workspace/box-factory.d.ts +73 -0
  179. package/dist/workspace/box-factory.d.ts.map +1 -0
  180. package/dist/workspace/changes-cli.d.ts.map +1 -1
  181. package/dist/workspace/changes.d.ts +8 -1
  182. package/dist/workspace/changes.d.ts.map +1 -1
  183. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
  184. package/dist/workspace/checks/boxes.d.ts.map +1 -1
  185. package/dist/workspace/checks/links.d.ts +1 -0
  186. package/dist/workspace/checks/links.d.ts.map +1 -1
  187. package/dist/workspace/checks/live.d.ts +40 -0
  188. package/dist/workspace/checks/live.d.ts.map +1 -0
  189. package/dist/workspace/checks.d.ts +26 -2
  190. package/dist/workspace/checks.d.ts.map +1 -1
  191. package/dist/workspace/compose-graph.d.ts +73 -0
  192. package/dist/workspace/compose-graph.d.ts.map +1 -1
  193. package/dist/workspace/conformance/index.d.ts +67 -6
  194. package/dist/workspace/conformance/index.d.ts.map +1 -1
  195. package/dist/workspace/conformance/vitest.d.ts +12 -3
  196. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  197. package/dist/workspace/conformance/writer.d.ts +368 -0
  198. package/dist/workspace/conformance/writer.d.ts.map +1 -0
  199. package/dist/workspace/decide.d.ts +1 -1
  200. package/dist/workspace/decide.d.ts.map +1 -1
  201. package/dist/workspace/declaration.d.ts +119 -0
  202. package/dist/workspace/declaration.d.ts.map +1 -1
  203. package/dist/workspace/declaration.schema.json +368 -4
  204. package/dist/workspace/export-cli.d.ts +12 -0
  205. package/dist/workspace/export-cli.d.ts.map +1 -0
  206. package/dist/workspace/export.d.ts +145 -0
  207. package/dist/workspace/export.d.ts.map +1 -0
  208. package/dist/workspace/generated-files.d.ts.map +1 -1
  209. package/dist/workspace/graph-cache.d.ts.map +1 -1
  210. package/dist/workspace/graph-cli.d.ts +2 -0
  211. package/dist/workspace/graph-cli.d.ts.map +1 -1
  212. package/dist/workspace/identity.d.ts +111 -0
  213. package/dist/workspace/identity.d.ts.map +1 -0
  214. package/dist/workspace/import.d.ts +73 -0
  215. package/dist/workspace/import.d.ts.map +1 -0
  216. package/dist/workspace/intent-cli.d.ts +7 -4
  217. package/dist/workspace/intent-cli.d.ts.map +1 -1
  218. package/dist/workspace/intent-record.d.ts +8 -1
  219. package/dist/workspace/intent-record.d.ts.map +1 -1
  220. package/dist/workspace/intent.d.ts +149 -5
  221. package/dist/workspace/intent.d.ts.map +1 -1
  222. package/dist/workspace/kinds.d.ts +24 -6
  223. package/dist/workspace/kinds.d.ts.map +1 -1
  224. package/dist/workspace/lineage-adopt-cli.d.ts +15 -0
  225. package/dist/workspace/lineage-adopt-cli.d.ts.map +1 -0
  226. package/dist/workspace/lineage-adopt.d.ts +106 -0
  227. package/dist/workspace/lineage-adopt.d.ts.map +1 -0
  228. package/dist/workspace/lineage-check.d.ts +9 -2
  229. package/dist/workspace/lineage-check.d.ts.map +1 -1
  230. package/dist/workspace/lineage-cli.d.ts +6 -1
  231. package/dist/workspace/lineage-cli.d.ts.map +1 -1
  232. package/dist/workspace/lineage-hash-index.d.ts +110 -0
  233. package/dist/workspace/lineage-hash-index.d.ts.map +1 -0
  234. package/dist/workspace/lineage-init.d.ts +10 -0
  235. package/dist/workspace/lineage-init.d.ts.map +1 -1
  236. package/dist/workspace/lineage-lock.d.ts +149 -2
  237. package/dist/workspace/lineage-lock.d.ts.map +1 -1
  238. package/dist/workspace/lineage-migrations.d.ts +15 -3
  239. package/dist/workspace/lineage-migrations.d.ts.map +1 -1
  240. package/dist/workspace/lineage-provenance.d.ts +18 -0
  241. package/dist/workspace/lineage-provenance.d.ts.map +1 -0
  242. package/dist/workspace/lineage-upgrade-cli.d.ts +2 -0
  243. package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -1
  244. package/dist/workspace/lineage-upgrade.d.ts +30 -1
  245. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  246. package/dist/workspace/lineage-versions.d.ts +116 -0
  247. package/dist/workspace/lineage-versions.d.ts.map +1 -0
  248. package/dist/workspace/links.d.ts +31 -5
  249. package/dist/workspace/links.d.ts.map +1 -1
  250. package/dist/workspace/ls-generated.d.ts +37 -0
  251. package/dist/workspace/ls-generated.d.ts.map +1 -0
  252. package/dist/workspace/ls.d.ts +4 -0
  253. package/dist/workspace/ls.d.ts.map +1 -1
  254. package/dist/workspace/member-commands.d.ts.map +1 -1
  255. package/dist/workspace/nested-graph.d.ts +56 -0
  256. package/dist/workspace/nested-graph.d.ts.map +1 -0
  257. package/dist/workspace/nesting.d.ts +21 -0
  258. package/dist/workspace/nesting.d.ts.map +1 -0
  259. package/dist/workspace/pin-cli.d.ts +10 -0
  260. package/dist/workspace/pin-cli.d.ts.map +1 -0
  261. package/dist/workspace/pin-integrity.d.ts +51 -0
  262. package/dist/workspace/pin-integrity.d.ts.map +1 -0
  263. package/dist/workspace/points.d.ts.map +1 -1
  264. package/dist/workspace/principal-classes.d.ts +114 -0
  265. package/dist/workspace/principal-classes.d.ts.map +1 -0
  266. package/dist/workspace/reason-codes.d.ts +47 -3
  267. package/dist/workspace/reason-codes.d.ts.map +1 -1
  268. package/dist/workspace/record-assets.d.ts.map +1 -1
  269. package/dist/workspace/record-sessions.d.ts +7 -11
  270. package/dist/workspace/record-sessions.d.ts.map +1 -1
  271. package/dist/workspace/records-checkout.d.ts +54 -0
  272. package/dist/workspace/records-checkout.d.ts.map +1 -0
  273. package/dist/workspace/records-cli.d.ts +57 -2
  274. package/dist/workspace/records-cli.d.ts.map +1 -1
  275. package/dist/workspace/records-close.d.ts +5 -2
  276. package/dist/workspace/records-close.d.ts.map +1 -1
  277. package/dist/workspace/records-since.d.ts.map +1 -1
  278. package/dist/workspace/records-write.d.ts +23 -4
  279. package/dist/workspace/records-write.d.ts.map +1 -1
  280. package/dist/workspace/records.d.ts +58 -5
  281. package/dist/workspace/records.d.ts.map +1 -1
  282. package/dist/workspace/returns.d.ts +129 -0
  283. package/dist/workspace/returns.d.ts.map +1 -0
  284. package/dist/workspace/runs-cli.d.ts +164 -0
  285. package/dist/workspace/runs-cli.d.ts.map +1 -0
  286. package/dist/workspace/runs.d.ts +423 -0
  287. package/dist/workspace/runs.d.ts.map +1 -0
  288. package/dist/workspace/source-block.d.ts +1 -1
  289. package/dist/workspace/source-block.d.ts.map +1 -1
  290. package/dist/workspace/status-gates.d.ts +15 -3
  291. package/dist/workspace/status-gates.d.ts.map +1 -1
  292. package/dist/workspace/status.d.ts +10 -0
  293. package/dist/workspace/status.d.ts.map +1 -1
  294. package/dist/workspace/symbols.d.ts +52 -0
  295. package/dist/workspace/symbols.d.ts.map +1 -0
  296. package/dist/workspace/template-manifest.d.ts +11 -3
  297. package/dist/workspace/template-manifest.d.ts.map +1 -1
  298. package/dist/workspace/trailer-joins.d.ts +46 -0
  299. package/dist/workspace/trailer-joins.d.ts.map +1 -0
  300. package/dist/workspace/trailers.d.ts +89 -0
  301. package/dist/workspace/trailers.d.ts.map +1 -0
  302. package/dist/workspace/trust/attestor.d.ts +8 -0
  303. package/dist/workspace/trust/attestor.d.ts.map +1 -1
  304. package/dist/workspace/trust/dsse.d.ts +58 -0
  305. package/dist/workspace/trust/dsse.d.ts.map +1 -0
  306. package/dist/workspace/trust/evidence-cli.d.ts +66 -0
  307. package/dist/workspace/trust/evidence-cli.d.ts.map +1 -0
  308. package/dist/workspace/trust/evidence.d.ts +93 -0
  309. package/dist/workspace/trust/evidence.d.ts.map +1 -0
  310. package/dist/workspace/trust/policy.d.ts +54 -1
  311. package/dist/workspace/trust/policy.d.ts.map +1 -1
  312. package/dist/workspace/trust/provenance.d.ts +21 -1
  313. package/dist/workspace/trust/provenance.d.ts.map +1 -1
  314. package/dist/workspace/trust/rotation.d.ts +132 -0
  315. package/dist/workspace/trust/rotation.d.ts.map +1 -0
  316. package/dist/workspace/trust/seal.d.ts +33 -0
  317. package/dist/workspace/trust/seal.d.ts.map +1 -1
  318. package/dist/workspace/trust/signers-cli.d.ts +49 -0
  319. package/dist/workspace/trust/signers-cli.d.ts.map +1 -0
  320. package/dist/workspace/trust/ssh-commit.d.ts +12 -0
  321. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  322. package/dist/workspace/trust/test-repo.d.ts +13 -0
  323. package/dist/workspace/trust/test-repo.d.ts.map +1 -1
  324. package/dist/workspace/trust/verify.d.ts +15 -0
  325. package/dist/workspace/trust/verify.d.ts.map +1 -1
  326. package/dist/workspace/work-cli.d.ts +39 -3
  327. package/dist/workspace/work-cli.d.ts.map +1 -1
  328. package/dist/workspace/work-evidence.d.ts +1 -1
  329. package/dist/workspace/work-evidence.d.ts.map +1 -1
  330. package/dist/workspace/work.d.ts +26 -1
  331. package/dist/workspace/work.d.ts.map +1 -1
  332. package/dist/workspace/workspace-principals.schema.json +60 -0
  333. package/dist/workspace/write-scope.d.ts +259 -0
  334. package/dist/workspace/write-scope.d.ts.map +1 -0
  335. package/dist/yaml.d.ts +21 -39
  336. package/dist/yaml.d.ts.map +1 -1
  337. package/package.json +2 -2
  338. package/src/audit/prior-art.ts +15 -0
  339. package/src/build.ts +8 -0
  340. package/src/cli/build-options.ts +4 -1
  341. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
  342. package/src/cli/commands/build.ts +2 -0
  343. package/src/cli/commands/carve-emit-state.test.ts +4 -0
  344. package/src/cli/commands/carve-emit.ts +5 -19
  345. package/src/cli/commands/check-lexicon.test.ts +62 -1
  346. package/src/cli/commands/check-lexicon.ts +36 -6
  347. package/src/cli/commands/import-live.test.ts +87 -1
  348. package/src/cli/commands/import-parser-options.test.ts +133 -0
  349. package/src/cli/commands/import.ts +69 -25
  350. package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
  351. package/src/cli/commands/init.test.ts +8 -1
  352. package/src/cli/commands/init.ts +23 -8
  353. package/src/cli/commands/lint.test.ts +37 -0
  354. package/src/cli/commands/onboard.test.ts +53 -3
  355. package/src/cli/commands/onboard.ts +52 -4
  356. package/src/cli/commands/pinned-upgrade.ts +73 -33
  357. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
  358. package/src/cli/handlers/components.ts +2 -2
  359. package/src/cli/handlers/dev.ts +8 -6
  360. package/src/cli/handlers/fan-out.test.ts +6 -6
  361. package/src/cli/handlers/fan-out.ts +2 -1
  362. package/src/cli/handlers/graph.test.ts +42 -0
  363. package/src/cli/handlers/graph.ts +22 -0
  364. package/src/cli/handlers/lifecycle.test.ts +67 -0
  365. package/src/cli/handlers/lifecycle.ts +6 -5
  366. package/src/cli/handlers/misc.ts +2 -0
  367. package/src/cli/handlers/operator.ts +88 -4
  368. package/src/cli/handlers/run.test.ts +111 -0
  369. package/src/cli/handlers/run.ts +85 -24
  370. package/src/cli/handlers/scenario.ts +2 -2
  371. package/src/cli/main.test.ts +33 -0
  372. package/src/cli/main.ts +168 -17
  373. package/src/cli/mcp/workspace-tools.test.ts +5 -2
  374. package/src/cli/mcp/workspace-tools.ts +62 -4
  375. package/src/cli/registry.ts +33 -2
  376. package/src/cli/serve-mcp-workspace.test.ts +1 -1
  377. package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
  378. package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
  379. package/src/codegen/pinned-upgrade.test.ts +104 -0
  380. package/src/codegen/pinned-upgrade.ts +250 -56
  381. package/src/codegen/release-wiring.test.ts +5 -1
  382. package/src/codegen/testdata/postgres-tags.json +242 -0
  383. package/src/components/fan-out-output.test.ts +1 -1
  384. package/src/components/fan-out.test.ts +1 -1
  385. package/src/components/promote.test.ts +1 -1
  386. package/src/config.test.ts +21 -0
  387. package/src/config.ts +27 -0
  388. package/src/content-digest.test.ts +2 -2
  389. package/src/declarable.test.ts +21 -0
  390. package/src/declarable.ts +19 -0
  391. package/src/discovery/fold-composite.test.ts +41 -5
  392. package/src/discovery/fold-import.test.ts +129 -0
  393. package/src/discovery/fold-import.ts +643 -131
  394. package/src/discovery/fold-package-composite.test.ts +264 -0
  395. package/src/discovery/fold-specifiers.test.ts +191 -0
  396. package/src/discovery/param-deps.test.ts +66 -1
  397. package/src/discovery/param-deps.ts +45 -0
  398. package/src/discovery/specifier-resolve.ts +344 -0
  399. package/src/fold/fold.test.ts +39 -1
  400. package/src/fold/fold.ts +83 -9
  401. package/src/fold-provenance.test.ts +140 -0
  402. package/src/graph-ir.test.ts +83 -0
  403. package/src/graph-ir.ts +29 -0
  404. package/src/import/embedded.test.ts +20 -0
  405. package/src/import/embedded.ts +13 -0
  406. package/src/import/parser-options.ts +46 -0
  407. package/src/import/parser.ts +20 -0
  408. package/src/lexicon.ts +79 -3
  409. package/src/lifecycle/gate-ledger.test.ts +14 -0
  410. package/src/lifecycle/gate-ledger.ts +18 -1
  411. package/src/lifecycle/observe.test.ts +18 -0
  412. package/src/lifecycle/observe.ts +6 -4
  413. package/src/lifecycle/plan-digest.test.ts +54 -3
  414. package/src/lifecycle/plan-digest.ts +38 -8
  415. package/src/lifecycle/snapshot.ts +2 -2
  416. package/src/lifecycle/work-lease.test.ts +1 -1
  417. package/src/lifecycle/work-lease.ts +93 -2
  418. package/src/lint/engine.test.ts +55 -0
  419. package/src/lint/engine.ts +30 -4
  420. package/src/lint/output-docs.test.ts +59 -56
  421. package/src/op/activities/activity-contracts.ts +1 -0
  422. package/src/op/activities/apply.test.ts +189 -5
  423. package/src/op/activities/apply.ts +230 -8
  424. package/src/op/activities/index.ts +3 -0
  425. package/src/op/activities/lexicon-upgrade.test.ts +160 -6
  426. package/src/op/activities/lexicon-upgrade.ts +154 -51
  427. package/src/op/activities/propose-upgrade.ts +8 -5
  428. package/src/op/composites/apply-op.ts +12 -5
  429. package/src/op/composites/composites.test.ts +30 -3
  430. package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
  431. package/src/op/composites/lexicon-upgrade-op.ts +29 -8
  432. package/src/op/emulator-lifecycle.test.ts +23 -0
  433. package/src/op/emulator-lifecycle.ts +52 -14
  434. package/src/op/gate-approval.test.ts +91 -0
  435. package/src/op/gate-approval.ts +5 -1
  436. package/src/op/gate.ts +34 -8
  437. package/src/op/index.ts +3 -1
  438. package/src/op/local-executor.test.ts +15 -0
  439. package/src/op/local-executor.ts +40 -12
  440. package/src/op/local-output.ts +1 -1
  441. package/src/op/operator.ts +4 -0
  442. package/src/op/run-context.test.ts +141 -0
  443. package/src/op/run-context.ts +51 -0
  444. package/src/op/runtimes/local.test.ts +14 -0
  445. package/src/op/runtimes/local.ts +2 -0
  446. package/src/op/steward-beside.ts +2 -0
  447. package/src/op/types.ts +8 -3
  448. package/src/op/work-lease-run.test.ts +47 -0
  449. package/src/op/work-lease-run.ts +73 -11
  450. package/src/provenance.ts +35 -0
  451. package/src/serializer.ts +9 -0
  452. package/src/telemetry-attribution.test.ts +91 -0
  453. package/src/telemetry-attribution.ts +145 -0
  454. package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
  455. package/src/workspace/agent-cli.ts +138 -0
  456. package/src/workspace/agent.schema.json +378 -0
  457. package/src/workspace/behold-kinds.test.ts +1 -1
  458. package/src/workspace/box-factory.test.ts +320 -0
  459. package/src/workspace/box-factory.ts +103 -0
  460. package/src/workspace/changes-cli.ts +5 -0
  461. package/src/workspace/changes.schema.json +171 -1
  462. package/src/workspace/changes.ts +61 -4
  463. package/src/workspace/check-live.test.ts +192 -0
  464. package/src/workspace/check.schema.json +64 -0
  465. package/src/workspace/checks/boxes.test.ts +2 -0
  466. package/src/workspace/checks/links.ts +23 -2
  467. package/src/workspace/checks/live.ts +113 -0
  468. package/src/workspace/checks.test.ts +2 -2
  469. package/src/workspace/checks.ts +49 -8
  470. package/src/workspace/compose-graph.test.ts +47 -0
  471. package/src/workspace/compose-graph.ts +127 -3
  472. package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +7 -1
  473. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +5 -0
  474. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +27 -0
  475. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +95 -0
  476. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +132 -0
  477. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +48 -0
  478. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +108 -0
  479. package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
  480. package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
  481. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +578 -0
  482. package/src/workspace/conformance/conformance.e2e.test.ts +35 -3
  483. package/src/workspace/conformance/index.mjs +30 -3
  484. package/src/workspace/conformance/index.ts +103 -15
  485. package/src/workspace/conformance/vitest.ts +44 -2
  486. package/src/workspace/conformance/writer-conformance.e2e.test.ts +131 -0
  487. package/src/workspace/conformance/writer.test.ts +156 -0
  488. package/src/workspace/conformance/writer.ts +880 -0
  489. package/src/workspace/decide.ts +7 -1
  490. package/src/workspace/declaration.schema.json +368 -4
  491. package/src/workspace/declaration.ts +270 -1
  492. package/src/workspace/declared-kinds.test.ts +33 -0
  493. package/src/workspace/evidence.schema.json +279 -0
  494. package/src/workspace/export-cli.ts +180 -0
  495. package/src/workspace/export-import.test.ts +282 -0
  496. package/src/workspace/export.ts +486 -0
  497. package/src/workspace/graph-cli.ts +51 -2
  498. package/src/workspace/graph-contract.test.ts +89 -4
  499. package/src/workspace/graph.schema.json +453 -1
  500. package/src/workspace/identity.test.ts +196 -0
  501. package/src/workspace/identity.ts +186 -0
  502. package/src/workspace/import.ts +325 -0
  503. package/src/workspace/intent-cli.ts +64 -10
  504. package/src/workspace/intent-record.schema.json +208 -0
  505. package/src/workspace/intent-record.ts +35 -1
  506. package/src/workspace/intent-why.test.ts +274 -0
  507. package/src/workspace/intent.schema.json +718 -7
  508. package/src/workspace/intent.test.ts +11 -2
  509. package/src/workspace/intent.ts +495 -32
  510. package/src/workspace/kinds.test.ts +5 -5
  511. package/src/workspace/kinds.ts +62 -33
  512. package/src/workspace/lineage-adopt-cli.ts +103 -0
  513. package/src/workspace/lineage-adopt.test.ts +552 -0
  514. package/src/workspace/lineage-adopt.ts +452 -0
  515. package/src/workspace/lineage-check.ts +26 -5
  516. package/src/workspace/lineage-cli.ts +12 -2
  517. package/src/workspace/lineage-hash-index.ts +305 -0
  518. package/src/workspace/lineage-init.test.ts +9 -0
  519. package/src/workspace/lineage-init.ts +29 -9
  520. package/src/workspace/lineage-lock.ts +54 -0
  521. package/src/workspace/lineage-migrations.test.ts +27 -0
  522. package/src/workspace/lineage-migrations.ts +44 -14
  523. package/src/workspace/lineage-provenance.ts +40 -0
  524. package/src/workspace/lineage-upgrade-cli.ts +9 -5
  525. package/src/workspace/lineage-upgrade.test.ts +92 -1
  526. package/src/workspace/lineage-upgrade.ts +111 -16
  527. package/src/workspace/lineage-versions.ts +348 -0
  528. package/src/workspace/links.test.ts +121 -2
  529. package/src/workspace/links.ts +97 -6
  530. package/src/workspace/ls-contract.test.ts +84 -1
  531. package/src/workspace/ls-generated.ts +111 -0
  532. package/src/workspace/ls.schema.json +19 -0
  533. package/src/workspace/ls.ts +8 -1
  534. package/src/workspace/member-commands.ts +3 -1
  535. package/src/workspace/nested-graph.test.ts +176 -0
  536. package/src/workspace/nested-graph.ts +169 -0
  537. package/src/workspace/nesting.ts +37 -0
  538. package/src/workspace/pin-cli.test.ts +71 -0
  539. package/src/workspace/pin-cli.ts +57 -0
  540. package/src/workspace/pin-integrity.test.ts +121 -0
  541. package/src/workspace/pin-integrity.ts +104 -0
  542. package/src/workspace/points-write.schema.json +2 -0
  543. package/src/workspace/principal-classes.test.ts +175 -0
  544. package/src/workspace/principal-classes.ts +307 -0
  545. package/src/workspace/read-contract.test.ts +24 -0
  546. package/src/workspace/reason-codes.test.ts +20 -2
  547. package/src/workspace/reason-codes.ts +53 -3
  548. package/src/workspace/record-sessions.ts +12 -14
  549. package/src/workspace/records-amend.schema.json +6 -0
  550. package/src/workspace/records-checkout.ts +106 -0
  551. package/src/workspace/records-cli.ts +195 -22
  552. package/src/workspace/records-close.schema.json +7 -2
  553. package/src/workspace/records-close.ts +10 -2
  554. package/src/workspace/records-formats.test.ts +13 -5
  555. package/src/workspace/records-new.schema.json +6 -0
  556. package/src/workspace/records-review.schema.json +6 -0
  557. package/src/workspace/records-sessions.test.ts +23 -6
  558. package/src/workspace/records-uncommitted.test.ts +171 -0
  559. package/src/workspace/records-write.test.ts +65 -8
  560. package/src/workspace/records-write.ts +92 -6
  561. package/src/workspace/records.schema.json +127 -4
  562. package/src/workspace/records.ts +123 -5
  563. package/src/workspace/returns.ts +328 -0
  564. package/src/workspace/runs-cli.ts +381 -0
  565. package/src/workspace/runs-write.schema.json +583 -0
  566. package/src/workspace/runs.schema.json +810 -0
  567. package/src/workspace/runs.test.ts +242 -0
  568. package/src/workspace/runs.ts +709 -0
  569. package/src/workspace/signers.schema.json +206 -0
  570. package/src/workspace/status-contract.test.ts +5 -0
  571. package/src/workspace/status-gates.ts +24 -9
  572. package/src/workspace/status.schema.json +127 -2
  573. package/src/workspace/status.ts +21 -1
  574. package/src/workspace/symbols.ts +180 -0
  575. package/src/workspace/template-manifest.ts +22 -4
  576. package/src/workspace/trailer-joins.test.ts +171 -0
  577. package/src/workspace/trailer-joins.ts +82 -0
  578. package/src/workspace/trailers.ts +148 -0
  579. package/src/workspace/trust/attestor.ts +15 -0
  580. package/src/workspace/trust/dsse.ts +134 -0
  581. package/src/workspace/trust/evidence-cli.ts +195 -0
  582. package/src/workspace/trust/evidence.test.ts +241 -0
  583. package/src/workspace/trust/evidence.ts +207 -0
  584. package/src/workspace/trust/policy.ts +110 -3
  585. package/src/workspace/trust/provenance.ts +41 -4
  586. package/src/workspace/trust/record-seal.test.ts +1 -1
  587. package/src/workspace/trust/rotation.test.ts +258 -0
  588. package/src/workspace/trust/rotation.ts +336 -0
  589. package/src/workspace/trust/seal.ts +63 -1
  590. package/src/workspace/trust/signers-cli.ts +178 -0
  591. package/src/workspace/trust/ssh-commit.ts +53 -4
  592. package/src/workspace/trust/test-repo.ts +18 -0
  593. package/src/workspace/trust/trust.test.ts +8 -2
  594. package/src/workspace/trust/verify-cli.ts +1 -0
  595. package/src/workspace/trust/verify.ts +22 -0
  596. package/src/workspace/work-cli.e2e.test.ts +28 -1
  597. package/src/workspace/work-cli.ts +133 -8
  598. package/src/workspace/work-evidence.schema.json +6 -0
  599. package/src/workspace/work-history.schema.json +73 -1
  600. package/src/workspace/work-history.test.ts +48 -5
  601. package/src/workspace/work-kind-fields.test.ts +154 -0
  602. package/src/workspace/work-lease.schema.json +14 -1
  603. package/src/workspace/work.ts +82 -0
  604. package/src/workspace/workspace-principals.schema.json +60 -0
  605. package/src/workspace/write-scope.test.ts +466 -0
  606. package/src/workspace/write-scope.ts +573 -0
  607. package/src/yaml.test.ts +111 -1
  608. package/src/yaml.ts +141 -549
@@ -508,3 +508,143 @@ describe("fold provenance over a real composite build (#2161)", () => {
508
508
  expect(verdicts[3].resolution.kind).toBe("edit-declaration");
509
509
  });
510
510
  });
511
+
512
+ // ─────────────────────────────────────────────────────────────────────────
513
+ // chant #3212 — a registered tag inside a composite factory.
514
+ // ─────────────────────────────────────────────────────────────────────────
515
+
516
+ const runtimePath = resolve(thisDir, "./runtime");
517
+ const provenancePath = resolve(thisDir, "./provenance");
518
+
519
+ /**
520
+ * A SQL-shaped tag, the shape the sql lexicon's `table` takes: it builds one
521
+ * entity out of its text and reports, per interpolation, which of the entity's
522
+ * fields that interpolation fed. An interpolated entity is a reference and
523
+ * feeds nothing the author typed, so it reports no field.
524
+ */
525
+ const TAGS = `
526
+ import { createResource } from ${JSON.stringify(runtimePath)};
527
+ import { setInterpolationFields } from ${JSON.stringify(provenancePath)};
528
+ const Table = createResource("Test::Table", ${JSON.stringify(LEXICON_NAME)}, {});
529
+ const CLAUSE = { TABLE: "name", "ENGINE =": "engine", "ORDER BY": "orderBy", TTL: "ttl", COMMENT: "comment" };
530
+
531
+ export function table(strings, ...values) {
532
+ const isEntity = (v) => typeof v === "object" && v !== null && "props" in v;
533
+ let ddl = strings[0];
534
+ const fields = values.map((v, i) => {
535
+ ddl += (isEntity(v) ? v.props.name : String(v)) + strings[i + 1];
536
+ if (isEntity(v)) return [];
537
+ const at = (k) => strings[i].lastIndexOf(k);
538
+ const clause = Object.keys(CLAUSE).sort((a, b) => at(b) - at(a))[0];
539
+ return at(clause) >= 0 ? [CLAUSE[clause], "ddl"] : ["ddl"];
540
+ });
541
+ const m = /CREATE TABLE (\\w+) ENGINE = (\\w+) ORDER BY (\\w+)(?: TTL (.+?))?(?: COMMENT (\\w+))?$/.exec(ddl);
542
+ if (!m) throw new Error("cannot parse: " + ddl);
543
+ const props = { name: m[1], engine: m[2], orderBy: m[3], ddl };
544
+ if (m[4]) props.ttl = m[4];
545
+ if (m[5]) props.comment = m[5];
546
+ const entity = new Table(props);
547
+ setInterpolationFields(entity, fields);
548
+ return entity;
549
+ }
550
+ `;
551
+
552
+ const TAG_COMPOSITES = `
553
+ import { table } from "./tags";
554
+ import { Composite } from ${JSON.stringify(compositePath)};
555
+
556
+ export const EventsTable = Composite((props) => {
557
+ const rollupName = \`\${props.name}_daily\`;
558
+ const events = table\`CREATE TABLE \${props.name} ENGINE = MergeTree ORDER BY ts TTL ts + INTERVAL \${props.ttlDays} DAY\`;
559
+ const rollup = table\`CREATE TABLE \${rollupName} ENGINE = \${"SummingMergeTree"} ORDER BY ts COMMENT \${events}\`;
560
+ return { events, rollup };
561
+ }, "EventsTable");
562
+ `;
563
+
564
+ const TAG_MAIN = `
565
+ import { table } from "../tags";
566
+ import { EventsTable } from "../composites";
567
+
568
+ export const audit = table\`CREATE TABLE audit ENGINE = MergeTree ORDER BY ts\`;
569
+ export const clicks = EventsTable({ name: "clicks", ttlDays: 30 });
570
+ `;
571
+
572
+ describe("fold provenance through a tagged template in a composite factory (#3212)", () => {
573
+ let testDir: string;
574
+ let srcDir: string;
575
+
576
+ beforeEach(async () => {
577
+ const dir = join(repoRoot, ".cache", `chant-3212-${process.pid}-${seq++}`);
578
+ await rm(dir, { recursive: true, force: true });
579
+ await mkdir(dir, { recursive: true });
580
+ testDir = await realpath(dir);
581
+ srcDir = join(testDir, "src");
582
+ await mkdir(srcDir, { recursive: true });
583
+ await writeFile(join(testDir, "tags.ts"), TAGS);
584
+ await writeFile(join(testDir, "composites.ts"), TAG_COMPOSITES);
585
+ await writeFile(join(srcDir, "main.ts"), TAG_MAIN);
586
+ });
587
+
588
+ afterEach(async () => {
589
+ await rm(testDir, { recursive: true, force: true });
590
+ });
591
+
592
+ const buildIt = () =>
593
+ build(srcDir, [namesSerializer], undefined, {
594
+ fold: true,
595
+ lexicons: [LEXICON_NAME],
596
+ intrinsics: [{ name: "table", isTag: true }],
597
+ });
598
+
599
+ test("a field fed by ${props.x} is that parameter, a field the text fixes is a literal", async () => {
600
+ const result = await buildIt();
601
+ expect(result.errors).toEqual([]);
602
+ const events = result.foldProvenance.clicksEvents;
603
+ const rollup = result.foldProvenance.clicksRollup;
604
+ const at = (parameters: string[]) => ({ kind: "composite-parameter", composite: "EventsTable", instance: "clicks", parameters });
605
+ const fixed = { kind: "composite-literal", composite: "EventsTable", instance: "clicks" };
606
+
607
+ expect(events.fields.name).toEqual(at(["name"]));
608
+ expect(events.fields.ttl).toEqual(at(["ttlDays"]));
609
+ expect(events.fields.ddl).toEqual(at(["name", "ttlDays"]));
610
+ expect(events.fields.engine).toEqual(fixed);
611
+ expect(events.fields.orderBy).toEqual(fixed);
612
+
613
+ // Through a body `const`; an interpolated literal string is fixed; an
614
+ // interpolated sibling entity is wiring, so it governs nothing.
615
+ expect(rollup.fields.name).toEqual(at(["name"]));
616
+ expect(rollup.fields.engine).toEqual(fixed);
617
+ expect(rollup.fields.comment).toEqual(fixed);
618
+ expect(rollup.fields.ddl).toEqual(at(["name"]));
619
+
620
+ expect(result.foldProvenance.audit.fields.ttl).toBeUndefined();
621
+ expect(result.foldProvenance.audit.fields.name).toEqual({ kind: "direct" });
622
+ });
623
+
624
+ test("drift on a parameter-fed field proposes the parameter at the call site", async () => {
625
+ const result = await buildIt();
626
+ const provenanceOf = (name: string): EntityProvenance | undefined => {
627
+ const entity = result.entities.get(name);
628
+ return entity ? getProvenance(entity) : undefined;
629
+ };
630
+ const drift = (name: string, path: string, declared: unknown, live: unknown): DeepEntityDrift => {
631
+ const origin = originOfPath(provenanceOf(name)?.paths, path);
632
+ return { name, type: "Test::Table", changes: [{ path, kind: "changed", declared, live, ...(origin ? { origin } : {}) }] };
633
+ };
634
+
635
+ const [ttl, engine] = resolveDeepDrift(
636
+ [
637
+ drift("clicksEvents", "ttl", "ts + INTERVAL 30 DAY", "ts + INTERVAL 90 DAY"),
638
+ drift("clicksEvents", "engine", "MergeTree", "ReplacingMergeTree"),
639
+ ],
640
+ provenanceOf,
641
+ );
642
+
643
+ expect(ttl.resolution.kind).toBe("propose-parameter");
644
+ if (ttl.resolution.kind !== "propose-parameter") throw new Error("unreachable");
645
+ expect(ttl.resolution.parameters).toEqual(["ttlDays"]);
646
+ expect(ttl.resolution.instance).toBe("clicks");
647
+ expect(ttl.resolution.sourceFile).toContain("main.ts");
648
+ expect(engine.resolution.kind).toBe("refuse-fixed");
649
+ });
650
+ });
@@ -287,3 +287,86 @@ describe("entity-declared references and stacks", () => {
287
287
  expect(ir.nodes.find((n) => n.id === "app/vpc")!.attrs).not.toHaveProperty("stack");
288
288
  });
289
289
  });
290
+
291
+ describe("whole declarables held in an entity's fields (#3257)", () => {
292
+ /** An entity shaped like the sql lexicon's: props non-enumerable, `dependsOn` enumerable. */
293
+ function holder(dependsOn: unknown[], extra: object = {}) {
294
+ const e = decl({ lexicon: "sql", entityType: "ClickHouse::Table", ...extra });
295
+ Object.defineProperty(e, "dependsOn", { value: dependsOn, enumerable: true });
296
+ return e;
297
+ }
298
+
299
+ test("draws an edge to each node named directly in an enumerable array", () => {
300
+ const db = decl({ lexicon: "sql", entityType: "ClickHouse::Database" });
301
+ const target = holder([db]);
302
+ const events = holder([db]);
303
+ const mv = holder([db, target, events]);
304
+ const entities = new Map<string, Declarable>([
305
+ ["analytics", db],
306
+ ["dailyActive", target],
307
+ ["events", events],
308
+ ["dailyActiveMv", mv],
309
+ ]);
310
+ resolveAttrRefs(entities);
311
+
312
+ const ir = buildGraphIr(entities);
313
+ const dep = (from: string, to: string) => ({ from, to, kind: "ref", viaAttr: "dependsOn" });
314
+ expect(ir.edges).toEqual([
315
+ dep("dailyActive", "analytics"),
316
+ dep("dailyActiveMv", "analytics"),
317
+ dep("dailyActiveMv", "dailyActive"),
318
+ dep("dailyActiveMv", "events"),
319
+ dep("events", "analytics"),
320
+ ]);
321
+ });
322
+
323
+ test("does not walk into the referenced node, and projects it as a $ref", () => {
324
+ const db = decl({ lexicon: "sql", entityType: "ClickHouse::Database", props: { name: "analytics" } });
325
+ const events = holder([db]);
326
+ const entities = new Map<string, Declarable>([
327
+ ["analytics", db],
328
+ ["events", events],
329
+ ]);
330
+ resolveAttrRefs(entities);
331
+
332
+ const ir = buildGraphIr(entities);
333
+ expect(ir.nodes.find((n) => n.id === "events")!.attrs.dependsOn).toEqual([{ $ref: "analytics" }]);
334
+ });
335
+
336
+ test("an AttrRef and the whole node to the same target collapse to one edge", () => {
337
+ const db = decl({ lexicon: "sql", entityType: "ClickHouse::Database" });
338
+ const events = decl({ lexicon: "sql", entityType: "ClickHouse::Table" });
339
+ const mv = holder([events, new AttrRef(events, "user_id")]);
340
+ const entities = new Map<string, Declarable>([
341
+ ["analytics", db],
342
+ ["events", events],
343
+ ["mv", mv],
344
+ ]);
345
+ resolveAttrRefs(entities);
346
+
347
+ const ir = buildGraphIr(entities);
348
+ expect(ir.edges).toEqual([{ from: "mv", to: "events", kind: "ref", viaAttr: "dependsOn" }]);
349
+ });
350
+
351
+ test("ignores a self reference, and still descends into an inlined property-kind resource", () => {
352
+ const self: Declarable = holder([]);
353
+ (self as unknown as { dependsOn: unknown[] }).dependsOn.push(self);
354
+ const vpc = decl({ lexicon: "aws", entityType: "Vpc" });
355
+ const inline = decl({
356
+ lexicon: "aws",
357
+ entityType: "Listener_Action",
358
+ kind: "property",
359
+ props: { vpc: new AttrRef(vpc, "Id") },
360
+ });
361
+ const lb = decl({ lexicon: "aws", entityType: "Lb", props: { actions: [inline] } });
362
+ const entities = new Map<string, Declarable>([
363
+ ["self", self],
364
+ ["vpc", vpc],
365
+ ["lb", lb],
366
+ ]);
367
+ resolveAttrRefs(entities);
368
+
369
+ const ir = buildGraphIr(entities);
370
+ expect(ir.edges).toEqual([{ from: "lb", to: "vpc", kind: "ref", viaAttr: "actions" }]);
371
+ });
372
+ });
package/src/graph-ir.ts CHANGED
@@ -384,6 +384,23 @@ function refTarget(ref: AttrRef, reverse: Map<object, string>): string | undefin
384
384
  return ref.getLogicalName() ?? (parent ? reverse.get(parent) : undefined);
385
385
  }
386
386
 
387
+ /**
388
+ * The node id a whole declarable held in a config bag stands for, or undefined
389
+ * when it is not a node of this graph (an inlined property-kind resource, say,
390
+ * which is config of the holder and gets walked instead). An entity can name
391
+ * another entity directly rather than through an AttrRef, as the sql lexicon's
392
+ * enumerable `dependsOn` does with the tables, views and databases a statement
393
+ * mentions (#3257); that is a reference to the node, not a nested bag.
394
+ */
395
+ function nodeReference(
396
+ value: unknown,
397
+ reverse: Map<object, string>,
398
+ nodeIds: Set<string>,
399
+ ): string | undefined {
400
+ const id = reverse.get(value as object);
401
+ return id !== undefined && nodeIds.has(id) ? id : undefined;
402
+ }
403
+
387
404
  /** Project a value into a JSON-safe, scrubbed form. References become `{ $ref }`. */
388
405
  function project(value: unknown, seen: Set<unknown>, reverse: Map<object, string>, nodeIds: Set<string>): unknown {
389
406
  if (value === null) return null;
@@ -414,6 +431,9 @@ function project(value: unknown, seen: Set<unknown>, reverse: Map<object, string
414
431
  // Nested declarables (e.g. an inlined property-kind resource) keep their
415
432
  // config in a non-enumerable `props` bag, like top-level nodes — project that.
416
433
  if (isDeclarable(value)) {
434
+ // Another node held whole is a reference to it, not config to inline.
435
+ const to = nodeReference(value, reverse, nodeIds);
436
+ if (to) return { $ref: to } satisfies AttrRefEnvelope;
417
437
  const out = projectConfig(value, seen, reverse, nodeIds);
418
438
  seen.delete(value);
419
439
  return out;
@@ -518,6 +538,15 @@ function collectEdges(
518
538
  if (to && to !== from && nodeIds.has(to)) edges.push({ from, to, kind: "ref", viaAttr });
519
539
  return;
520
540
  }
541
+ // A whole node held in props or an enumerable field (the sql lexicon's
542
+ // `dependsOn`) is a reference to it: draw the edge, and do not walk its
543
+ // config, whose refs belong to that node's own edges (#3257). Checked
544
+ // before `seen` so the same node under two attributes yields both edges.
545
+ const held = nodeReference(value, reverse, nodeIds);
546
+ if (held) {
547
+ if (held !== from) edges.push({ from, to: held, kind: "ref", viaAttr });
548
+ return;
549
+ }
521
550
  if (seen.has(value)) return;
522
551
  seen.add(value);
523
552
  // A nested declarable (an inlined property-kind resource, e.g. a listener's
@@ -85,6 +85,26 @@ describe("EmbeddedImports", () => {
85
85
  ]);
86
86
  });
87
87
 
88
+ test("an importer that keeps matched content as written is not asked to import it, and its reason is the warning", () => {
89
+ let imported = 0;
90
+ const resolver = new EmbeddedImports([
91
+ {
92
+ lexicon: "acme",
93
+ importer: {
94
+ ...widgets,
95
+ keepsAsWritten: (c) => ((c.document as { size?: number }).size === 3 ? "a size-3 widget builds back as size 4" : undefined),
96
+ import: (c) => (imported++, widgets.import(c)),
97
+ },
98
+ },
99
+ ]);
100
+ expect(resolver.resolve(site())).toBeUndefined();
101
+ expect(imported).toBe(0);
102
+ expect(resolver.files).toEqual([]);
103
+ expect(resolver.warnings).toEqual(['ConfigMap agent data["config.yaml"] is a widget, kept as written: a size-3 widget builds back as size 4']);
104
+ expect(isEmbeddedReference(resolver.resolve(site({ text: "kind: widget\nsize: 2\n" })))).toBe(true);
105
+ expect(imported).toBe(1);
106
+ });
107
+
88
108
  test("when two lexicons match, the first imports it and the other is named", () => {
89
109
  const resolver = new EmbeddedImports([
90
110
  { lexicon: "acme", importer: widgets },
@@ -102,6 +102,13 @@ export interface EmbeddedContentImporter {
102
102
  readonly what: string;
103
103
  /** Whether this content is one it imports. Must not throw on content that is not. */
104
104
  matches(content: EmbeddedContent): boolean;
105
+ /**
106
+ * Why content it matches is kept as written instead of imported, or
107
+ * `undefined` to import it. Asked before `import`; the reason is the
108
+ * warning's. grafana keeps a v2 dashboard, which it reads but would build
109
+ * back as v1 JSON (#3031).
110
+ */
111
+ keepsAsWritten?(content: EmbeddedContent): string | undefined;
105
112
  /** Import it. A throw keeps the content as written, with a warning. */
106
113
  import(content: EmbeddedContent): EmbeddedImport;
107
114
  }
@@ -233,6 +240,12 @@ export class EmbeddedImports implements EmbeddedContentResolver {
233
240
  );
234
241
  }
235
242
 
243
+ const kept = chosen.importer.keepsAsWritten?.(content);
244
+ if (kept !== undefined) {
245
+ this.warnings.push(`${content.location} is ${chosen.importer.what}, kept as written: ${kept}`);
246
+ return undefined;
247
+ }
248
+
236
249
  let result: EmbeddedImport;
237
250
  try {
238
251
  result = chosen.importer.import(content);
@@ -0,0 +1,46 @@
1
+ import type { LexiconPlugin } from "../lexicon";
2
+ import type { ParserOptions, ParserOptionSpec } from "./parser";
3
+
4
+ /**
5
+ * Check `--parser-option` entries (`key` or `key=value`) against what the
6
+ * lexicon declares in `parserOptions()` and convert each value to its declared
7
+ * type (#2994). Returns the options, or an error message naming what is wrong
8
+ * and what the lexicon accepts. An empty list is always valid.
9
+ */
10
+ export function resolveParserOptions(
11
+ plugin: Pick<LexiconPlugin, "name" | "parserOptions">,
12
+ entries: readonly string[] | undefined,
13
+ ): { options: ParserOptions } | { error: string } {
14
+ const options: ParserOptions = {};
15
+ if (!entries || entries.length === 0) return { options };
16
+
17
+ const specs: ParserOptionSpec[] = plugin.parserOptions?.() ?? [];
18
+ const accepted =
19
+ specs.length === 0
20
+ ? `lexicon "${plugin.name}" declares no parser options`
21
+ : `${plugin.name} accepts: ${specs.map((s) => `${s.name} (${s.type}): ${s.description}`).join("; ")}`;
22
+
23
+ for (const entry of entries) {
24
+ const eq = entry.indexOf("=");
25
+ const name = eq === -1 ? entry : entry.slice(0, eq);
26
+ const raw = eq === -1 ? undefined : entry.slice(eq + 1);
27
+ const spec = specs.find((s) => s.name === name);
28
+ if (!spec) {
29
+ return { error: `Unknown parser option "${name}" for lexicon "${plugin.name}"; ${accepted}.` };
30
+ }
31
+ if (spec.type === "boolean") {
32
+ if (raw === undefined || raw === "true") options[name] = true;
33
+ else if (raw === "false") options[name] = false;
34
+ else return { error: `Parser option "${name}" is a boolean: use ${name}, ${name}=true or ${name}=false, got "${raw}".` };
35
+ } else if (raw === undefined) {
36
+ return { error: `Parser option "${name}" needs a value: --parser-option ${name}=<${spec.type}>.` };
37
+ } else if (spec.type === "number") {
38
+ const n = Number(raw);
39
+ if (raw.trim() === "" || !Number.isFinite(n)) return { error: `Parser option "${name}" is a number, got "${raw}".` };
40
+ options[name] = n;
41
+ } else {
42
+ options[name] = raw;
43
+ }
44
+ }
45
+ return { options };
46
+ }
@@ -78,6 +78,26 @@ export interface ParseContext {
78
78
  readonly embedded?: EmbeddedContentResolver;
79
79
  }
80
80
 
81
+ /** A value a parser option takes. */
82
+ export type ParserOptionValue = string | number | boolean;
83
+
84
+ /** The options `chant import --parser-option` hands a lexicon's parser, by name. */
85
+ export type ParserOptions = Record<string, ParserOptionValue>;
86
+
87
+ /**
88
+ * One option a lexicon's parser accepts, declared by `LexiconPlugin.parserOptions()`.
89
+ * `chant import --parser-option <name>[=<value>]` is checked against this list and
90
+ * the value is converted to `type` before `templateParser(options)` sees it.
91
+ */
92
+ export interface ParserOptionSpec {
93
+ /** The key after `--parser-option`, e.g. "acceptLossyV1". */
94
+ name: string;
95
+ /** `boolean` takes `true`/`false`, and a bare `--parser-option name` means true. */
96
+ type: "boolean" | "string" | "number";
97
+ /** One line, shown when the option is refused and in the docs. */
98
+ description: string;
99
+ }
100
+
81
101
  /**
82
102
  * Interface for template parsers that convert external formats to IR
83
103
  */
package/src/lexicon.ts CHANGED
@@ -3,7 +3,7 @@ import type { Declarable } from "./declarable";
3
3
  import type { LintRule } from "./lint/rule";
4
4
  import type { RuleSpec } from "./lint/declarative";
5
5
  import type { PostSynthCheck } from "./lint/post-synth";
6
- import type { TemplateParser, TemplateIR } from "./import/parser";
6
+ import type { TemplateParser, TemplateIR, ParseContext, ParserOptions, ParserOptionSpec } from "./import/parser";
7
7
  import type { TypeScriptGenerator } from "./import/generator";
8
8
  import type { EmbeddedContentImporter } from "./import/embedded";
9
9
  import type { AgentConfigImporter } from "./agents/importer";
@@ -426,12 +426,26 @@ export interface InitTemplateSet {
426
426
  * repos or version-constant locations. See ./codegen/pinned-upgrade.ts.
427
427
  */
428
428
  export interface UpstreamPin {
429
+ /**
430
+ * Short name for this pin (`clickhouse`, `postgres-17`), used to pick one
431
+ * pin of a plugin that declares several (`chant dev pinned-upgrade <dir>
432
+ * <label>`). Optional on a lone `upstreamPin`, where it defaults to the
433
+ * lexicon's name; required on every entry of `upstreamPins`.
434
+ */
435
+ readonly label?: string;
429
436
  /** Source file (relative to the lexicon package root) holding the pinned version constant. */
430
437
  readonly file: string;
431
438
  /** Regex whose first capture group is the current pinned version in `file`. */
432
439
  readonly pattern: RegExp;
433
440
  /** Rebuild a line that matched `pattern`, substituting the new version. */
434
441
  replace(newVersion: string, line: string): string;
442
+ /**
443
+ * Set when the version line is not the whole pin: other constants (an image
444
+ * digest, a checksum) move with it and `replace` cannot compute them. The
445
+ * upgrade tooling then reports the newer version and this text, and edits
446
+ * nothing. Say what to move and how.
447
+ */
448
+ readonly alsoMoves?: string;
435
449
  /** Where to query for the latest stable upstream tag. */
436
450
  readonly upstream: {
437
451
  readonly owner: string;
@@ -440,6 +454,25 @@ export interface UpstreamPin {
440
454
  readonly kind: "releases" | "tags";
441
455
  /** Only consider tags ending with this suffix (e.g. "-ee" for GitLab). */
442
456
  readonly tagSuffix?: string;
457
+ /**
458
+ * Map a raw tag to the version string the pin stores, or null to skip the
459
+ * tag. For upstreams whose tags are not versions: postgres/postgres tags
460
+ * are `REL_18_6`, `REL_18_BETA1`, `REL9_6_24`, so
461
+ * `(t) => /^REL_(\d+)_(\d+)$/.exec(t)?.slice(1).join(".") ?? null` gives
462
+ * `18.6` and drops betas, release candidates and the old spelling.
463
+ *
464
+ * When set, the tool reads every page of the tag list (not just the first
465
+ * 50), because such lists are not ordered by version, and the version this
466
+ * returns is what `replace` receives and what the report shows. The pinned
467
+ * constant may hold either the mapped version or the raw tag.
468
+ */
469
+ readonly tagVersion?: (tag: string) => string | null;
470
+ /**
471
+ * Only consider versions whose first numeric segment equals the pinned
472
+ * version's, so a pin on `17.9` reports `17.11` and never `18.6`. For
473
+ * lexicons that pin one snapshot per major line. Default false.
474
+ */
475
+ readonly trackMajor?: boolean;
443
476
  };
444
477
  }
445
478
 
@@ -1160,8 +1193,19 @@ export interface LexiconPlugin {
1160
1193
  */
1161
1194
  detectTemplate?(data: unknown): boolean;
1162
1195
 
1163
- /** Return a parser for importing external templates into IR */
1164
- templateParser?(): TemplateParser;
1196
+ /**
1197
+ * Return a parser for importing external templates into IR. `options` holds
1198
+ * the `chant import --parser-option` values, already checked against
1199
+ * `parserOptions()` and converted to their declared types; it is empty when
1200
+ * none were given.
1201
+ */
1202
+ templateParser?(options?: ParserOptions): TemplateParser;
1203
+
1204
+ /**
1205
+ * The options `templateParser` accepts. `chant import --parser-option` refuses
1206
+ * any name not listed here, so a lexicon with no options leaves this out.
1207
+ */
1208
+ parserOptions?(): ParserOptionSpec[];
1165
1209
 
1166
1210
  /** Return a generator for converting IR to TypeScript */
1167
1211
  templateGenerator?(): TypeScriptGenerator;
@@ -1200,6 +1244,14 @@ export interface LexiconPlugin {
1200
1244
  /** How this lexicon pins its upstream schema version + where to check for a newer one (self-upgrade tooling — see ./codegen/pinned-upgrade.ts). Omit for lexicons with no pinned upstream. */
1201
1245
  readonly upstreamPin?: UpstreamPin;
1202
1246
 
1247
+ /**
1248
+ * Further pins, for a lexicon that pins more than one upstream (sql: the
1249
+ * ClickHouse pin in `upstreamPin` plus one Postgres server per major). Each
1250
+ * entry needs a unique `label`. The upgrade tooling checks `upstreamPin` and
1251
+ * every entry here, one report per pin. Omit when there is one pin.
1252
+ */
1253
+ readonly upstreamPins?: readonly (UpstreamPin & { readonly label: string })[];
1254
+
1203
1255
  /**
1204
1256
  * Generate a CI pipeline from the discovered component graph — the
1205
1257
  * `chant build --components --generate <name>` seam. Only CI-provider
@@ -1259,6 +1311,22 @@ export interface LexiconPlugin {
1259
1311
  */
1260
1312
  buildRoots?(ctx: BuildRootContext): Promise<BuildRootContribution>;
1261
1313
 
1314
+ /**
1315
+ * Facts about the whole read that this lexicon's entities imply, for the
1316
+ * graph IR's `meta` bag (#2559). The otel lexicon answers
1317
+ * `{ collector: <topology> }`, which `chant workspace graph` lifts into its
1318
+ * `collectors` section, so core carries the shape of the answer and never
1319
+ * imports the lexicon that computes it.
1320
+ *
1321
+ * Called by `chant graph --format ir` with the discovered entities of the
1322
+ * project, after the plugins are loaded. Return `undefined` or an empty
1323
+ * object when the project declares nothing this lexicon reports; the IR is
1324
+ * then unchanged. Keys must be unique to the lexicon (two lexicons writing
1325
+ * one key is a conflict the graph refuses). Omit for lexicons with nothing
1326
+ * to report.
1327
+ */
1328
+ graphMeta?(entities: ReadonlyMap<string, Declarable>): Record<string, unknown> | undefined;
1329
+
1262
1330
  // LSP
1263
1331
  /** Provide completions for LSP */
1264
1332
  completionProvider?(ctx: CompletionContext): CompletionItem[];
@@ -1989,6 +2057,14 @@ export interface ResourceSelector {
1989
2057
  export type ExportedTemplate = TemplateIR & {
1990
2058
  /** Phantom marker — never present at runtime. */
1991
2059
  readonly __fidelity?: "full-config";
2060
+ /**
2061
+ * The same export parsed again with a {@link ParseContext}, from the
2062
+ * objects already read: no second read of the live target. An exporter
2063
+ * that maps through its import parser offers this so `chant import --from`
2064
+ * can hand embedded content to the lexicon that owns it, as file import
2065
+ * does (#2995). Without it, embedded content is kept as written.
2066
+ */
2067
+ readonly reparse?: (context: ParseContext) => TemplateIR;
1992
2068
  };
1993
2069
 
1994
2070
  /**
@@ -298,6 +298,20 @@ describe("latestResolutionForPlan (#2300)", () => {
298
298
  expect(found.mismatched).toBeUndefined();
299
299
  });
300
300
 
301
+ // #2547: plan digests carry a version prefix now. An approval written under
302
+ // the bare prefix is still an approval of the same plan.
303
+ test("a resolution recorded under the bare sha256: prefix answers the same plan's jcs1 digest", () => {
304
+ const found = latestResolutionForPlan([resolution({ planDigest: PLAN_A })], "approve-live-apply", EPOCH, `jcs1-${PLAN_A}`);
305
+ expect(found.resolution?.resolvedBy).toBe("alex");
306
+ expect(found.mismatched).toBeUndefined();
307
+ });
308
+
309
+ test("the prefix never makes a different plan match", () => {
310
+ const found = latestResolutionForPlan([resolution({ planDigest: PLAN_B })], "approve-live-apply", EPOCH, `jcs1-${PLAN_A}`);
311
+ expect(found.resolution).toBeUndefined();
312
+ expect(found.mismatched?.planDigest).toBe(PLAN_B);
313
+ });
314
+
301
315
  test("a resolution for another plan does not, and comes back named", () => {
302
316
  const found = latestResolutionForPlan([resolution({ planDigest: PLAN_B })], "approve-live-apply", EPOCH, PLAN_A);
303
317
  expect(found.resolution).toBeUndefined();
@@ -41,6 +41,7 @@
41
41
  * (absent on the resolution lines written before #2119, which is why
42
42
  * `"resolution"` is the default reading).
43
43
  */
44
+ import { samePlanDigest } from "./plan-digest";
44
45
  import { sortedJsonReplacer } from "../utils";
45
46
  import { currentGateOrigin, type GateOrigin } from "./gate-origin";
46
47
  import type { GateApprover, GatePolicyDecision, ResolvedGateApproval } from "../op/gate-approval";
@@ -186,6 +187,22 @@ export interface GateResolutionRecord {
186
187
  * was recorded. Present only when the gate declared a policy.
187
188
  */
188
189
  policyDecision?: GatePolicyDecision;
190
+ /**
191
+ * An ssh signature over this approval by `resolvedBy` (#3163, ws-080):
192
+ * `chant approve --sign`, in the `chant-gate` namespace over the op, gate,
193
+ * environment, plan digest, approver and timestamp. It attests the approver
194
+ * only when the signers file at base lists a key for them. A gate the
195
+ * declaration's `identity.gates` names counts only approvals whose seal
196
+ * verifies. Absent on an unsigned approval.
197
+ */
198
+ seal?: GateApprovalSeal;
199
+ }
200
+
201
+ /** A gate approval's seal (#3163): who signed, the key's fingerprint (reported, never trusted), and the armored signature. */
202
+ export interface GateApprovalSeal {
203
+ signer: string;
204
+ key: string;
205
+ signature: string;
189
206
  }
190
207
 
191
208
  export type GateResolutionInput = Omit<GateResolutionRecord, "version" | "kind">;
@@ -513,7 +530,7 @@ export function latestResolutionForPlan(
513
530
  if (new Date(r.timestamp).getTime() < since) continue;
514
531
  const newest = (best: GateResolutionRecord | undefined) =>
515
532
  !best || new Date(r.timestamp).getTime() >= new Date(best.timestamp).getTime();
516
- if (r.planDigest === planDigest) {
533
+ if (samePlanDigest(r.planDigest, planDigest)) {
517
534
  if (newest(matched)) matched = r;
518
535
  } else if (newest(mismatched)) {
519
536
  mismatched = r;
@@ -97,6 +97,24 @@ describe("observeResources", () => {
97
97
  expect(names).toEqual(["web-vpc"]);
98
98
  });
99
99
 
100
+ it("keeps property-kind declarables out of the observation universe (#3001)", async () => {
101
+ // A property-kind declarable (a grafana panel, an aws SecurityGroup_Ingress
102
+ // const) carries props but lives only inside the resource holding it.
103
+ // Asked about on its own, a reader can only say "absent" or "not observed".
104
+ const buildResult = {
105
+ outputs: new Map<string, string>([["aws", "{}"]]),
106
+ entities: new Map<string, unknown>([
107
+ ["web-sg", { lexicon: "aws", entityType: "AWS::EC2::SecurityGroup", kind: "resource", props: {} }],
108
+ ["ingress", { lexicon: "aws", entityType: "AWS::EC2::SecurityGroup.Ingress", kind: "property", props: { FromPort: 443 } }],
109
+ ]),
110
+ errors: [],
111
+ } as unknown as BuildResult;
112
+ let names: string[] = [];
113
+ const plugins = [awsPlugin(({ entityNames }) => { names = entityNames; return {}; })];
114
+ await observeResources("prod", plugins, buildResult);
115
+ expect(names).toEqual(["web-sg"]);
116
+ });
117
+
100
118
  it("collects a throwing plugin into errors instead of failing the whole graph, and reports its entities unobserved (#1089)", async () => {
101
119
  const plugins = [
102
120
  awsPlugin(() => { throw new Error("access denied"); }),
@@ -25,7 +25,7 @@ import {
25
25
  } from "../observation";
26
26
  import { zeroResourcesWarning } from "../live-endpoint";
27
27
  import { unqualifiedKey } from "./identity";
28
- import { isResourceDeclarable } from "../declarable";
28
+ import { isObservableDeclarable } from "../declarable";
29
29
 
30
30
  export interface ObserveResult {
31
31
  observations: LiveObservation[];
@@ -150,8 +150,10 @@ export async function observeResources(
150
150
  // parameters and serializer directives (gcp's `defaultAnnotations`) are
151
151
  // build-time inputs — declared, but with nothing in any cloud to compare
152
152
  // against, so keeping them in the universe makes every diff report a
153
- // hole (or worse, a deletion) for a resource that cannot exist.
154
- if (!isResourceDeclarable(entity)) continue;
153
+ // hole (or worse, a deletion) for a resource that cannot exist. A
154
+ // property-kind declarable is read as part of the resource holding it,
155
+ // never on its own (#3001).
156
+ if (!isObservableDeclarable(entity)) continue;
155
157
  entityNames.push(name);
156
158
  entities.set(name, {
157
159
  entityType: entity.entityType,
@@ -174,7 +176,7 @@ export async function observeResources(
174
176
  stackEntityNames = [];
175
177
  stackEntities = new Map();
176
178
  for (const [name, entity] of sb.entities) {
177
- if (entity.lexicon !== plugin.name || !isResourceDeclarable(entity)) continue;
179
+ if (entity.lexicon !== plugin.name || !isObservableDeclarable(entity)) continue;
178
180
  stackEntityNames.push(name);
179
181
  stackEntities.set(name, {
180
182
  entityType: entity.entityType,