@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
@@ -97,6 +97,19 @@ function makeBuildResult(entitiesByLexicon: Record<string, string[]>): BuildResu
97
97
  } as unknown as BuildResult;
98
98
  }
99
99
 
100
+ /**
101
+ * #3001 — a build holding one resource and one property-kind declarable that
102
+ * the resource inlines, the shape every grafana dashboard with exported panels
103
+ * has. Only the resource has a live counterpart of its own.
104
+ */
105
+ function buildWithPropertyKind(lexicon: string): BuildResult {
106
+ const build = makeBuildResult({ [lexicon]: ["board"] });
107
+ const panel = { lexicon, entityType: `${lexicon}::Panel`, kind: "property", props: { title: "CPU" } };
108
+ build.entities.set("board", { lexicon, entityType: `${lexicon}::Board`, kind: "resource", props: { panels: [panel] } } as never);
109
+ build.entities.set("cpuPanel", panel as never);
110
+ return build;
111
+ }
112
+
100
113
  const meta = (overrides: Partial<ResourceMetadata> = {}): ResourceMetadata => ({
101
114
  type: "AWS::S3::Bucket",
102
115
  status: "CREATE_COMPLETE",
@@ -537,6 +550,37 @@ describe("runLifecycleDiff --live", () => {
537
550
  });
538
551
 
539
552
  // #1014 — property-level drift, gated purely on the deep capability.
553
+ test("hands only resource-kind entities to describeResources and observeResourcesDeep (#3001)", async () => {
554
+ buildMock.mockResolvedValue(buildWithPropertyKind("graf"));
555
+ fetchLifecycleMock.mockResolvedValue(undefined);
556
+ readSnapshotMock.mockResolvedValue(null);
557
+ const seen: { thin?: string[]; deep?: string[] } = {};
558
+ const plugins: LexiconPlugin[] = [
559
+ createMockPlugin({
560
+ name: "graf",
561
+ describeResources: async (opts) => {
562
+ seen.thin = [...opts.entities.keys()];
563
+ return { board: meta({ type: "graf::Board", physicalId: "uid-1" }) };
564
+ },
565
+ observeResourcesDeep: async (opts) => {
566
+ seen.deep = [...opts.entities.keys()];
567
+ return deepObservation({ board: { type: "graf::Board", properties: { panels: [{ title: "CPU" }] } } });
568
+ },
569
+ }),
570
+ ];
571
+ const exit = await runLifecycleDiff({
572
+ args: makeArgs({ path: "diff", extraPositional: "prod", live: true }),
573
+ plugins,
574
+ serializers: plugins.map((p) => p.serializer),
575
+ });
576
+ expect(exit).toBe(0);
577
+ expect(seen.thin).toEqual(["board"]);
578
+ expect(seen.deep).toEqual(["board"]);
579
+ const stdout = stdoutBuf.join("\n");
580
+ expect(stdout).not.toContain("cpuPanel");
581
+ expect(stdout).toContain("0 missing");
582
+ });
583
+
540
584
  describe("deep observation (#1014)", () => {
541
585
  const withDeep = (over: Parameters<typeof createMockPlugin>[0] = {}) =>
542
586
  createMockPlugin({
@@ -872,6 +916,29 @@ describe("runLifecyclePlan", () => {
872
916
  expect(stdoutBuf.join("\n")).toContain("bucket");
873
917
  });
874
918
 
919
+ test("proposes no create for a property-kind declarable its parent inlines (#3001)", async () => {
920
+ buildMock.mockResolvedValue(buildWithPropertyKind("graf"));
921
+ let names: string[] = [];
922
+ const plugins: LexiconPlugin[] = [
923
+ createMockPlugin({
924
+ name: "graf",
925
+ describeResources: async (opts) => {
926
+ names = opts.entityNames;
927
+ return { board: meta({ type: "graf::Board", physicalId: "uid-1" }) };
928
+ },
929
+ }),
930
+ ];
931
+ const exit = await runLifecyclePlan({
932
+ args: makeArgs({ path: "plan", extraPositional: "prod", json: true }),
933
+ plugins,
934
+ serializers: plugins.map((p) => p.serializer),
935
+ });
936
+ expect(exit).toBe(0);
937
+ expect(names).toEqual(["board"]);
938
+ const plan = JSON.parse(stdoutBuf.join("\n"));
939
+ expect(plan.entries.map((e: { name: string }) => e.name)).not.toContain("cpuPanel");
940
+ });
941
+
875
942
  // #1983 — `--report markdown` emits the reviewer-facing projection instead
876
943
  // of the human render, same as `--report gitlab-mr` swaps in the widget JSON.
877
944
  test("--report markdown emits the markdown projection instead of the human render", async () => {
@@ -42,7 +42,7 @@ import { unknownEnvError, isProdLikeEnvironment } from "../../env";
42
42
  import { planTeardown, executeTeardown, type TeardownPlan, type TeardownReport } from "../../lifecycle/teardown";
43
43
  import { collectBuildRootContributors } from "../plugins";
44
44
  import { applyLiveEndpoint } from "../../live-endpoint";
45
- import { isResourceDeclarable } from "../../declarable";
45
+ import { isObservableDeclarable } from "../../declarable";
46
46
  import { formatError, formatWarning, formatSuccess, formatBold } from "../format";
47
47
  import type { CommandContext } from "../registry";
48
48
  import type { LifecycleSnapshot } from "../../lifecycle/types";
@@ -812,13 +812,14 @@ async function runLifecycleDiffLive(args: LiveDiffArgs): Promise<LiveDiffOutcome
812
812
  }
813
813
 
814
814
  // Build per-lexicon entity index. Resource declarables only — outputs,
815
- // parameters and serializer directives have no live counterpart, and a
815
+ // parameters, serializer directives and property-kind declarables (read
816
+ // inside their parent, #3001) have no live counterpart of their own, and a
816
817
  // declared name the reader can never resolve reads as missing (see
817
818
  // lifecycle/observe.ts).
818
819
  const declared = new Set<string>();
819
820
  const entities: DeclaredEntities = new Map();
820
821
  for (const [name, entity] of args.buildResult.entities) {
821
- if (entity.lexicon === lexiconName && isResourceDeclarable(entity)) {
822
+ if (entity.lexicon === lexiconName && isObservableDeclarable(entity)) {
822
823
  declared.add(name);
823
824
  // Path origins are carried across explicitly (#1443): this map is plain
824
825
  // objects, and the symbol-keyed provenance channel does not survive it.
@@ -1289,11 +1290,11 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
1289
1290
  // A receipt has no `props` payload of its own but is declared, diffed,
1290
1291
  // and observed like any resource (#1832) — it joins the declared axis
1291
1292
  // so its lexicon's observation can confirm presence or absence.
1292
- if (entity.lexicon === lexiconName && (isResourceDeclarable(entity) || isEffectReceipt(entity))) {
1293
+ if (entity.lexicon === lexiconName && (isObservableDeclarable(entity) || isEffectReceipt(entity))) {
1293
1294
  declared.add(name);
1294
1295
  entities.set(name, {
1295
1296
  entityType: entity.entityType,
1296
- props: (isResourceDeclarable(entity) && entity.props != null ? entity.props : {}) as Record<string, unknown>,
1297
+ props: (isObservableDeclarable(entity) && entity.props != null ? entity.props : {}) as Record<string, unknown>,
1297
1298
  });
1298
1299
  }
1299
1300
  }
@@ -324,6 +324,7 @@ export async function runImport(ctx: CommandContext): Promise<number> {
324
324
  lexicon: "k8s",
325
325
  output: args.output,
326
326
  force: args.force,
327
+ parserOptions: args.parserOption,
327
328
  });
328
329
  printImportResult(result);
329
330
  return result.success ? 0 : 1;
@@ -381,6 +382,7 @@ export async function runImport(ctx: CommandContext): Promise<number> {
381
382
  output: ctx.args.output,
382
383
  force: ctx.args.force,
383
384
  lexicon: ctx.args.lexicon,
385
+ parserOptions: ctx.args.parserOption,
384
386
  });
385
387
 
386
388
  printImportResult(result);
@@ -9,7 +9,7 @@
9
9
  import { lexiconNames } from "../../lexicon-module";
10
10
  import { loadChantConfig } from "../../config";
11
11
  import { build } from "../../build";
12
- import { isResourceDeclarable } from "../../declarable";
12
+ import { isObservableDeclarable } from "../../declarable";
13
13
  import { collectBuildRootContributors, collectChangeSubscribers } from "../plugins";
14
14
  import { discoverOps, discoverStewards } from "../../op/discover";
15
15
  import { pickSteward, stewardBesideOf, stewardFormFor, stewardLeaseName, stewardTurnOps, DEFAULT_STEWARD_ENV } from "../../op/steward";
@@ -116,7 +116,7 @@ async function collectOperatorSubscribers(
116
116
  // subscription may watch.
117
117
  const entities = new Map<string, Map<string, { entityType: string; props: Record<string, unknown> }>>();
118
118
  for (const [name, entity] of buildResult.entities) {
119
- if (!isResourceDeclarable(entity)) continue;
119
+ if (!isObservableDeclarable(entity)) continue;
120
120
  let perLexicon = entities.get(entity.lexicon);
121
121
  if (!perLexicon) entities.set(entity.lexicon, (perLexicon = new Map()));
122
122
  perLexicon.set(name, {
@@ -902,6 +902,7 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
902
902
  allowSameOrigin: ctx.args.allowSameOrigin,
903
903
  ...(ctx.args.roles ? { roles: ctx.args.roles } : {}),
904
904
  ...(ctx.args.agent ? { agent: true } : {}),
905
+ ...(ctx.args.sign !== undefined ? { sign: ctx.args.sign } : {}),
905
906
  });
906
907
  if (!outcome.ok) return 1;
907
908
 
@@ -959,6 +960,15 @@ export interface GateApprovalOptions {
959
960
  * model-authored channel it is one whether or not this is set.
960
961
  */
961
962
  agent?: boolean;
963
+ /**
964
+ * `--sign [<key file>]` (#3163, ws-080): seal the approval with an ssh key,
965
+ * a key file or true for git's `user.signingkey`. The seal attests the
966
+ * approver when the signers file at base lists the key for them, and a gate
967
+ * the declaration's `identity.gates` names counts only sealed approvals.
968
+ */
969
+ sign?: string | true;
970
+ /** Where the workspace and the key file are looked up from. Defaults to the process's directory. */
971
+ cwd?: string;
962
972
  /** Replaces the lexicon evaluator a gate policy is loaded from. For tests. */
963
973
  evaluator?: GatePolicyEvaluator;
964
974
  }
@@ -1047,7 +1057,7 @@ export async function recordGateApproval(
1047
1057
  if (opts.plan !== undefined) {
1048
1058
  if (!isPlanDigest(opts.plan)) {
1049
1059
  console.error(formatError({
1050
- message: `--plan must be a plan digest ("sha256:" and 64 hex characters), got "${opts.plan}"`,
1060
+ message: `--plan must be a plan digest ("jcs1-sha256:" or "sha256:", then 64 hex characters), got "${opts.plan}"`,
1051
1061
  hint: "Copy it from the gated run's `plan :` line, or from the pending-gate summary. It is not a plan file path.",
1052
1062
  }));
1053
1063
  return { ok: false };
@@ -1107,6 +1117,78 @@ export async function recordGateApproval(
1107
1117
  ...(opts.roles && opts.roles.length > 0 ? { roles: [...new Set(opts.roles)] } : {}),
1108
1118
  };
1109
1119
 
1120
+ // #3163, ws-080: the declaration at base may require a checkable approver
1121
+ // (identity.attribution "identified") and a signed approval for this gate
1122
+ // (identity.gates). Both are judged before anything is written.
1123
+ const cwd = opts.cwd ?? process.cwd();
1124
+ const identity = await import("../../workspace/identity");
1125
+ const { scopeSource } = await import("../../workspace/write-scope");
1126
+ const scope = scopeSource(cwd);
1127
+ try {
1128
+ identity.refuseUnidentified(scope, [resolvedBy], opts.actor !== undefined ? "--actor" : "the approver");
1129
+ } catch (err) {
1130
+ if (!(err instanceof identity.IdentityError)) throw err;
1131
+ console.error(formatError({
1132
+ message: `Gate "${gate}" on "${opName}" was not approved: ${err.message}`,
1133
+ hint: "Pass --actor <forge identity or signer>, as the surface that signed the person in maps them, for example --actor github:<login>.",
1134
+ }));
1135
+ return { ok: false };
1136
+ }
1137
+ const rule = identity.gateAdmissionFrom(scope, gate);
1138
+ if (rule && opts.sign === undefined) {
1139
+ console.error(formatError({
1140
+ message:
1141
+ `Gate "${gate}" needs a signed approval: chant.workspace.json at base names it in identity.gates` +
1142
+ (rule.requirement.class !== null ? `, from the class ${rule.requirement.class}` : "") +
1143
+ `, and this approval is not signed`,
1144
+ hint: `chant approve ${opName} ${gate} --actor <principal> --sign [<key file>], with a key ${scope.policy.signersPath} at base lists for that principal.`,
1145
+ }));
1146
+ return { ok: false };
1147
+ }
1148
+ const timestamp = new Date().toISOString();
1149
+ let seal: GateResolutionRecord["seal"];
1150
+ if (opts.sign !== undefined) {
1151
+ const { resolveSigningKey, sealGateApproval, SealError } = await import("../../workspace/trust/seal");
1152
+ try {
1153
+ const key = resolveSigningKey(opts.sign, cwd);
1154
+ try {
1155
+ seal = sealGateApproval(key.file, {
1156
+ op: opName,
1157
+ gate,
1158
+ ...(environment !== undefined ? { environment } : {}),
1159
+ ...(planDigest !== undefined ? { planDigest } : {}),
1160
+ resolvedBy,
1161
+ timestamp,
1162
+ });
1163
+ } finally {
1164
+ key.cleanup();
1165
+ }
1166
+ } catch (err) {
1167
+ if (!(err instanceof SealError)) throw err;
1168
+ console.error(formatError({ message: `Gate "${gate}" on "${opName}" was not approved: ${err.message}` }));
1169
+ return { ok: false };
1170
+ }
1171
+ }
1172
+ if (rule) {
1173
+ const why = rule.refuses({
1174
+ op: opName,
1175
+ gate,
1176
+ ...(environment !== undefined ? { environment } : {}),
1177
+ ...(planDigest !== undefined ? { planDigest } : {}),
1178
+ resolvedBy,
1179
+ timestamp,
1180
+ seal,
1181
+ });
1182
+ if (why !== null) {
1183
+ console.error(formatError({
1184
+ message: `Gate "${gate}" on "${opName}" was not approved: ${why}`,
1185
+ hint: `identity.gates.${gate} in chant.workspace.json at base counts only an approval signed by a key ${scope.policy.signersPath} lists for the approver` +
1186
+ (rule.requirement.class !== null ? `, who holds the role of the class ${rule.requirement.class}` : "") + ".",
1187
+ }));
1188
+ return { ok: false };
1189
+ }
1190
+ }
1191
+
1110
1192
  // #2508: a gate with a policy has it evaluated for this approval now, while
1111
1193
  // the approver is known, against the context the gated run recorded. The
1112
1194
  // decision is written next to the approval, so a pass can be traced to the
@@ -1148,7 +1230,8 @@ export async function recordGateApproval(
1148
1230
  op: opName,
1149
1231
  gate,
1150
1232
  resolvedBy,
1151
- timestamp: new Date().toISOString(),
1233
+ timestamp,
1234
+ ...(seal ? { seal } : {}),
1152
1235
  approver,
1153
1236
  ...(policyDecision ? { policyDecision } : {}),
1154
1237
  ...(opts.note ? { note: opts.note } : {}),
@@ -1166,6 +1249,7 @@ export async function recordGateApproval(
1166
1249
  console.error(formatSuccess(
1167
1250
  `Gate "${gate}" on "${opName}" resolved by ${record.resolvedBy} at ${record.timestamp}` +
1168
1251
  (record.url ? ` (${record.url})` : "") +
1252
+ (record.seal ? `, signed with ${record.seal.key}` : "") +
1169
1253
  (pushed ? "" : " (local only — the push did not land)"),
1170
1254
  ));
1171
1255
  if (record.planDigest) {
@@ -424,6 +424,30 @@ describe("runOp: a steward's turn (#2750)", () => {
424
424
  expect(acquireStewardTurnMock).not.toHaveBeenCalled();
425
425
  });
426
426
 
427
+ test("fountain form with run.on: fountain and no flag: runs on fountain like --on fountain (#2523)", async () => {
428
+ const runtime = {
429
+ name: "fountain",
430
+ start: vi.fn(async (op: { name: string }) => ({
431
+ op: op.name,
432
+ runId: "f-1",
433
+ result: async () => ({ op: op.name, runId: "f-1", state: "completed" as const, startedAt: "t", endedAt: "t" }),
434
+ })),
435
+ status: vi.fn(), log: vi.fn(), list: vi.fn(), cancel: vi.fn(),
436
+ };
437
+ discoverOpsMock.mockResolvedValue({ ops: new Map([localOp("release", [])]), errors: [] });
438
+ discoverStewardsMock.mockResolvedValueOnce(stewardOwning("release", "box-steward", "fountain"));
439
+ loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["fountain"], run: { on: "fountain" } } });
440
+ makeStdoutSpy();
441
+ const exit = await runOp({
442
+ args: makeArgs({ path: "release" }),
443
+ plugins: [{ name: "fountain", opRuntime: runtime } as never], serializers: [],
444
+ });
445
+
446
+ expect(exit).toBe(0);
447
+ expect(runtime.start).toHaveBeenCalledTimes(1);
448
+ expect(acquireStewardTurnMock).not.toHaveBeenCalled();
449
+ });
450
+
427
451
  test("local form, the turn lease's own acquire throws (a stale .lock): refused as a lease error, not an uncaught exception, and nothing runs", async () => {
428
452
  discoverOpsMock.mockResolvedValue({
429
453
  ops: new Map([localOp("release", [{ kind: "activity", fn: "shellCmd", args: { cmd: "true" } }])]),
@@ -899,6 +923,93 @@ describe("run subcommands on the resolved runtime", () => {
899
923
  expect(stderr.join("\n")).toContain('lexicon "plain" does not host Op runs');
900
924
  });
901
925
 
926
+ test("run.on in chant.config.ts is the default runtime when --on is not given (#2523)", async () => {
927
+ const runtime = makeStubRuntime();
928
+ discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
929
+ loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub"], run: { on: "stub" } } });
930
+ makeStdoutSpy();
931
+ makeStderrSpy();
932
+ const ctx = (over: Partial<ParsedArgs>) => ({ args: makeArgs(over), plugins: [stubPlugin(runtime)], serializers: [] });
933
+
934
+ expect(await runOp(ctx({ path: "hello" }))).toBe(0);
935
+ expect(await runOpStatus(ctx({ extraPositional: "hello" }))).toBe(0);
936
+ expect(await runOpLog(ctx({ extraPositional: "hello" }))).toBe(0);
937
+ expect(await runOpList(ctx({}))).toBe(0);
938
+ expect(runtime.start).toHaveBeenCalledTimes(1);
939
+ expect(runtime.status).toHaveBeenCalledWith("hello");
940
+ expect(runtime.log).toHaveBeenCalledTimes(1);
941
+ expect(runtime.list).toHaveBeenCalledTimes(1);
942
+ });
943
+
944
+ test("--on local overrides run.on and runs on the built-in runtime (#2523)", async () => {
945
+ const runtime = makeStubRuntime();
946
+ discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
947
+ loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub"], run: { on: "stub" } } });
948
+ makeStdoutSpy();
949
+ makeStderrSpy();
950
+ await runOpList({ args: makeArgs({ on: "local" }), plugins: [stubPlugin(runtime)], serializers: [] });
951
+ expect(runtime.list).not.toHaveBeenCalled();
952
+ });
953
+
954
+ test("run.on naming an unconfigured lexicon gets --on's refusal, naming run.on (#2523)", async () => {
955
+ discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
956
+ loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["aws"], run: { on: "nope" } } });
957
+ const stderr = makeStderrSpy();
958
+ const exit = await runOp({ args: makeArgs({ path: "hello" }), plugins: [], serializers: [] });
959
+ expect(exit).toBe(1);
960
+ expect(stderr.join("\n")).toContain('run.on "nope" in chant.config.ts: "nope" is not a configured lexicon');
961
+ expect(stderr.join("\n")).toContain("Configured lexicons: aws");
962
+ expect(stderr.join("\n")).toContain("--on local");
963
+ });
964
+
965
+ test("an explicit --on wins over run.on (#2523)", async () => {
966
+ const chosen = makeStubRuntime();
967
+ const other = makeStubRuntime();
968
+ discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
969
+ loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub", "other"], run: { on: "other" } } });
970
+ makeStdoutSpy();
971
+ makeStderrSpy();
972
+ const plugins = [stubPlugin(chosen), { name: "other", opRuntime: other } as never];
973
+ expect(await runOpList({ args: makeArgs({ on: "stub" }), plugins, serializers: [] })).toBe(0);
974
+ expect(chosen.list).toHaveBeenCalledTimes(1);
975
+ expect(other.list).not.toHaveBeenCalled();
976
+ });
977
+
978
+ test("run.on: local selects the built-in runtime (#2523)", async () => {
979
+ const runtime = makeStubRuntime();
980
+ discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
981
+ loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub"], run: { on: "local" } } });
982
+ makeStdoutSpy();
983
+ makeStderrSpy();
984
+ await runOpList({ args: makeArgs({}), plugins: [stubPlugin(runtime)], serializers: [] });
985
+ expect(runtime.list).not.toHaveBeenCalled();
986
+ });
987
+
988
+ test("an invalid run block is refused, not run locally (#2523)", async () => {
989
+ const { InvalidChantConfigError } = await vi.importActual<typeof import("../../config")>("../../config");
990
+ const runtime = makeStubRuntime();
991
+ discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
992
+ loadChantConfigMock.mockRejectedValue(
993
+ new InvalidChantConfigError("Invalid chant config in /p/chant.config.ts: run.on: Expected string, received number", "run"),
994
+ );
995
+ const stderr = makeStderrSpy();
996
+ makeStdoutSpy();
997
+ expect(await runOpList({ args: makeArgs({}), plugins: [stubPlugin(runtime)], serializers: [] })).toBe(1);
998
+ expect(stderr.join("\n")).toContain("Invalid chant config in /p/chant.config.ts: run.on");
999
+ expect(runtime.list).not.toHaveBeenCalled();
1000
+ // --on local still runs: the flag needs nothing from the config.
1001
+ expect(await runOpList({ args: makeArgs({ on: "local" }), plugins: [stubPlugin(runtime)], serializers: [] })).toBe(0);
1002
+ });
1003
+
1004
+ test("run.on naming a lexicon with no opRuntime gets --on's refusal, naming run.on (#2523)", async () => {
1005
+ discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
1006
+ loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["plain"], run: { on: "plain" } } });
1007
+ const stderr = makeStderrSpy();
1008
+ const exit = await runOp({ args: makeArgs({ path: "hello" }), plugins: [{ name: "plain" } as never], serializers: [] });
1009
+ expect(exit).toBe(1);
1010
+ expect(stderr.join("\n")).toContain('run.on "plain" in chant.config.ts: lexicon "plain" does not host Op runs');
1011
+ });
1012
+
902
1013
  test("--components on a runtime that cannot host them → one line, exit 1", async () => {
903
1014
  const runtime = makeStubRuntime();
904
1015
  const stderr = makeStderrSpy();
@@ -1,7 +1,7 @@
1
1
  import { lexiconNames } from "../../lexicon-module";
2
- import { resolve, dirname } from "node:path";
2
+ import { resolve, dirname, basename } from "node:path";
3
3
  import { writeFileSync, mkdirSync, readFileSync } from "node:fs";
4
- import { loadChantConfig, resolveAutoReleaseDisabled, type ChantConfig } from "../../config";
4
+ import { loadChantConfig, resolveAutoReleaseDisabled, InvalidChantConfigError, type ChantConfig } from "../../config";
5
5
  import { discoverOps, discoverStewards } from "../../op/discover";
6
6
  import { stewardWorkHolder } from "../../op/work-lease-run";
7
7
  import type { OpConfig } from "../../op/types";
@@ -108,10 +108,12 @@ function refuseDurableComponentSubcommand(what: string, hint: string): number {
108
108
  /**
109
109
  * Resolve the one runtime this invocation talks to.
110
110
  *
111
- * `--on <name>` picks the named lexicon's `opRuntime` (`../../lexicon.ts`);
112
- * without it, core's built-in `local` provider runs the Op in this process.
113
- * Every `chant run` subcommand goes through whichever comes back, so there is
114
- * one dispatch path rather than a branch per runtime.
111
+ * `--on <name>` picks the named lexicon's `opRuntime` (`../../lexicon.ts`).
112
+ * Without it, `run.on` in `chant.config.ts` does (#2523), and without either,
113
+ * core's built-in `local` provider runs the Op in this process. `local`, from
114
+ * either place, names the built-in provider. Every `chant run` subcommand goes
115
+ * through whichever comes back, so there is one dispatch path rather than a
116
+ * branch per runtime.
115
117
  *
116
118
  * A lexicon already loaded into the command context wins the lookup — that is
117
119
  * how a test hands in a stub, and how a command that loaded plugins for its
@@ -121,22 +123,20 @@ function refuseDurableComponentSubcommand(what: string, hint: string): number {
121
123
  *
122
124
  * Returns `undefined` after printing an actionable error — an unconfigured
123
125
  * name is answered with the configured list, a configured lexicon that hosts
124
- * nothing is named outright.
126
+ * nothing is named outright. A `run.on` default gets the same messages as
127
+ * `--on`, labelled `run.on` and the config file instead of `--on`.
125
128
  */
126
129
  async function resolveOpRuntime(ctx: CommandContext): Promise<OpRuntimeProvider | undefined> {
127
- const on = ctx.args.on;
130
+ const choice = await chooseOpRuntime(ctx);
131
+ if (!choice) return undefined;
132
+ const { on, source, configured } = choice;
128
133
  if (!on || on === "local") return createLocalOpRuntime({ projectPath: resolve(".") });
129
134
 
130
- // Read the configured list straight from `chant.config.ts` rather than
131
- // through `resolveProjectLexicons`: `--on` names a *configured* lexicon, and
132
- // that helper's fallback is a source scan of the whole project, which is a
133
- // long wait to be told a name is wrong.
134
- let configured: string[] = [];
135
- try {
136
- configured = lexiconNames((await loadChantConfig(resolve("."))).config.lexicons ?? []);
137
- } catch {
138
- // No/unreadable chant.config.ts — the error below says so by listing nothing.
139
- }
135
+ // How the error names the choice, and how to get the built-in runtime back.
136
+ const label = source === "flag" ? `--on ${on}` : `run.on "${on}" in ${choice.configFile}`;
137
+ const fallback = source === "flag"
138
+ ? "Omit --on to run on the built-in local runtime."
139
+ : `Fix \`run.on\` in ${choice.configFile}, or pass --on local to run on the built-in local runtime.`;
140
140
 
141
141
  let plugin = ctx.plugins.find((p) => p.name === on);
142
142
  if (!plugin && configured.includes(on)) {
@@ -146,18 +146,18 @@ async function resolveOpRuntime(ctx: CommandContext): Promise<OpRuntimeProvider
146
146
  if (!plugin) {
147
147
  const known = [...new Set([...ctx.plugins.map((p) => p.name), ...configured])];
148
148
  console.error(formatError({
149
- message: `--on ${on}: "${on}" is not a configured lexicon`,
149
+ message: `${label}: "${on}" is not a configured lexicon`,
150
150
  hint: known.length > 0
151
- ? `Configured lexicons: ${known.join(", ")}. Omit --on to run on the built-in local runtime.`
152
- : "chant.config.ts configures no lexicons. Omit --on to run on the built-in local runtime.",
151
+ ? `Configured lexicons: ${known.join(", ")}. ${fallback}`
152
+ : `chant.config.ts configures no lexicons. ${fallback}`,
153
153
  }));
154
154
  return undefined;
155
155
  }
156
156
 
157
157
  if (!plugin.opRuntime) {
158
158
  console.error(formatError({
159
- message: `--on ${on}: lexicon "${on}" does not host Op runs`,
160
- hint: "It declares no opRuntime. Omit --on to run on the built-in local runtime.",
159
+ message: `${label}: lexicon "${on}" does not host Op runs`,
160
+ hint: `It declares no opRuntime. ${fallback}`,
161
161
  }));
162
162
  return undefined;
163
163
  }
@@ -165,6 +165,64 @@ async function resolveOpRuntime(ctx: CommandContext): Promise<OpRuntimeProvider
165
165
  return plugin.opRuntime;
166
166
  }
167
167
 
168
+ /** Which runtime name an invocation asked for, and where the name came from. */
169
+ interface OpRuntimeChoice {
170
+ /** The runtime name; `undefined` or `"local"` is the built-in runtime. */
171
+ on: string | undefined;
172
+ /** `flag` for `--on`, `config` for `run.on`, `default` when neither named one. */
173
+ source: "flag" | "config" | "default";
174
+ /** The lexicons `chant.config.ts` configures. */
175
+ configured: string[];
176
+ /** The config file's name, for messages about `run.on`. */
177
+ configFile: string;
178
+ }
179
+
180
+ /**
181
+ * The runtime name `--on`, then `run.on` in `chant.config.ts` (#2523), then
182
+ * the built-in default pick, without loading any lexicon.
183
+ *
184
+ * Reads the configured list straight from `chant.config.ts` rather than
185
+ * through `resolveProjectLexicons`: `--on` names a *configured* lexicon, and
186
+ * that helper's fallback is a source scan of the whole project, which is a
187
+ * long wait to be told a name is wrong.
188
+ *
189
+ * A config that fails to load leaves the flag's choice alone, as before. With
190
+ * no flag, a config whose `run` block is invalid is refused (printed, then
191
+ * `undefined`): running the Op locally would hide that the project asked for
192
+ * another runtime. Any other load failure falls back to the built-in runtime,
193
+ * which is what a project with no `run.on` got before. `quiet` skips printing
194
+ * that refusal, for a caller that only peeks at the choice and leaves the
195
+ * reporting to {@link resolveOpRuntime}.
196
+ */
197
+ async function chooseOpRuntime(ctx: CommandContext, quiet = false): Promise<OpRuntimeChoice | undefined> {
198
+ const flag = ctx.args.on;
199
+ if (flag === "local") return { on: "local", source: "flag", configured: [], configFile: "chant.config.ts" };
200
+
201
+ let configured: string[] = [];
202
+ let configuredOn: string | undefined;
203
+ let configFile = "chant.config.ts";
204
+ try {
205
+ const { config, configPath } = await loadChantConfig(resolve("."));
206
+ configured = lexiconNames(config.lexicons ?? []);
207
+ configuredOn = config.run?.on;
208
+ if (configPath) configFile = basename(configPath);
209
+ } catch (err) {
210
+ if (!flag && err instanceof InvalidChantConfigError && err.key === "run") {
211
+ if (quiet) return undefined;
212
+ console.error(formatError({
213
+ message: err.message,
214
+ hint: "`run.on` names the runtime that hosts this project's Op runs: a configured lexicon with an opRuntime, or \"local\". Fix it, or pass --on to pick the runtime for this run.",
215
+ }));
216
+ return undefined;
217
+ }
218
+ // No/unreadable chant.config.ts: an `--on` error says so by listing nothing.
219
+ }
220
+
221
+ if (flag) return { on: flag, source: "flag", configured, configFile };
222
+ if (configuredOn) return { on: configuredOn, source: "config", configured, configFile };
223
+ return { on: undefined, source: "default", configured, configFile };
224
+ }
225
+
168
226
  /** An ISO-8601 instant trimmed to what a table cell has room for. */
169
227
  function shortInstant(iso: string | undefined): string {
170
228
  return iso ? iso.slice(0, 19).replace("T", " ") : "—";
@@ -395,6 +453,7 @@ export async function runOpApprove(ctx: CommandContext): Promise<number> {
395
453
  note: ctx.args.note,
396
454
  url: ctx.args.url,
397
455
  plan: ctx.args.plan,
456
+ ...(ctx.args.sign !== undefined ? { sign: ctx.args.sign } : {}),
398
457
  });
399
458
  if (!outcome.ok) return 1;
400
459
 
@@ -582,7 +641,9 @@ async function stewardTurnGate(opName: string, ctx: CommandContext): Promise<Ste
582
641
  const form = stewardFormFor(owner, env);
583
642
 
584
643
  if (form === "fountain") {
585
- if (ctx.args.on === "fountain") return { ok: true };
644
+ // `run.on: "fountain"` asks for the steward's thread as much as the flag
645
+ // does (#2523).
646
+ if ((await chooseOpRuntime(ctx, true))?.on === "fountain") return { ok: true };
586
647
  return {
587
648
  ok: false,
588
649
  message: `Op "${opName}" is one of steward "${owner.name}"'s Ops, which runs on fountain in environment "${env}"`,
@@ -15,7 +15,7 @@ import { collectEffectReceipts, isEffectReceipt, type EffectReceiptDeclaration }
15
15
  import { evaluateScenario, type ScenarioBehaviourFixture, type ScenarioVerdict } from "../../lifecycle/scenario-eval";
16
16
  import { validateBehaviourResult } from "../../behaviour-delta";
17
17
  import { collectScenarios, type ScenarioDeclaration, type ScenarioGiven } from "../../lifecycle/scenario";
18
- import { isResourceDeclarable } from "../../declarable";
18
+ import { isObservableDeclarable } from "../../declarable";
19
19
  import { loadChantConfig } from "../../config";
20
20
  import { unknownEnvError } from "../../env";
21
21
  import { collectBuildRootContributors } from "../plugins";
@@ -281,7 +281,7 @@ async function evaluateOneScenario(
281
281
  // and observed like any resource (#1832) — it joins the declared axis so
282
282
  // its lexicon's fixture data can confirm presence, absence, or a hole,
283
283
  // the same as `runLifecyclePlan` (lifecycle.ts:1184).
284
- if (!isResourceDeclarable(entity) && !isEffectReceipt(entity)) continue;
284
+ if (!isObservableDeclarable(entity) && !isEffectReceipt(entity)) continue;
285
285
  if (!declaredByLexicon.has(entity.lexicon)) declaredByLexicon.set(entity.lexicon, new Set());
286
286
  declaredByLexicon.get(entity.lexicon)!.add(name);
287
287
  }