@intentius/chant 0.101.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 (427) 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/builder.d.ts.map +1 -1
  9. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  10. package/dist/cli/commands/check-lexicon.d.ts +16 -0
  11. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  12. package/dist/cli/commands/import.d.ts +8 -0
  13. package/dist/cli/commands/import.d.ts.map +1 -1
  14. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  15. package/dist/cli/commands/init.d.ts +19 -0
  16. package/dist/cli/commands/init.d.ts.map +1 -1
  17. package/dist/cli/commands/onboard.d.ts +9 -0
  18. package/dist/cli/commands/onboard.d.ts.map +1 -1
  19. package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
  20. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
  21. package/dist/cli/handlers/dev.d.ts.map +1 -1
  22. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  23. package/dist/cli/handlers/misc.d.ts.map +1 -1
  24. package/dist/cli/handlers/operator.d.ts +9 -0
  25. package/dist/cli/handlers/operator.d.ts.map +1 -1
  26. package/dist/cli/handlers/run.d.ts.map +1 -1
  27. package/dist/cli/lsp/server.d.ts.map +1 -1
  28. package/dist/cli/main.d.ts.map +1 -1
  29. package/dist/cli/mcp/server.d.ts.map +1 -1
  30. package/dist/cli/mcp/tools/build.d.ts.map +1 -1
  31. package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
  32. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  33. package/dist/cli/mcp/tools/import.d.ts.map +1 -1
  34. package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
  35. package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
  36. package/dist/cli/mcp/tools/search.d.ts.map +1 -1
  37. package/dist/cli/mcp/workspace-tools.d.ts +1 -1
  38. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  39. package/dist/cli/registry.d.ts +11 -2
  40. package/dist/cli/registry.d.ts.map +1 -1
  41. package/dist/codegen/fetch.d.ts.map +1 -1
  42. package/dist/codegen/naming.d.ts.map +1 -1
  43. package/dist/codegen/pinned-upgrade.d.ts +67 -3
  44. package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
  45. package/dist/codegen/registry.d.ts.map +1 -1
  46. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  47. package/dist/components/capability.d.ts.map +1 -1
  48. package/dist/components/driver.d.ts.map +1 -1
  49. package/dist/components/fan-out.d.ts.map +1 -1
  50. package/dist/components/starter-plugin.d.ts.map +1 -1
  51. package/dist/components/verbs/process-runner.d.ts.map +1 -1
  52. package/dist/components/verbs/r2-sync.d.ts.map +1 -1
  53. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  54. package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
  55. package/dist/components/verbs/sign.d.ts.map +1 -1
  56. package/dist/components/verbs/verify.d.ts.map +1 -1
  57. package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
  58. package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
  59. package/dist/components/verbs/wrangler.d.ts.map +1 -1
  60. package/dist/composite.d.ts.map +1 -1
  61. package/dist/config.d.ts +19 -7
  62. package/dist/config.d.ts.map +1 -1
  63. package/dist/declarable.d.ts +16 -0
  64. package/dist/declarable.d.ts.map +1 -1
  65. package/dist/discovery/cache.d.ts.map +1 -1
  66. package/dist/discovery/fold-import.d.ts +15 -5
  67. package/dist/discovery/fold-import.d.ts.map +1 -1
  68. package/dist/discovery/param-deps.d.ts +16 -0
  69. package/dist/discovery/param-deps.d.ts.map +1 -1
  70. package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
  71. package/dist/discovery/specifier-resolve.d.ts +37 -0
  72. package/dist/discovery/specifier-resolve.d.ts.map +1 -0
  73. package/dist/errors.d.ts.map +1 -1
  74. package/dist/fold/fold.d.ts +3 -1
  75. package/dist/fold/fold.d.ts.map +1 -1
  76. package/dist/graph-detail.d.ts.map +1 -1
  77. package/dist/graph-ir.d.ts.map +1 -1
  78. package/dist/graph-layout.d.ts.map +1 -1
  79. package/dist/import/base-parser.d.ts.map +1 -1
  80. package/dist/import/embedded.d.ts +7 -0
  81. package/dist/import/embedded.d.ts.map +1 -1
  82. package/dist/import/parser-options.d.ts +14 -0
  83. package/dist/import/parser-options.d.ts.map +1 -0
  84. package/dist/import/parser.d.ts +17 -0
  85. package/dist/import/parser.d.ts.map +1 -1
  86. package/dist/kubectl-context.d.ts.map +1 -1
  87. package/dist/lexicon-output.d.ts.map +1 -1
  88. package/dist/lexicon-schema.d.ts +3 -3
  89. package/dist/lexicon.d.ts +55 -3
  90. package/dist/lexicon.d.ts.map +1 -1
  91. package/dist/lifecycle/assert-live.d.ts.map +1 -1
  92. package/dist/lifecycle/gate-ledger.d.ts +15 -0
  93. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  94. package/dist/lifecycle/git.d.ts.map +1 -1
  95. package/dist/lifecycle/member-ledger.d.ts.map +1 -1
  96. package/dist/lifecycle/observe.d.ts.map +1 -1
  97. package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
  98. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  99. package/dist/lifecycle/scenario.d.ts.map +1 -1
  100. package/dist/lifecycle/status.d.ts.map +1 -1
  101. package/dist/lifecycle/work-lease.d.ts +61 -2
  102. package/dist/lifecycle/work-lease.d.ts.map +1 -1
  103. package/dist/lint/config.d.ts +8 -8
  104. package/dist/lint/engine.d.ts +6 -0
  105. package/dist/lint/engine.d.ts.map +1 -1
  106. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  107. package/dist/op/activities/activity-contracts.d.ts +11 -11
  108. package/dist/op/activities/apply.d.ts +79 -9
  109. package/dist/op/activities/apply.d.ts.map +1 -1
  110. package/dist/op/activities/decide.d.ts.map +1 -1
  111. package/dist/op/activities/index.d.ts +1 -1
  112. package/dist/op/activities/index.d.ts.map +1 -1
  113. package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
  114. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
  115. package/dist/op/activity-contract.d.ts +1 -1
  116. package/dist/op/activity-failure.d.ts.map +1 -1
  117. package/dist/op/activity-profiles.d.ts.map +1 -1
  118. package/dist/op/builders.d.ts.map +1 -1
  119. package/dist/op/composites/apply-op.d.ts +12 -5
  120. package/dist/op/composites/apply-op.d.ts.map +1 -1
  121. package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
  122. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
  123. package/dist/op/decide-backend.d.ts.map +1 -1
  124. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  125. package/dist/op/emulator-lifecycle.d.ts +21 -2
  126. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  127. package/dist/op/gate-approval.d.ts +5 -1
  128. package/dist/op/gate-approval.d.ts.map +1 -1
  129. package/dist/op/gate-wait.d.ts.map +1 -1
  130. package/dist/op/gate.d.ts +19 -3
  131. package/dist/op/gate.d.ts.map +1 -1
  132. package/dist/op/index.d.ts +2 -0
  133. package/dist/op/index.d.ts.map +1 -1
  134. package/dist/op/local-executor.d.ts +6 -0
  135. package/dist/op/local-executor.d.ts.map +1 -1
  136. package/dist/op/operator.d.ts.map +1 -1
  137. package/dist/op/run-context.d.ts +39 -0
  138. package/dist/op/run-context.d.ts.map +1 -0
  139. package/dist/op/run-live.d.ts.map +1 -1
  140. package/dist/op/runtimes/local.d.ts.map +1 -1
  141. package/dist/op/source-archive.d.ts.map +1 -1
  142. package/dist/op/steward-beside.d.ts.map +1 -1
  143. package/dist/op/steward-points.d.ts.map +1 -1
  144. package/dist/op/steward.d.ts.map +1 -1
  145. package/dist/op/types.d.ts +8 -3
  146. package/dist/op/types.d.ts.map +1 -1
  147. package/dist/op/work-lease-run.d.ts +24 -4
  148. package/dist/op/work-lease-run.d.ts.map +1 -1
  149. package/dist/project-root.d.ts.map +1 -1
  150. package/dist/provenance.d.ts +21 -0
  151. package/dist/provenance.d.ts.map +1 -1
  152. package/dist/pseudo-parameter.d.ts.map +1 -1
  153. package/dist/reconcile.d.ts.map +1 -1
  154. package/dist/secret-materialization.d.ts.map +1 -1
  155. package/dist/terraform/emit-source.d.ts.map +1 -1
  156. package/dist/terraform/parse.d.ts.map +1 -1
  157. package/dist/testing.d.ts.map +1 -1
  158. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  159. package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
  160. package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
  161. package/dist/workspace/agent-cli.d.ts +5 -0
  162. package/dist/workspace/agent-cli.d.ts.map +1 -1
  163. package/dist/workspace/box-factory.d.ts +73 -0
  164. package/dist/workspace/box-factory.d.ts.map +1 -0
  165. package/dist/workspace/changes.d.ts.map +1 -1
  166. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
  167. package/dist/workspace/checks/boxes.d.ts.map +1 -1
  168. package/dist/workspace/checks.d.ts +5 -0
  169. package/dist/workspace/checks.d.ts.map +1 -1
  170. package/dist/workspace/compose-graph.d.ts +10 -0
  171. package/dist/workspace/compose-graph.d.ts.map +1 -1
  172. package/dist/workspace/conformance/index.d.ts +67 -6
  173. package/dist/workspace/conformance/index.d.ts.map +1 -1
  174. package/dist/workspace/conformance/vitest.d.ts +12 -3
  175. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  176. package/dist/workspace/conformance/writer.d.ts +368 -0
  177. package/dist/workspace/conformance/writer.d.ts.map +1 -0
  178. package/dist/workspace/decide.d.ts +1 -1
  179. package/dist/workspace/decide.d.ts.map +1 -1
  180. package/dist/workspace/declaration.d.ts +91 -4
  181. package/dist/workspace/declaration.d.ts.map +1 -1
  182. package/dist/workspace/declaration.schema.json +234 -5
  183. package/dist/workspace/generated-files.d.ts.map +1 -1
  184. package/dist/workspace/graph-cache.d.ts.map +1 -1
  185. package/dist/workspace/graph-cli.d.ts +2 -0
  186. package/dist/workspace/graph-cli.d.ts.map +1 -1
  187. package/dist/workspace/identity.d.ts +111 -0
  188. package/dist/workspace/identity.d.ts.map +1 -0
  189. package/dist/workspace/intent-cli.d.ts +7 -4
  190. package/dist/workspace/intent-cli.d.ts.map +1 -1
  191. package/dist/workspace/intent-record.d.ts +8 -1
  192. package/dist/workspace/intent-record.d.ts.map +1 -1
  193. package/dist/workspace/intent.d.ts +149 -5
  194. package/dist/workspace/intent.d.ts.map +1 -1
  195. package/dist/workspace/kinds.d.ts +18 -4
  196. package/dist/workspace/kinds.d.ts.map +1 -1
  197. package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
  198. package/dist/workspace/lineage-lock.d.ts +2 -2
  199. package/dist/workspace/lineage-lock.d.ts.map +1 -1
  200. package/dist/workspace/links.d.ts.map +1 -1
  201. package/dist/workspace/points.d.ts.map +1 -1
  202. package/dist/workspace/principal-classes.d.ts +114 -0
  203. package/dist/workspace/principal-classes.d.ts.map +1 -0
  204. package/dist/workspace/reason-codes.d.ts +21 -1
  205. package/dist/workspace/reason-codes.d.ts.map +1 -1
  206. package/dist/workspace/record-assets.d.ts.map +1 -1
  207. package/dist/workspace/records-checkout.d.ts +54 -0
  208. package/dist/workspace/records-checkout.d.ts.map +1 -0
  209. package/dist/workspace/records-cli.d.ts +27 -1
  210. package/dist/workspace/records-cli.d.ts.map +1 -1
  211. package/dist/workspace/records-close.d.ts +1 -1
  212. package/dist/workspace/records-close.d.ts.map +1 -1
  213. package/dist/workspace/records-since.d.ts.map +1 -1
  214. package/dist/workspace/records-write.d.ts +5 -4
  215. package/dist/workspace/records-write.d.ts.map +1 -1
  216. package/dist/workspace/records.d.ts +15 -0
  217. package/dist/workspace/records.d.ts.map +1 -1
  218. package/dist/workspace/runs-cli.d.ts +164 -0
  219. package/dist/workspace/runs-cli.d.ts.map +1 -0
  220. package/dist/workspace/runs.d.ts +423 -0
  221. package/dist/workspace/runs.d.ts.map +1 -0
  222. package/dist/workspace/source-block.d.ts +1 -1
  223. package/dist/workspace/source-block.d.ts.map +1 -1
  224. package/dist/workspace/status-gates.d.ts +15 -3
  225. package/dist/workspace/status-gates.d.ts.map +1 -1
  226. package/dist/workspace/status.d.ts +10 -0
  227. package/dist/workspace/status.d.ts.map +1 -1
  228. package/dist/workspace/symbols.d.ts +52 -0
  229. package/dist/workspace/symbols.d.ts.map +1 -0
  230. package/dist/workspace/trailer-joins.d.ts +46 -0
  231. package/dist/workspace/trailer-joins.d.ts.map +1 -0
  232. package/dist/workspace/trailers.d.ts +89 -0
  233. package/dist/workspace/trailers.d.ts.map +1 -0
  234. package/dist/workspace/trust/attestor.d.ts.map +1 -1
  235. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  236. package/dist/workspace/trust/seal.d.ts +33 -0
  237. package/dist/workspace/trust/seal.d.ts.map +1 -1
  238. package/dist/workspace/trust/test-repo.d.ts.map +1 -1
  239. package/dist/workspace/work-cli.d.ts +39 -3
  240. package/dist/workspace/work-cli.d.ts.map +1 -1
  241. package/dist/workspace/work-evidence.d.ts +1 -1
  242. package/dist/workspace/work-evidence.d.ts.map +1 -1
  243. package/dist/workspace/work.d.ts +26 -1
  244. package/dist/workspace/work.d.ts.map +1 -1
  245. package/dist/workspace/workspace-principals.schema.json +60 -0
  246. package/dist/workspace/write-scope.d.ts +79 -19
  247. package/dist/workspace/write-scope.d.ts.map +1 -1
  248. package/dist/yaml.d.ts +21 -39
  249. package/dist/yaml.d.ts.map +1 -1
  250. package/package.json +2 -2
  251. package/src/audit/prior-art.ts +15 -0
  252. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
  253. package/src/cli/commands/carve-emit-state.test.ts +4 -0
  254. package/src/cli/commands/carve-emit.ts +5 -19
  255. package/src/cli/commands/check-lexicon.test.ts +62 -1
  256. package/src/cli/commands/check-lexicon.ts +36 -6
  257. package/src/cli/commands/import-live.test.ts +18 -0
  258. package/src/cli/commands/import-parser-options.test.ts +133 -0
  259. package/src/cli/commands/import.ts +21 -3
  260. package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
  261. package/src/cli/commands/init.test.ts +8 -1
  262. package/src/cli/commands/init.ts +23 -8
  263. package/src/cli/commands/lint.test.ts +37 -0
  264. package/src/cli/commands/onboard.test.ts +53 -3
  265. package/src/cli/commands/onboard.ts +52 -4
  266. package/src/cli/commands/pinned-upgrade.ts +73 -33
  267. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
  268. package/src/cli/handlers/components.ts +2 -2
  269. package/src/cli/handlers/dev.ts +8 -6
  270. package/src/cli/handlers/lifecycle.test.ts +67 -0
  271. package/src/cli/handlers/lifecycle.ts +6 -5
  272. package/src/cli/handlers/misc.ts +2 -0
  273. package/src/cli/handlers/operator.ts +87 -3
  274. package/src/cli/handlers/run.test.ts +111 -0
  275. package/src/cli/handlers/run.ts +85 -24
  276. package/src/cli/handlers/scenario.ts +2 -2
  277. package/src/cli/main.test.ts +1 -0
  278. package/src/cli/main.ts +65 -11
  279. package/src/cli/mcp/workspace-tools.test.ts +5 -2
  280. package/src/cli/mcp/workspace-tools.ts +29 -2
  281. package/src/cli/registry.ts +11 -2
  282. package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
  283. package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
  284. package/src/codegen/pinned-upgrade.test.ts +104 -0
  285. package/src/codegen/pinned-upgrade.ts +250 -56
  286. package/src/codegen/testdata/postgres-tags.json +242 -0
  287. package/src/config.test.ts +21 -0
  288. package/src/config.ts +15 -0
  289. package/src/declarable.test.ts +21 -0
  290. package/src/declarable.ts +19 -0
  291. package/src/discovery/fold-composite.test.ts +41 -5
  292. package/src/discovery/fold-import.test.ts +129 -0
  293. package/src/discovery/fold-import.ts +643 -131
  294. package/src/discovery/fold-package-composite.test.ts +264 -0
  295. package/src/discovery/fold-specifiers.test.ts +191 -0
  296. package/src/discovery/param-deps.test.ts +66 -1
  297. package/src/discovery/param-deps.ts +45 -0
  298. package/src/discovery/specifier-resolve.ts +344 -0
  299. package/src/fold/fold.test.ts +39 -1
  300. package/src/fold/fold.ts +83 -9
  301. package/src/fold-provenance.test.ts +140 -0
  302. package/src/graph-ir.test.ts +83 -0
  303. package/src/graph-ir.ts +29 -0
  304. package/src/import/embedded.test.ts +20 -0
  305. package/src/import/embedded.ts +13 -0
  306. package/src/import/parser-options.ts +46 -0
  307. package/src/import/parser.ts +20 -0
  308. package/src/lexicon.ts +55 -3
  309. package/src/lifecycle/gate-ledger.ts +16 -0
  310. package/src/lifecycle/observe.test.ts +18 -0
  311. package/src/lifecycle/observe.ts +6 -4
  312. package/src/lifecycle/snapshot.ts +2 -2
  313. package/src/lifecycle/work-lease.test.ts +1 -1
  314. package/src/lifecycle/work-lease.ts +93 -2
  315. package/src/lint/engine.test.ts +55 -0
  316. package/src/lint/engine.ts +30 -4
  317. package/src/lint/output-docs.test.ts +59 -56
  318. package/src/op/activities/apply.test.ts +189 -5
  319. package/src/op/activities/apply.ts +230 -8
  320. package/src/op/activities/index.ts +3 -0
  321. package/src/op/activities/lexicon-upgrade.test.ts +160 -6
  322. package/src/op/activities/lexicon-upgrade.ts +154 -51
  323. package/src/op/composites/apply-op.ts +12 -5
  324. package/src/op/composites/composites.test.ts +30 -3
  325. package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
  326. package/src/op/composites/lexicon-upgrade-op.ts +29 -8
  327. package/src/op/emulator-lifecycle.test.ts +23 -0
  328. package/src/op/emulator-lifecycle.ts +52 -14
  329. package/src/op/gate-approval.test.ts +74 -0
  330. package/src/op/gate-approval.ts +5 -1
  331. package/src/op/gate.ts +31 -5
  332. package/src/op/index.ts +2 -0
  333. package/src/op/local-executor.test.ts +15 -0
  334. package/src/op/local-executor.ts +40 -12
  335. package/src/op/local-output.ts +1 -1
  336. package/src/op/operator.ts +4 -0
  337. package/src/op/run-context.test.ts +141 -0
  338. package/src/op/run-context.ts +51 -0
  339. package/src/op/runtimes/local.test.ts +14 -0
  340. package/src/op/runtimes/local.ts +2 -0
  341. package/src/op/steward-beside.ts +2 -0
  342. package/src/op/types.ts +8 -3
  343. package/src/op/work-lease-run.test.ts +47 -0
  344. package/src/op/work-lease-run.ts +73 -11
  345. package/src/provenance.ts +35 -0
  346. package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
  347. package/src/workspace/agent-cli.ts +7 -3
  348. package/src/workspace/agent.schema.json +22 -0
  349. package/src/workspace/box-factory.test.ts +320 -0
  350. package/src/workspace/box-factory.ts +103 -0
  351. package/src/workspace/changes.schema.json +12 -20
  352. package/src/workspace/changes.ts +4 -1
  353. package/src/workspace/checks/boxes.test.ts +2 -0
  354. package/src/workspace/checks.ts +29 -5
  355. package/src/workspace/compose-graph.ts +7 -0
  356. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +27 -0
  357. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +95 -0
  358. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +132 -0
  359. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +48 -0
  360. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +108 -0
  361. package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
  362. package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
  363. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +578 -0
  364. package/src/workspace/conformance/conformance.e2e.test.ts +35 -3
  365. package/src/workspace/conformance/index.mjs +30 -3
  366. package/src/workspace/conformance/index.ts +103 -15
  367. package/src/workspace/conformance/vitest.ts +44 -2
  368. package/src/workspace/conformance/writer-conformance.e2e.test.ts +131 -0
  369. package/src/workspace/conformance/writer.test.ts +156 -0
  370. package/src/workspace/conformance/writer.ts +880 -0
  371. package/src/workspace/decide.ts +7 -1
  372. package/src/workspace/declaration.schema.json +234 -5
  373. package/src/workspace/declaration.ts +174 -10
  374. package/src/workspace/graph-cli.ts +5 -2
  375. package/src/workspace/graph.schema.json +247 -0
  376. package/src/workspace/identity.test.ts +196 -0
  377. package/src/workspace/identity.ts +186 -0
  378. package/src/workspace/intent-cli.ts +64 -10
  379. package/src/workspace/intent-record.schema.json +208 -0
  380. package/src/workspace/intent-record.ts +35 -1
  381. package/src/workspace/intent-why.test.ts +274 -0
  382. package/src/workspace/intent.schema.json +718 -7
  383. package/src/workspace/intent.test.ts +11 -2
  384. package/src/workspace/intent.ts +495 -32
  385. package/src/workspace/kinds.ts +44 -37
  386. package/src/workspace/points-write.schema.json +1 -0
  387. package/src/workspace/principal-classes.test.ts +175 -0
  388. package/src/workspace/principal-classes.ts +307 -0
  389. package/src/workspace/reason-codes.test.ts +11 -3
  390. package/src/workspace/reason-codes.ts +24 -1
  391. package/src/workspace/records-amend.schema.json +2 -0
  392. package/src/workspace/records-checkout.ts +106 -0
  393. package/src/workspace/records-cli.ts +103 -11
  394. package/src/workspace/records-close.schema.json +1 -0
  395. package/src/workspace/records-new.schema.json +2 -0
  396. package/src/workspace/records-review.schema.json +2 -0
  397. package/src/workspace/records-uncommitted.test.ts +171 -0
  398. package/src/workspace/records-write.ts +28 -6
  399. package/src/workspace/records.schema.json +72 -1
  400. package/src/workspace/records.ts +27 -0
  401. package/src/workspace/runs-cli.ts +381 -0
  402. package/src/workspace/runs-write.schema.json +583 -0
  403. package/src/workspace/runs.schema.json +810 -0
  404. package/src/workspace/runs.test.ts +242 -0
  405. package/src/workspace/runs.ts +709 -0
  406. package/src/workspace/status-contract.test.ts +5 -0
  407. package/src/workspace/status-gates.ts +22 -8
  408. package/src/workspace/status.schema.json +127 -2
  409. package/src/workspace/status.ts +21 -1
  410. package/src/workspace/symbols.ts +180 -0
  411. package/src/workspace/trailer-joins.test.ts +171 -0
  412. package/src/workspace/trailer-joins.ts +82 -0
  413. package/src/workspace/trailers.ts +148 -0
  414. package/src/workspace/trust/seal.ts +52 -1
  415. package/src/workspace/work-cli.e2e.test.ts +28 -1
  416. package/src/workspace/work-cli.ts +133 -8
  417. package/src/workspace/work-evidence.schema.json +2 -0
  418. package/src/workspace/work-history.schema.json +73 -1
  419. package/src/workspace/work-history.test.ts +48 -5
  420. package/src/workspace/work-kind-fields.test.ts +154 -0
  421. package/src/workspace/work-lease.schema.json +14 -1
  422. package/src/workspace/work.ts +82 -0
  423. package/src/workspace/workspace-principals.schema.json +60 -0
  424. package/src/workspace/write-scope.test.ts +128 -2
  425. package/src/workspace/write-scope.ts +170 -45
  426. package/src/yaml.test.ts +111 -1
  427. package/src/yaml.ts +141 -549
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * lexiconUpgrade — the upgrade-detection and PR-automation activity for
3
- * in-scope lexicons (k8s, gcp, docker, gitlab, aws, azure, github).
3
+ * in-scope lexicons: every lexicon whose plugin declares `upstreamPin` (k8s,
4
+ * k3s, gcp, docker, gitlab, cedar, sql, ...) plus the rolling ones (aws, azure,
5
+ * github, fly).
4
6
  *
5
7
  * Classifies the lexicon as pinned or rolling, calls the right check function,
6
8
  * then surfaces the result in the requested mode: report (default), issue, or
@@ -25,7 +27,7 @@ import { exec } from "node:child_process";
25
27
  import { promisify } from "node:util";
26
28
  import { readFileSync, writeFileSync } from "node:fs";
27
29
  import { join } from "node:path";
28
- import type { UpgradeCheckResult, LexiconId } from "../../codegen/pinned-upgrade";
30
+ import type { UpgradeCheckResult, LexiconId, UpstreamPin } from "../../codegen/pinned-upgrade";
29
31
  import type { RollingUpgradeResult, RollingLexicon } from "../../codegen/rolling-upgrade";
30
32
  import { bumpForSeverity, bumpPackageJsonVersion } from "../../codegen/version-bump";
31
33
  import { postOrUpdateGithubIssue } from "./reconcile";
@@ -48,8 +50,9 @@ export type SemverLabel = "minor" | "breaking";
48
50
  export interface LexiconUpgradeArgs {
49
51
  /**
50
52
  * Which lexicon to check. Determines pinned vs rolling dispatch:
51
- * PINNED: k8s, gcp, docker, gitlab, cedar
52
53
  * ROLLING: aws, azure, github, fly
54
+ * PINNED: any other lexicon whose plugin declares `upstreamPin`
55
+ * (discovered at run time, as `chant dev pinned-upgrade` does)
53
56
  */
54
57
  lexicon: SupportedLexicon;
55
58
  /**
@@ -57,11 +60,17 @@ export interface LexiconUpgradeArgs {
57
60
  * Defaults to `<cwd>/lexicons/<lexicon>`.
58
61
  */
59
62
  lexiconDir?: string;
63
+ /**
64
+ * Label of one pin to check, for a lexicon whose plugin declares several
65
+ * (`upstreamPins`). Omitted, every pin is checked and reported.
66
+ */
67
+ pin?: string;
60
68
  /** What to produce. Default: "report". */
61
69
  mode?: LexiconUpgradeMode;
62
70
  /**
63
- * Override the pinned check function — inject a mock in tests.
64
- * The real implementation calls checkPinnedUpgrade from core.
71
+ * Override the pinned check function — inject a mock in tests. It may
72
+ * return one result or one per pin.
73
+ * The real implementation calls checkPinnedUpgrades from core.
65
74
  */
66
75
  _checkPinned?: CheckPinnedFn;
67
76
  /**
@@ -77,6 +86,13 @@ export interface LexiconUpgradeArgs {
77
86
  * Override the applyPinnedVersionBump function — inject a mock in tests.
78
87
  */
79
88
  _applyBump?: ApplyBumpFn;
89
+ /**
90
+ * Override pin discovery — inject a mock in tests. The real one is
91
+ * `loadUpstreamPin` from core, the loader `chant dev pinned-upgrade` uses.
92
+ * Not consulted when `_checkPinned` is injected: that stands in for the
93
+ * whole pinned check.
94
+ */
95
+ _loadPin?: (lexicon: string, lexiconDir: string) => Promise<UpstreamPin | null>;
80
96
  /**
81
97
  * Override the package.json version writer — inject a mock in tests.
82
98
  * Defaults to bumpPackageJsonVersion.
@@ -109,9 +125,11 @@ export interface LexiconUpgradeResult {
109
125
  export type CheckPinnedFn = (opts: {
110
126
  lexiconDir: string;
111
127
  lexicon: LexiconId;
128
+ /** One pin's label; omitted, every pin of the plugin. */
129
+ pin?: string;
112
130
  force?: boolean;
113
131
  verbose?: boolean;
114
- }) => Promise<UpgradeCheckResult>;
132
+ }) => Promise<UpgradeCheckResult | UpgradeCheckResult[]>;
115
133
 
116
134
  /** Matches checkRollingUpgrade from core. */
117
135
  export type CheckRollingFn = (opts: {
@@ -141,6 +159,7 @@ export type ApplyBumpFn = (
141
159
  lexicon: LexiconId,
142
160
  lexiconDir: string,
143
161
  newVersion: string,
162
+ pin?: string,
144
163
  ) => { filePath: string } | Promise<{ filePath: string }>;
145
164
 
146
165
  /**
@@ -177,21 +196,32 @@ export function computeBumpedVersion(
177
196
 
178
197
  // ── Classification ────────────────────────────────────────────────────
179
198
 
180
- // cedar pins `CEDAR_WASM_VERSION`, a dependency version rather than a spec
181
- // URL, but the pinned-constant machinery is the same (#1650).
182
- const PINNED_LEXICONS: ReadonlySet<string> = new Set<LexiconId>(["k8s", "gcp", "docker", "gitlab", "cedar"]);
199
+ // Rolling lexicons follow their upstream spec with no version constant, so
200
+ // they are a fixed list. Every other lexicon is pinned when its plugin declares
201
+ // `upstreamPin` (cedar's CEDAR_WASM_VERSION, sql's ClickHouse version, k3s...):
202
+ // discovered at run time, not listed here.
183
203
  const ROLLING_LEXICONS: ReadonlySet<string> = new Set<RollingLexicon>(["aws", "azure", "github", "fly"]);
184
204
 
185
- /** Return true when the lexicon uses a pinned version constant. */
186
- export function isPinned(lexicon: SupportedLexicon): lexicon is LexiconId {
187
- return PINNED_LEXICONS.has(lexicon);
188
- }
189
-
190
205
  /** Return true when the lexicon follows a rolling spec (no pinned constant). */
191
206
  export function isRolling(lexicon: SupportedLexicon): lexicon is RollingLexicon {
192
207
  return ROLLING_LEXICONS.has(lexicon);
193
208
  }
194
209
 
210
+ /**
211
+ * Return true when the lexicon's plugin declares an `upstreamPin` (loaded from
212
+ * `lexiconDir`, else from its installed package), the same discovery
213
+ * `chant dev pinned-upgrade` does.
214
+ */
215
+ export async function isPinned(
216
+ lexicon: SupportedLexicon,
217
+ lexiconDir?: string,
218
+ loadPin?: (lexicon: string, lexiconDir: string) => Promise<UpstreamPin | null>,
219
+ ): Promise<boolean> {
220
+ if (isRolling(lexicon)) return false;
221
+ const load = loadPin ?? (async (l, d) => (await import("../../codegen/pinned-upgrade")).loadUpstreamPin(l, d));
222
+ return (await load(lexicon, lexiconDir ?? `${process.cwd()}/lexicons/${lexicon}`)) !== null;
223
+ }
224
+
195
225
  // ── Semver labeling ───────────────────────────────────────────────────
196
226
 
197
227
  /**
@@ -210,8 +240,8 @@ export function severityToLabel(severity: string): SemverLabel | null {
210
240
  // ── Branch / PR naming ────────────────────────────────────────────────
211
241
 
212
242
  /** Long-lived branch name for a lexicon upgrade PR. */
213
- export function upgradeBranchName(lexicon: SupportedLexicon): string {
214
- return `lexicon-upgrade/${lexicon}`;
243
+ export function upgradeBranchName(lexicon: SupportedLexicon, pin?: string): string {
244
+ return pin ? `lexicon-upgrade/${lexicon}-${pin}` : `lexicon-upgrade/${lexicon}`;
215
245
  }
216
246
 
217
247
  /** PR title for an upgrade. */
@@ -219,8 +249,9 @@ export function upgradePrTitle(
219
249
  lexicon: SupportedLexicon,
220
250
  from?: string,
221
251
  to?: string | null,
252
+ pin?: string,
222
253
  ): string {
223
- if (from && to) return `feat(${lexicon}): upgrade spec ${from} to ${to}`;
254
+ if (from && to) return `feat(${lexicon}): upgrade ${pin ? `${pin} ` : "spec "}${from} to ${to}`;
224
255
  return `feat(${lexicon}): upgrade to latest spec`;
225
256
  }
226
257
 
@@ -229,14 +260,18 @@ export function upgradePrTitle(
229
260
  /** Build the PR/issue/report body from the upgrade result data. */
230
261
  export function buildUpgradeSummary(opts: {
231
262
  lexicon: SupportedLexicon;
263
+ /** The pin's label, for a lexicon with several pins. */
264
+ pin?: string;
232
265
  from?: string;
233
266
  to?: string | null;
234
267
  deltaText: string;
235
268
  semverLabel: SemverLabel | null;
236
269
  validationOk: boolean;
237
270
  failures?: Array<{ step: string; output: string }>;
271
+ /** Set for a pin that moves with other constants: nothing was bumped or regenerated. */
272
+ manualPin?: { file: string; instructions: string };
238
273
  }): string {
239
- const { lexicon, from, to, deltaText, semverLabel, validationOk, failures = [] } = opts;
274
+ const { lexicon, from, to, deltaText, semverLabel, validationOk, failures = [], manualPin } = opts;
240
275
 
241
276
  // Example validation status (#604): the upgrade check lints the lexicon's
242
277
  // examples (regenLexicon step 6), and an example failure makes validation
@@ -246,7 +281,7 @@ export function buildUpgradeSummary(opts: {
246
281
  // for rolling lexicons.)
247
282
  const exampleFailures = failures.filter((f) => f.step === "lint" || f.step === "examples");
248
283
 
249
- const lines: string[] = [`## Lexicon upgrade: ${lexicon}`];
284
+ const lines: string[] = [`## Lexicon upgrade: ${lexicon}${opts.pin ? ` (${opts.pin})` : ""}`];
250
285
  if (from) lines.push(``, `**From:** \`${from}\` **To:** \`${to ?? "latest"}\``);
251
286
  if (semverLabel) lines.push(`**Semver label:** \`${semverLabel}\``);
252
287
  lines.push(
@@ -257,6 +292,17 @@ export function buildUpgradeSummary(opts: {
257
292
 
258
293
  lines.push(``);
259
294
 
295
+ if (manualPin) {
296
+ lines.push(`### Manual pin bump`);
297
+ lines.push(``);
298
+ lines.push(
299
+ `This pin cannot be rewritten automatically, so nothing was edited or regenerated. ` +
300
+ `Edit \`${manualPin.file}\`: ${manualPin.instructions}`,
301
+ );
302
+ lines.push(``);
303
+ return lines.join("\n");
304
+ }
305
+
260
306
  if (!validationOk) {
261
307
  lines.push(`### Validation failures`);
262
308
  lines.push(``);
@@ -403,7 +449,7 @@ let _realApplyBump: ApplyBumpFn | null = null;
403
449
  async function getRealCheckPinned(): Promise<CheckPinnedFn> {
404
450
  if (!_realCheckPinned) {
405
451
  const mod = await import("../../codegen/pinned-upgrade");
406
- _realCheckPinned = (opts) => mod.checkPinnedUpgrade(opts);
452
+ _realCheckPinned = (opts) => mod.checkPinnedUpgrades(opts);
407
453
  }
408
454
  return _realCheckPinned;
409
455
  }
@@ -419,8 +465,8 @@ async function getRealCheckRolling(): Promise<CheckRollingFn> {
419
465
  async function getRealApplyBump(): Promise<ApplyBumpFn> {
420
466
  if (!_realApplyBump) {
421
467
  const mod = await import("../../codegen/pinned-upgrade");
422
- _realApplyBump = (lexicon, lexiconDir, newVersion) =>
423
- mod.applyPinnedVersionBump(lexicon, lexiconDir, newVersion);
468
+ _realApplyBump = (lexicon, lexiconDir, newVersion, pin) =>
469
+ mod.applyPinnedVersionBump(lexicon, lexiconDir, newVersion, pin);
424
470
  }
425
471
  return _realApplyBump;
426
472
  }
@@ -430,9 +476,12 @@ async function getRealApplyBump(): Promise<ApplyBumpFn> {
430
476
  /**
431
477
  * Run the lexicon upgrade check for one lexicon and surface the result.
432
478
  *
433
- * Pinned lexicons (k8s, gcp, docker, gitlab):
479
+ * Pinned lexicons (any whose plugin declares `upstreamPin`):
434
480
  * Calls checkPinnedUpgrade. A PR is opened only when hasUpgrade=true AND
435
481
  * validation passed. Validation failures produce a report or issue.
482
+ * A pin that moves with other constants (`alsoMoves`, sql's version plus
483
+ * digest) is report-only: the summary names the newer version and the
484
+ * manual step, and no branch, bump or PR is made.
436
485
  *
437
486
  * Rolling lexicons (aws, azure, github):
438
487
  * Calls checkRollingUpgrade. A PR is opened only when the surface changed
@@ -472,20 +521,65 @@ export async function lexiconUpgrade(args: LexiconUpgradeArgs): Promise<LexiconU
472
521
  let to: string | null | undefined;
473
522
  let freshSnapshotJson: string | null = null;
474
523
 
475
- if (isPinned(lexicon)) {
524
+ // An injected check stands in for the whole pinned check, so discovery runs
525
+ // only on the real path.
526
+ const pinned = !isRolling(lexicon) && (args._checkPinned ? true : await isPinned(lexicon, lexiconDir, args._loadPin));
527
+ let manualPin: { file: string; instructions: string } | undefined;
528
+
529
+ let pinLabel: string | undefined;
530
+ let summaryOverride: string | undefined;
531
+
532
+ if (pinned) {
476
533
  const checkPinned = args._checkPinned ?? (await getRealCheckPinned());
477
- const result = await checkPinned({ lexiconDir, lexicon });
534
+ const raw = await checkPinned({ lexiconDir, lexicon, ...(args.pin ? { pin: args.pin } : {}) });
535
+ const results = Array.isArray(raw) ? raw : [raw];
536
+ const multi = results.length > 1;
537
+
538
+ // The pin the rest of the flow (validation, bump, PR) acts on: the first
539
+ // one that has a rewritable upgrade, else the first with any finding.
540
+ // A pin with `alsoMoves` (manualPin) is report-only, so it never leads.
541
+ const rewritable = (r: UpgradeCheckResult) => r.hasUpgrade && !r.manualPin && !r.fetchError;
542
+ const result = results.find(rewritable) ?? results.find((r) => r.hasUpgrade || r.fetchError) ?? results[0]!;
543
+ if (multi) {
544
+ pinLabel = rewritable(result) ? result.pin : undefined;
545
+ const sections = results.map((r) =>
546
+ buildUpgradeSummary({
547
+ lexicon,
548
+ pin: r.pin,
549
+ from: r.from,
550
+ to: r.to,
551
+ deltaText: r.validation?.deltaText ?? "",
552
+ semverLabel: severityToLabel(r.validation?.severity ?? "none"),
553
+ validationOk: r.fetchError ? false : (r.validation?.ok ?? true),
554
+ failures: r.fetchError
555
+ ? [{ step: "upstream-fetch", output: r.fetchError }]
556
+ : (r.validation?.failures ?? []).map((f) => ({ step: f.step, output: f.output })),
557
+ manualPin: r.manualPin,
558
+ }),
559
+ );
560
+ const others = results.filter((r) => r !== result && rewritable(r));
561
+ if (others.length > 0) {
562
+ const names = others.map((r) => r.pin).join(", ");
563
+ sections.push(
564
+ "More than one pin has an upgrade this run can bump. This run acts on " + result.pin +
565
+ "; run it again with pin set to each of the others (" + names + ").",
566
+ );
567
+ }
568
+ summaryOverride = sections.join("\n\n");
569
+ }
478
570
 
479
571
  if (result.fetchError) {
480
- const summary = buildUpgradeSummary({
481
- lexicon,
482
- from: result.from,
483
- to: result.to,
484
- deltaText: "",
485
- semverLabel: null,
486
- validationOk: false,
487
- failures: [{ step: "upstream-fetch", output: result.fetchError }],
488
- });
572
+ const summary =
573
+ summaryOverride ??
574
+ buildUpgradeSummary({
575
+ lexicon,
576
+ from: result.from,
577
+ to: result.to,
578
+ deltaText: "",
579
+ semverLabel: null,
580
+ validationOk: false,
581
+ failures: [{ step: "upstream-fetch", output: result.fetchError }],
582
+ });
489
583
  return {
490
584
  lexicon,
491
585
  mode,
@@ -497,9 +591,10 @@ export async function lexiconUpgrade(args: LexiconUpgradeArgs): Promise<LexiconU
497
591
  };
498
592
  }
499
593
 
500
- hasUpgrade = result.hasUpgrade;
594
+ hasUpgrade = results.some((r) => r.hasUpgrade && !r.fetchError);
501
595
  from = result.from;
502
596
  to = result.to;
597
+ manualPin = multi ? (rewritable(result) ? undefined : result.manualPin) : result.manualPin;
503
598
  severity = result.validation?.severity ?? "none";
504
599
  deltaText = result.validation?.deltaText ?? "";
505
600
  validationOk = result.validation?.ok ?? true;
@@ -524,20 +619,23 @@ export async function lexiconUpgrade(args: LexiconUpgradeArgs): Promise<LexiconU
524
619
  } else {
525
620
  throw new Error(
526
621
  `Lexicon "${lexicon as string}" is not supported by LexiconUpgradeOp. ` +
527
- `Supported: k8s, gcp, docker, gitlab, aws, azure, github`,
622
+ `Supported: the rolling lexicons (${[...ROLLING_LEXICONS].join(", ")}) and any lexicon whose plugin declares upstreamPin.`,
528
623
  );
529
624
  }
530
625
 
531
626
  const semverLabel = severityToLabel(severity);
532
- const summary = buildUpgradeSummary({
533
- lexicon,
534
- from,
535
- to,
536
- deltaText,
537
- semverLabel,
538
- validationOk,
539
- failures,
540
- });
627
+ const summary =
628
+ summaryOverride ??
629
+ buildUpgradeSummary({
630
+ lexicon,
631
+ from,
632
+ to,
633
+ deltaText,
634
+ semverLabel,
635
+ validationOk,
636
+ failures,
637
+ manualPin,
638
+ });
541
639
 
542
640
  // ── 2. No upgrade ─────────────────────────────────────────────────
543
641
 
@@ -584,15 +682,20 @@ export async function lexiconUpgrade(args: LexiconUpgradeArgs): Promise<LexiconU
584
682
 
585
683
  // ── 4. Upgrade ready: surface according to mode ───────────────────
586
684
 
587
- const title = upgradePrTitle(lexicon, from, to);
685
+ const title = upgradePrTitle(lexicon, from, to, pinLabel);
588
686
 
589
687
  // #546: rolling lexicons have no version pin, so a "pull-request" would only
590
688
  // refresh the baseline snapshot — low signal, no build-output change. Surface
591
689
  // rolling drift as an issue instead; re-baselining stays a deliberate
592
690
  // maintainer action (`chant dev surface-diff <lexicon> --update-snapshot --bump`).
593
691
  // Pinned lexicons keep opening version-bump PRs.
594
- const effectiveMode: LexiconUpgradeMode =
595
- mode === "pull-request" && isRolling(lexicon) ? "issue" : mode;
692
+ // A pin that moves with other constants cannot be bumped here, so it is
693
+ // report-only whatever the mode.
694
+ const effectiveMode: LexiconUpgradeMode = manualPin
695
+ ? "report"
696
+ : mode === "pull-request" && isRolling(lexicon)
697
+ ? "issue"
698
+ : mode;
596
699
 
597
700
  if (effectiveMode === "report") {
598
701
  return {
@@ -622,7 +725,7 @@ export async function lexiconUpgrade(args: LexiconUpgradeArgs): Promise<LexiconU
622
725
 
623
726
  // ── 5. pull-request mode: idempotent branch + PR management ──────
624
727
 
625
- const branch = upgradeBranchName(lexicon);
728
+ const branch = upgradeBranchName(lexicon, pinLabel);
626
729
 
627
730
  // Idempotency check: if the open PR already has this summary, nothing changed.
628
731
  // Compare trimmed — gh/git round-trips can alter trailing whitespace.
@@ -662,9 +765,9 @@ export async function lexiconUpgrade(args: LexiconUpgradeArgs): Promise<LexiconU
662
765
  await gh(`git checkout ${shellQuote(branch)}`);
663
766
 
664
767
  // For pinned lexicons: apply the spec version bump permanently on this branch.
665
- if (isPinned(lexicon) && to) {
768
+ if (pinned && to) {
666
769
  const applyBump = args._applyBump ?? (await getRealApplyBump());
667
- const { filePath } = await applyBump(lexicon, lexiconDir, to);
770
+ const { filePath } = await (pinLabel ? applyBump(lexicon, lexiconDir, to, pinLabel) : applyBump(lexicon, lexiconDir, to));
668
771
  await gh(`git add ${shellQuote(filePath)}`).catch(() => {/* best-effort */});
669
772
 
670
773
  // Bump the lexicon's package.json version so merging this PR causes
@@ -10,8 +10,11 @@
10
10
  * the ownership tag on `arm` (chant #1448 — previously `--mode Complete`, which
11
11
  * removed resources chant never applied), the ownership label on `gcp`
12
12
  * (chant #1449), the metadata marker (machines) and managed app
13
- * (volumes/ips/certs/secrets) on `fly` (chant #1449), and the stack itself on
14
- * `cloudformation`, which a resource CFN did not create is not in. All of
13
+ * (volumes/ips/certs/secrets) on `fly` (chant #1449), the ownership labels on
14
+ * `grafana` (chant #3011), the comment marker on `clickhouse` (chant #3208)
15
+ * and `postgres` (chant #3280),
16
+ * and the stack itself on `cloudformation`, which a
17
+ * resource CFN did not create is not in. All of
15
18
  * them are owned-only. See {@link DeleteMode}.
16
19
  *
17
20
  * A gate is a fact, not a wait (#2119): the local executor reads the gate
@@ -52,14 +55,18 @@ import { defaultOutput, hasNativeRollback, type ApplyTarget, type DeleteMode } f
52
55
  export interface ApplyOpConfig {
53
56
  /** Op name (kebab-case). */
54
57
  name: string;
55
- /** Environment — CFN stack name / ARM resource group / kube context env;
56
- * a log label on `gcp` and `fly`. */
58
+ /** Environment — CFN stack name / ARM resource group / kube context env /
59
+ * the `grafana.profiles.<env>` entry on `grafana` / the `sql.profiles.<env>`
60
+ * entry on `clickhouse` and `postgres`; a log label on `gcp` and
61
+ * `fly`. */
57
62
  env: string;
58
63
  /** Native apply mechanism. Default: "kubectl". */
59
64
  target?: ApplyTarget;
60
65
  /** Built manifest/template path. Default per target: `dist` (dir) for kubectl,
61
66
  * `template.json` (file) for CloudFormation/ARM, `dist/gcp.yaml` for gcp,
62
- * `dist/fly.json` for fly. Must match your build output. */
67
+ * `dist/fly.json` for fly, `dist/grafana.json` (the build index) for
68
+ * grafana, `dist/schema.json` for clickhouse and postgres. Must match your
69
+ * build output. */
63
70
  output?: string;
64
71
  /** Project directory to build. Default: ".". */
65
72
  path?: string;
@@ -157,6 +157,33 @@ describe("ApplyOp: shape", () => {
157
157
  expect(applyStep.fn).toBe("nativeApply");
158
158
  expect(applyStep.args).toEqual({ target: "kubectl", env: "prod", output: "dist", deleteMode: "never" });
159
159
  });
160
+
161
+ test("target clickhouse applies the sql build output, dist/schema.json by default (#3208)", () => {
162
+ const { op } = ApplyOp({ name: "p", env: "prod", target: "clickhouse", delete: "owned-only" });
163
+ const phases = getProps(op).phases as Array<Record<string, unknown>>;
164
+ const applyStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
165
+ expect(applyStep.fn).toBe("nativeApply");
166
+ expect(applyStep.args).toEqual({ target: "clickhouse", env: "prod", output: "dist/schema.json", deleteMode: "owned-only" });
167
+ expect(getProps(op).onFailure).toBeUndefined();
168
+ });
169
+
170
+ test("target postgres applies the sql build output, dist/schema.json by default (#3280)", () => {
171
+ const { op } = ApplyOp({ name: "p", env: "prod", target: "postgres", delete: "owned-only" });
172
+ const phases = getProps(op).phases as Array<Record<string, unknown>>;
173
+ const applyStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
174
+ expect(applyStep.fn).toBe("nativeApply");
175
+ expect(applyStep.args).toEqual({ target: "postgres", env: "prod", output: "dist/schema.json", deleteMode: "owned-only" });
176
+ expect(getProps(op).onFailure).toBeUndefined();
177
+ });
178
+
179
+ test("target grafana applies the build index, dist/grafana.json by default (#3011)", () => {
180
+ const { op } = ApplyOp({ name: "p", env: "prod", target: "grafana", delete: "owned-only" });
181
+ const phases = getProps(op).phases as Array<Record<string, unknown>>;
182
+ const applyStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
183
+ expect(applyStep.fn).toBe("nativeApply");
184
+ expect(applyStep.args).toEqual({ target: "grafana", env: "prod", output: "dist/grafana.json", deleteMode: "owned-only" });
185
+ expect(getProps(op).onFailure).toBeUndefined();
186
+ });
160
187
  });
161
188
 
162
189
  describe("ApplyOp: gating + deletes", () => {
@@ -274,7 +301,7 @@ describe("ApplyOp: compensation (#125, total-or-refused in #1449)", () => {
274
301
  });
275
302
 
276
303
  test("compensate: true is refused at build time for every rollback-less target (#1449)", () => {
277
- for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly"] as const) {
304
+ for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly", "grafana", "clickhouse", "postgres"] as const) {
278
305
  expect(() => ApplyOp({ name: "p", env: "prod", target, compensate: true })).toThrow(
279
306
  new RegExp(`ApplyOp "p": compensate is enabled, but target "${target}" has no automatic rollback`),
280
307
  );
@@ -296,7 +323,7 @@ describe("ApplyOp: compensation (#125, total-or-refused in #1449)", () => {
296
323
  });
297
324
 
298
325
  test("an object without a command is refused the same way as true", () => {
299
- for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly"] as const) {
326
+ for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly", "grafana", "clickhouse", "postgres"] as const) {
300
327
  expect(() => ApplyOp({ name: "p", env: "prod", target, compensate: {} })).toThrow(
301
328
  /has no automatic rollback/,
302
329
  );
@@ -304,7 +331,7 @@ describe("ApplyOp: compensation (#125, total-or-refused in #1449)", () => {
304
331
  });
305
332
 
306
333
  test("a command lifts the refusal on every target", () => {
307
- for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly", "cloudformation"] as const) {
334
+ for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly", "grafana", "clickhouse", "postgres", "cloudformation"] as const) {
308
335
  const { op } = ApplyOp({
309
336
  name: "p",
310
337
  env: "prod",
@@ -49,17 +49,22 @@ describe("LexiconUpgradeOp composite (#527)", () => {
49
49
  expect(phases[0].steps[0].outcomeAttribute).toEqual({ name: "HasUpgrade", from: "hasUpgrade" });
50
50
  });
51
51
 
52
- test("in-scope set is exactly the 9 lexicons (helm/forgejo excluded)", () => {
52
+ test("known set is exactly the 11 lexicons (helm/forgejo excluded)", () => {
53
53
  // cedar joined with #1650: it pins `CEDAR_WASM_VERSION`, a dependency
54
54
  // rather than a downloaded spec, but the pinned-constant machinery the Op
55
55
  // drives is identical. helm/forgejo still have no upstream at all.
56
56
  expect([...IN_SCOPE_LEXICONS].sort()).toEqual(
57
- ["aws", "azure", "cedar", "docker", "fly", "gcp", "github", "gitlab", "k8s"].sort(),
57
+ ["aws", "azure", "cedar", "docker", "fly", "gcp", "github", "gitlab", "k3s", "k8s", "sql"].sort(),
58
58
  );
59
59
  expect(IN_SCOPE_LEXICONS).not.toContain("helm");
60
60
  expect(IN_SCOPE_LEXICONS).not.toContain("forgejo");
61
61
  });
62
62
 
63
+ test("accepts a pinned lexicon by name, discovered at run time", () => {
64
+ expect(() => LexiconUpgradeOp({ lexicon: "sql" })).not.toThrow();
65
+ expect(() => LexiconUpgradeOp({ lexicon: "k3s" })).not.toThrow();
66
+ });
67
+
63
68
  test("rejects out-of-scope lexicons at construction", () => {
64
69
  for (const l of ["helm", "forgejo"]) {
65
70
  expect(() => LexiconUpgradeOp({ lexicon: l as never })).toThrow(/not in scope/);
@@ -13,9 +13,13 @@
13
13
  * lands on the Op itself (#2120).
14
14
  *
15
15
  * Lexicon classification (which check the activity dispatches to):
16
- * PINNED {k8s, gcp, docker, gitlab} → checkPinnedUpgrade
17
- * ROLLING {aws, azure, github} → checkRollingUpgrade
18
- * helm / forgejo are excluded — no upstream spec.
16
+ * ROLLING {aws, azure, github, fly} → checkRollingUpgrade
17
+ * PINNED any other lexicon whose plugin declares `upstreamPin`
18
+ * (k8s, k3s, gcp, docker, gitlab, cedar, sql, ...) → checkPinnedUpgrade
19
+ * The pinned set is discovered from the plugin at run time, the way `chant dev
20
+ * pinned-upgrade` does. A pin that moves with other constants (`alsoMoves`, as
21
+ * sql's version plus digest) is report-only. helm / forgejo are excluded: no
22
+ * upstream spec.
19
23
  *
20
24
  * Finding-modes mirror ReconcileOp: `report` (default, no external services)
21
25
  * | `issue` | `pull-request`. For the epic goal, `pull-request` opens/updates a
@@ -43,9 +47,15 @@ import { Op, phase } from "../builders";
43
47
  import type { OpResource } from "../resource";
44
48
  import type { LexiconUpgradeMode, SupportedLexicon } from "../activities/lexicon-upgrade";
45
49
 
46
- /** The in-scope lexicons. helm / forgejo are excluded (no upstream spec). */
50
+ /**
51
+ * The lexicons known to be in scope. This is not a gate: a lexicon that declares
52
+ * `upstreamPin` is dispatched without being listed. helm / forgejo are excluded
53
+ * (no upstream spec).
54
+ */
47
55
  export const IN_SCOPE_LEXICONS: readonly SupportedLexicon[] = [
48
56
  "k8s",
57
+ "k3s",
58
+ "sql",
49
59
  "gcp",
50
60
  "docker",
51
61
  "gitlab",
@@ -74,6 +84,12 @@ export interface LexiconUpgradeOpConfig {
74
84
  * `<cwd>/lexicons/<lexicon>` at run time.
75
85
  */
76
86
  lexiconDir?: string;
87
+ /**
88
+ * Label of one pin to check, for a lexicon whose plugin declares several
89
+ * (`upstreamPins`; sql has `clickhouse` and `postgres-14` to `postgres-18`).
90
+ * Omit to check every pin.
91
+ */
92
+ pin?: string;
77
93
  /**
78
94
  * What to produce on findings. Default: "report".
79
95
  * @default "report"
@@ -86,15 +102,19 @@ export interface LexiconUpgradeOpResources {
86
102
  op: InstanceType<typeof OpResource>;
87
103
  }
88
104
 
105
+ const EXCLUDED_LEXICONS: ReadonlySet<string> = new Set(["helm", "forgejo"]);
106
+
89
107
  /**
90
- * Build a LexiconUpgradeOp for one lexicon. Rejects out-of-scope lexicons at
91
- * construction time so a typo never silently produces a no-op Op.
108
+ * Build a LexiconUpgradeOp for one lexicon. Rejects the lexicons with no
109
+ * upstream spec at construction time. Whether any other name declares a pin is
110
+ * only known by loading its plugin, so the activity checks that at run time and
111
+ * fails with a clear message for a name that has none.
92
112
  */
93
113
  export function LexiconUpgradeOp(config: LexiconUpgradeOpConfig): LexiconUpgradeOpResources {
94
- if (!IN_SCOPE_LEXICONS.includes(config.lexicon)) {
114
+ if (EXCLUDED_LEXICONS.has(config.lexicon)) {
95
115
  throw new Error(
96
116
  `LexiconUpgradeOp: "${config.lexicon}" is not in scope. ` +
97
- `In-scope lexicons: ${IN_SCOPE_LEXICONS.join(", ")} ` +
117
+ `Known lexicons: ${IN_SCOPE_LEXICONS.join(", ")}, and any lexicon declaring upstreamPin ` +
98
118
  `(helm, forgejo have no upstream spec).`,
99
119
  );
100
120
  }
@@ -119,6 +139,7 @@ export function LexiconUpgradeOp(config: LexiconUpgradeOpConfig): LexiconUpgrade
119
139
  args: {
120
140
  lexicon: config.lexicon,
121
141
  ...(config.lexiconDir ? { lexiconDir: config.lexiconDir } : {}),
142
+ ...(config.pin ? { pin: config.pin } : {}),
122
143
  mode: onFinding,
123
144
  },
124
145
  // Surface whether a PR-worthy upgrade was found as the run's
@@ -39,3 +39,26 @@ describe("emulatorLifecycle command builders", () => {
39
39
  expect(emu.endpoint(4599)).toBe("http://localhost:4599");
40
40
  });
41
41
  });
42
+
43
+ describe("an emulator reached by its own client, not over HTTP", () => {
44
+ const pg = emulatorLifecycle({
45
+ name: "chant-postgres",
46
+ image: "postgres:18",
47
+ containerPort: 5432,
48
+ readyCommand: ["pg_isready", "-h", "127.0.0.1"],
49
+ endpoint: (port) => `postgres://postgres@localhost:${port}/postgres`,
50
+ });
51
+
52
+ test("readiness is a command run in the container", () => {
53
+ expect(pg.readyExecCommand("chant-postgres")).toBe("docker exec chant-postgres pg_isready -h 127.0.0.1");
54
+ expect(emulatorLifecycle({ name: "x", image: "x:1", containerPort: 1, healthPath: "/h" }).readyExecCommand("x")).toBeUndefined();
55
+ });
56
+
57
+ test("the endpoint is the declared one, not an http URL", () => {
58
+ expect(pg.endpoint(55432)).toBe("postgres://postgres@localhost:55432/postgres");
59
+ });
60
+
61
+ test("a spec with neither a health path nor a ready command is refused", () => {
62
+ expect(() => emulatorLifecycle({ name: "x", image: "x:1", containerPort: 1 })).toThrow(/neither a healthPath nor a readyCommand/);
63
+ });
64
+ });