@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
@@ -21,6 +21,14 @@
21
21
  * per-resource `gcpApply` and the fly lexicon's `flyApply`, which never had a
22
22
  * CLI in the path to begin with: gcp maps each CNRM kind to its REST API
23
23
  * itself, and fly speaks the Machines API (flaps) directly, no flyctl.
24
+ * `grafana` joined later (#3011): the grafana lexicon's `grafanaApply`, which
25
+ * writes folders, library panels and dashboards over Grafana's HTTP API.
26
+ * `clickhouse` joined after it (#3208): the sql lexicon's `clickhouseApply`,
27
+ * which sends a ClickHouse server the CREATE and ALTER statements a build's
28
+ * classified changes need, over its HTTP interface. `postgres` joined after
29
+ * that (#3280): the sql lexicon's `postgresApply`, which sends a Postgres
30
+ * server the statements a build's classified changes need, grouped into
31
+ * transactions, with `CONCURRENTLY` statements outside them.
24
32
  * **The dispatcher stayed here**, because "which
25
33
  * mechanism applies this target" is not any one product's knowledge — and
26
34
  * because the activity keeps its name, its arguments and its place in
@@ -40,7 +48,7 @@ import { importLexiconPackage } from "../../lexicon-module";
40
48
  const execAsync = promisify(exec);
41
49
 
42
50
  /** The native apply mechanism for a target. */
43
- export type ApplyTarget = "cloudformation" | "kubectl" | "arm" | "kustomize" | "gcp" | "fly";
51
+ export type ApplyTarget = "cloudformation" | "kubectl" | "arm" | "kustomize" | "gcp" | "fly" | "grafana" | "clickhouse" | "postgres";
44
52
 
45
53
  /**
46
54
  * How apply treats resources no longer declared.
@@ -52,7 +60,16 @@ export type ApplyTarget = "cloudformation" | "kubectl" | "arm" | "kustomize" | "
52
60
  * ownership label (via the gcp lexicon's `gcpApply` — only kinds it can
53
61
  * list; the rest are reported not-prunable), `fly` prunes machines by the
54
62
  * metadata marker and the metadata-less types (volumes/ips/certs/secrets)
55
- * app-scoped under a managed app (via the fly lexicon's `flyApply`), and
63
+ * app-scoped under a managed app (via the fly lexicon's `flyApply`),
64
+ * `grafana` prunes dashboards and folders by the ownership labels (via the
65
+ * grafana lexicon's `grafanaApply`; library panels, Grafana 11 and a project
66
+ * with no ownership stack are reported not-prunable), `clickhouse` drops
67
+ * tables, views and databases whose comment carries the project's marker
68
+ * (via the sql lexicon's `clickhouseApply`; a project with no ownership
69
+ * stack is reported not-prunable), `postgres` drops the objects in the
70
+ * declared schemas whose comment carries the project's marker (via the sql
71
+ * lexicon's `postgresApply`; never an object another tool keeps, and a
72
+ * project with no ownership stack is reported not-prunable), and
56
73
  * `cloudformation` is bounded by the stack — which holds, because a
57
74
  * resource CFN did not create is not in the stack.
58
75
  * - `gated` — same delete scope as `owned-only`, but an approval gate precedes
@@ -66,7 +83,11 @@ export interface NativeApplyArgs {
66
83
  target: ApplyTarget;
67
84
  /** Environment. What it means is per target: the CFN stack name on
68
85
  * `cloudformation`, the ARM resource group on `arm`, the chant environment
69
- * on `kubectl`/`kustomize`. On `gcp` and `fly` it is a log label only — the
86
+ * on `kubectl`/`kustomize`, and on `grafana` the chant environment whose
87
+ * `grafana.profiles.<env>` names the Grafana to write to, and on
88
+ * `clickhouse` and `postgres` the chant environment whose
89
+ * `sql.profiles.<env>` names the server. On `gcp` and
90
+ * `fly` it is a log label only — the
70
91
  * gcp applier resolves the project (`GOOGLE_CLOUD_PROJECT` env / CNRM
71
92
  * annotation) and endpoint (`GCP_ENDPOINT_URL` env) itself, and the fly
72
93
  * applier resolves its endpoint (`FLY_FLAPS_BASE_URL` env) and token
@@ -74,7 +95,9 @@ export interface NativeApplyArgs {
74
95
  env: string;
75
96
  /** Built manifest/template path. Default per target ({@link defaultOutput}):
76
97
  * `dist` (a dir) for kubectl, `template.json` (a file) for
77
- * CloudFormation/ARM, `dist/gcp.yaml` for gcp, `dist/fly.json` for fly. */
98
+ * CloudFormation/ARM, `dist/gcp.yaml` for gcp, `dist/fly.json` for fly,
99
+ * `dist/grafana.json` (the build index) for grafana, `dist/schema.json`
100
+ * (the sql lexicon's build output) for clickhouse and postgres. */
78
101
  output?: string;
79
102
  /** Delete handling. Default: never. */
80
103
  deleteMode?: DeleteMode;
@@ -182,6 +205,57 @@ export type FlyApplier = (
182
205
  signal?: AbortSignal,
183
206
  ) => Promise<ApplyResult>;
184
207
 
208
+ /**
209
+ * The grafana lexicon's API applier, as this module needs to call it (#3011):
210
+ * `grafanaApply` composed with the lexicon's own `toApplyResult` projection,
211
+ * so what crosses this seam is core's versioned apply envelope (#1446).
212
+ *
213
+ * `environment` selects `grafana.profiles.<environment>` (else `GRAFANA_URL`
214
+ * with `GRAFANA_TOKEN`), the binding observe and export use. The ownership
215
+ * marker is not passed: `grafanaApply` reads `ownership` from the project's
216
+ * `chant.config.ts` itself, and a prune without a stack declines as
217
+ * not-prunable rather than delete another project's dashboards.
218
+ */
219
+ export type GrafanaApplier = (
220
+ args: {
221
+ indexPath: string;
222
+ environment?: string;
223
+ prune?: boolean;
224
+ },
225
+ signal?: AbortSignal,
226
+ ) => Promise<ApplyResult>;
227
+
228
+ /**
229
+ * The sql lexicon's ClickHouse applier, as this module needs to call it
230
+ * (#3208): `clickhouseApply` composed with the lexicon's own `toApplyResult`
231
+ * projection, so what crosses this seam is core's versioned apply envelope
232
+ * (#1446). A rebuild-class change rides it as NOT-ATTEMPTED.
233
+ *
234
+ * `environment` selects `sql.profiles.<environment>` (else `CLICKHOUSE_URL`),
235
+ * the binding observe and import use. The ownership marker is not passed:
236
+ * `clickhouseApply` reads `ownership` from the project's `chant.config.ts`.
237
+ */
238
+ export type ClickHouseApplier = (
239
+ args: {
240
+ buildPath: string;
241
+ environment?: string;
242
+ prune?: boolean;
243
+ },
244
+ signal?: AbortSignal,
245
+ ) => Promise<ApplyResult>;
246
+
247
+ /**
248
+ * The sql lexicon's Postgres applier, as this module needs to call it
249
+ * (#3280): `postgresApply` composed with the lexicon's own `toApplyResult`,
250
+ * the same seam as {@link ClickHouseApplier}. An expand-and-contract change
251
+ * and a rolled-back transaction's objects ride it as NOT-ATTEMPTED.
252
+ *
253
+ * `environment` selects `sql.profiles.<environment>` (else `POSTGRES_URL`).
254
+ * The ownership marker and the timeouts are not passed: `postgresApply`
255
+ * reads `ownership` and the profile from the project's `chant.config.ts`.
256
+ */
257
+ export type PostgresApplier = ClickHouseApplier;
258
+
185
259
  /**
186
260
  * The aws lexicon's CloudFormation applier, as this module needs to call it
187
261
  * (#1449). Structural, like {@link K8sApplier} and {@link AzureApplier}.
@@ -363,6 +437,102 @@ async function loadFlyApplier(): Promise<FlyApplier> {
363
437
  return async (args, signal) => toApplyResult(await flyApply(args, signal));
364
438
  }
365
439
 
440
+ /**
441
+ * Load the grafana lexicon's `grafanaApply` (#3011). Same variable-specifier
442
+ * trick as {@link loadK8sApplier}, for the same reason. The activities subpath,
443
+ * not the package root or `op/builders`: those hold the typed step builder,
444
+ * while the activity itself (and its `toApplyResult`) lives here.
445
+ */
446
+ async function loadGrafanaApplier(): Promise<GrafanaApplier> {
447
+ const spec = "@intentius/chant-lexicon-grafana/op/activities";
448
+ type GrafanaModule = {
449
+ grafanaApply?: (args: Parameters<GrafanaApplier>[0], signal?: AbortSignal) => Promise<unknown>;
450
+ toApplyResult?: (result: unknown) => ApplyResult;
451
+ };
452
+ let mod: GrafanaModule;
453
+ try {
454
+ mod = (await importLexiconPackage(spec)) as GrafanaModule;
455
+ } catch (err) {
456
+ throw new Error(
457
+ `apply target "grafana" needs @intentius/chant-lexicon-grafana, which could not be loaded ` +
458
+ `(${err instanceof Error ? err.message : String(err)}). Grafana applies go through the grafana ` +
459
+ `lexicon's API applier (chant #2948); install the grafana lexicon and list it in chant.config.ts.`,
460
+ );
461
+ }
462
+ const { grafanaApply, toApplyResult } = mod;
463
+ if (typeof grafanaApply !== "function") {
464
+ throw new Error(
465
+ "the installed @intentius/chant-lexicon-grafana exports no grafanaApply — it predates chant #2948",
466
+ );
467
+ }
468
+ if (typeof toApplyResult !== "function") {
469
+ throw new Error(
470
+ "the installed @intentius/chant-lexicon-grafana exports no toApplyResult — it predates chant #2948",
471
+ );
472
+ }
473
+ return async (args, signal) => toApplyResult(await grafanaApply(args, signal));
474
+ }
475
+
476
+ /**
477
+ * Load the sql lexicon's `clickhouseApply` (#3208). Same variable-specifier
478
+ * trick as {@link loadK8sApplier}, for the same reason.
479
+ */
480
+ async function loadClickHouseApplier(): Promise<ClickHouseApplier> {
481
+ const spec = "@intentius/chant-lexicon-sql/op/activities";
482
+ type SqlModule = {
483
+ clickhouseApply?: (args: Parameters<ClickHouseApplier>[0], signal?: AbortSignal) => Promise<unknown>;
484
+ toApplyResult?: (result: unknown) => ApplyResult;
485
+ };
486
+ let mod: SqlModule;
487
+ try {
488
+ mod = (await importLexiconPackage(spec)) as SqlModule;
489
+ } catch (err) {
490
+ throw new Error(
491
+ `apply target "clickhouse" needs @intentius/chant-lexicon-sql, which could not be loaded ` +
492
+ `(${err instanceof Error ? err.message : String(err)}). ClickHouse applies go through the sql ` +
493
+ `lexicon's applier (chant #3208); install the sql lexicon and list it in chant.config.ts.`,
494
+ );
495
+ }
496
+ const { clickhouseApply, toApplyResult } = mod;
497
+ if (typeof clickhouseApply !== "function") {
498
+ throw new Error("the installed @intentius/chant-lexicon-sql exports no clickhouseApply — it predates chant #3208");
499
+ }
500
+ if (typeof toApplyResult !== "function") {
501
+ throw new Error("the installed @intentius/chant-lexicon-sql exports no toApplyResult — it predates chant #3208");
502
+ }
503
+ return async (args, signal) => toApplyResult(await clickhouseApply(args, signal));
504
+ }
505
+
506
+ /**
507
+ * Load the sql lexicon's `postgresApply` (#3280). Same variable-specifier
508
+ * trick as {@link loadK8sApplier}, for the same reason.
509
+ */
510
+ async function loadPostgresApplier(): Promise<PostgresApplier> {
511
+ const spec = "@intentius/chant-lexicon-sql/op/activities";
512
+ type SqlModule = {
513
+ postgresApply?: (args: Parameters<PostgresApplier>[0], signal?: AbortSignal) => Promise<unknown>;
514
+ toApplyResult?: (result: unknown) => ApplyResult;
515
+ };
516
+ let mod: SqlModule;
517
+ try {
518
+ mod = (await importLexiconPackage(spec)) as SqlModule;
519
+ } catch (err) {
520
+ throw new Error(
521
+ `apply target "postgres" needs @intentius/chant-lexicon-sql, which could not be loaded ` +
522
+ `(${err instanceof Error ? err.message : String(err)}). Postgres applies go through the sql ` +
523
+ `lexicon's applier (chant #3280); install the sql lexicon and list it in chant.config.ts.`,
524
+ );
525
+ }
526
+ const { postgresApply, toApplyResult } = mod;
527
+ if (typeof postgresApply !== "function") {
528
+ throw new Error("the installed @intentius/chant-lexicon-sql exports no postgresApply — it predates chant #3280");
529
+ }
530
+ if (typeof toApplyResult !== "function") {
531
+ throw new Error("the installed @intentius/chant-lexicon-sql exports no toApplyResult — it predates chant #3208");
532
+ }
533
+ return async (args, signal) => toApplyResult(await postgresApply(args, signal));
534
+ }
535
+
366
536
  /**
367
537
  * Load the aws lexicon's `awsApply` (#1449). Same variable-specifier trick as
368
538
  * {@link loadK8sApplier}, for the same reason.
@@ -418,8 +588,11 @@ async function loadAwsRollback(): Promise<AwsRollback> {
418
588
  * manifests), so `dist`; kustomize's "output" is the kustomization DIRECTORY
419
589
  * the render reads. The file targets take the file their lexicon's build
420
590
  * conventionally emits: `template.json` for CloudFormation/ARM, `dist/gcp.yaml`
421
- * (the CNRM manifest) for gcp, `dist/fly.json` (the serialized plan) for fly.
422
- * Pure — exported for testing.
591
+ * (the CNRM manifest) for gcp, `dist/fly.json` (the serialized plan) for fly,
592
+ * `dist/grafana.json` (the index the grafana serializer writes, with the
593
+ * dashboard files beside it) for grafana, `dist/schema.json` (the sql
594
+ * lexicon's build output) for clickhouse and postgres. Pure — exported for
595
+ * testing.
423
596
  */
424
597
  export function defaultOutput(target: ApplyTarget): string {
425
598
  switch (target) {
@@ -430,6 +603,11 @@ export function defaultOutput(target: ApplyTarget): string {
430
603
  return "dist/gcp.yaml";
431
604
  case "fly":
432
605
  return "dist/fly.json";
606
+ case "grafana":
607
+ return "dist/grafana.json";
608
+ case "clickhouse":
609
+ case "postgres":
610
+ return "dist/schema.json";
433
611
  default:
434
612
  return "template.json";
435
613
  }
@@ -469,7 +647,9 @@ function collapseEnvelope(envelope: ApplyResult, label: string): NativeApplyResu
469
647
  * Apply declared source to the cloud via the target's native mechanism.
470
648
  * Deletes (when enabled) ride that mechanism's own delete path — marker-scoped
471
649
  * on `kubectl`, tag-scoped on `arm`, label-scoped on `gcp`, marker- and
472
- * app-scoped on `fly`, and stack-scoped on `cloudformation`, where the deploy
650
+ * app-scoped on `fly`, label-scoped on `grafana`, comment-marker-scoped on
651
+ * `clickhouse` and `postgres`, and stack-scoped on
652
+ * `cloudformation`, where the deploy
473
653
  * itself removes resources dropped from the template and `deleteMode` changes
474
654
  * nothing: a resource CFN did not create is not in the stack. See
475
655
  * {@link DeleteMode}.
@@ -477,7 +657,7 @@ function collapseEnvelope(envelope: ApplyResult, label: string): NativeApplyResu
477
657
  * The appliers are injectable so this dispatcher can be tested without the
478
658
  * product lexicons present; production resolves them through
479
659
  * {@link loadK8sApplier} / {@link loadAzureApplier} / {@link loadAwsApplier} /
480
- * {@link loadGcpApplier} / {@link loadFlyApplier}.
660
+ * {@link loadGcpApplier} / {@link loadFlyApplier} / {@link loadGrafanaApplier}.
481
661
  */
482
662
  export async function nativeApply(
483
663
  args: NativeApplyArgs,
@@ -488,6 +668,9 @@ export async function nativeApply(
488
668
  awsApplier?: AwsApplier,
489
669
  gcpApplier?: GcpApplier,
490
670
  flyApplier?: FlyApplier,
671
+ grafanaApplier?: GrafanaApplier,
672
+ clickhouseApplier?: ClickHouseApplier,
673
+ postgresApplier?: PostgresApplier,
491
674
  ): Promise<NativeApplyResult> {
492
675
  const output = args.output ?? defaultOutput(args.target);
493
676
  const deleteMode = args.deleteMode ?? "never";
@@ -568,6 +751,45 @@ export async function nativeApply(
568
751
  return collapseEnvelope(envelope, args.env);
569
752
  }
570
753
 
754
+ if (args.target === "grafana") {
755
+ // grafana (#3011): the grafana lexicon's API applier. Unlike gcp and fly,
756
+ // env is not just a label here: it selects `grafana.profiles.<env>`, the
757
+ // Grafana observe and export already read. The ownership marker comes from
758
+ // the project's chant.config.ts inside grafanaApply.
759
+ const apply = grafanaApplier ?? (await loadGrafanaApplier());
760
+ const envelope = await apply(
761
+ { indexPath: output, environment: args.env, prune: deleteMode !== "never" },
762
+ signal,
763
+ );
764
+ // No binding or a refused token rides the envelope as NOT-ATTEMPTED for
765
+ // every resource, as do library panels and anything else prune could
766
+ // not consider.
767
+ return collapseEnvelope(envelope, args.env);
768
+ }
769
+
770
+ if (args.target === "clickhouse") {
771
+ // clickhouse (#3208): the sql lexicon's applier. env selects
772
+ // `sql.profiles.<env>`, the server observe and import read; the marker
773
+ // comes from the project's chant.config.ts inside clickhouseApply.
774
+ const apply = clickhouseApplier ?? (await loadClickHouseApplier());
775
+ const envelope = await apply({ buildPath: output, environment: args.env, prune: deleteMode !== "never" }, signal);
776
+ // A refused rebuild and a column drop an additive apply withholds ride the
777
+ // envelope as NOT-ATTEMPTED, so an Op can gate on them.
778
+ return collapseEnvelope(envelope, args.env);
779
+ }
780
+
781
+ if (args.target === "postgres") {
782
+ // postgres (#3280): the sql lexicon's applier. env selects
783
+ // `sql.profiles.<env>`; the marker and the lock and statement timeouts
784
+ // come from the project's chant.config.ts inside postgresApply.
785
+ const apply = postgresApplier ?? (await loadPostgresApplier());
786
+ const envelope = await apply({ buildPath: output, environment: args.env, prune: deleteMode !== "never" }, signal);
787
+ // A refused expand-and-contract change, a column drop an additive apply
788
+ // withholds, and the objects of a rolled-back transaction ride the
789
+ // envelope as NOT-ATTEMPTED, so an Op can gate on them.
790
+ return collapseEnvelope(envelope, args.env);
791
+ }
792
+
571
793
  // cloudformation (#1449): the aws lexicon's native applier. env is the stack
572
794
  // name and output the template path; endpoint resolution (#1694) and
573
795
  // capability derivation (#980) are awsApply's own, so nothing else is passed.
@@ -70,6 +70,9 @@ export type {
70
70
  AzureApplier,
71
71
  GcpApplier,
72
72
  FlyApplier,
73
+ GrafanaApplier,
74
+ ClickHouseApplier,
75
+ PostgresApplier,
73
76
  AwsApplier,
74
77
  AwsRollback,
75
78
  } from "./apply";
@@ -18,23 +18,37 @@ import type { UpgradeCheckResult } from "../../codegen/pinned-upgrade";
18
18
  import type { RollingUpgradeResult } from "../../codegen/rolling-upgrade";
19
19
  import { tmpdir } from "node:os";
20
20
  import { join } from "node:path";
21
- import { writeFileSync, readFileSync } from "node:fs";
21
+ import { writeFileSync, readFileSync, mkdirSync } from "node:fs";
22
22
 
23
23
  // ── Classification ────────────────────────────────────────────────────
24
24
 
25
25
  describe("pinned vs rolling classification", () => {
26
- test("pinned lexicons", () => {
27
- for (const l of ["k8s", "gcp", "docker", "gitlab"] as const) {
28
- expect(isPinned(l)).toBe(true);
26
+ const pin = {
27
+ file: "src/pin.ts",
28
+ pattern: /V = "(.+)"/,
29
+ replace: (v: string) => `V = "${v}"`,
30
+ upstream: { owner: "o", repo: "r", kind: "releases" as const },
31
+ };
32
+
33
+ test("pinned lexicons are any whose plugin declares upstreamPin", async () => {
34
+ const loadPin = vi.fn(async () => pin);
35
+ for (const l of ["k8s", "gcp", "docker", "gitlab", "sql", "k3s", "cedar"]) {
36
+ expect(await isPinned(l, "/x", loadPin)).toBe(true);
29
37
  expect(isRolling(l)).toBe(false);
30
38
  }
31
39
  });
32
40
 
33
- test("rolling lexicons", () => {
41
+ test("a lexicon without a pin is not pinned", async () => {
42
+ expect(await isPinned("helm", "/x", async () => null)).toBe(false);
43
+ });
44
+
45
+ test("rolling lexicons", async () => {
46
+ const loadPin = vi.fn(async () => null);
34
47
  for (const l of ["aws", "azure", "github"] as const) {
35
48
  expect(isRolling(l)).toBe(true);
36
- expect(isPinned(l)).toBe(false);
49
+ expect(await isPinned(l, "/x", loadPin)).toBe(false);
37
50
  }
51
+ expect(loadPin).not.toHaveBeenCalled();
38
52
  });
39
53
  });
40
54
 
@@ -389,6 +403,86 @@ describe("lexiconUpgrade breakage handling", () => {
389
403
 
390
404
  // ── out-of-scope rejection ────────────────────────────────────────────
391
405
 
406
+ describe("discovered pinned lexicons (#3266)", () => {
407
+ const pin = {
408
+ file: "src/pin.ts",
409
+ pattern: /V = "(.+)"/,
410
+ replace: (v: string) => `V = "${v}"`,
411
+ upstream: { owner: "o", repo: "r", kind: "releases" as const },
412
+ };
413
+
414
+ test("a lexicon with a declared pin reaches the pinned check and opens a PR", async () => {
415
+ const checkPinned: CheckPinnedFn = vi.fn(async () => pinnedResult());
416
+ const applyBump = vi.fn(async () => ({ filePath: "/x/src/pin.ts" }));
417
+ const { gh } = recordingGh();
418
+ const dir = join(tmpdir(), `chant-3266-${process.pid}`);
419
+ mkdirSync(dir, { recursive: true });
420
+ writeFileSync(join(dir, "package.json"), JSON.stringify({ version: "0.1.0" }));
421
+ const r = await lexiconUpgrade({
422
+ lexicon: "k3s",
423
+ lexiconDir: dir,
424
+ mode: "pull-request",
425
+ _loadPin: async () => pin,
426
+ _checkPinned: checkPinned,
427
+ _applyBump: applyBump,
428
+ _bumpPackageVersion: vi.fn(),
429
+ _gh: gh,
430
+ });
431
+ expect(checkPinned).toHaveBeenCalledOnce();
432
+ expect(applyBump).toHaveBeenCalledWith("k3s", dir, "v1.33.0");
433
+ expect(r.hasUpgrade).toBe(true);
434
+ });
435
+
436
+ test("a lexicon with no pin and not rolling is rejected", async () => {
437
+ await expect(
438
+ lexiconUpgrade({ lexicon: "helm", mode: "report", _loadPin: async () => null }),
439
+ ).rejects.toThrow(/not supported/);
440
+ });
441
+
442
+ test("a discovered pin reaches the real check when none is injected", async () => {
443
+ const loadPin = vi.fn(async () => pin);
444
+ // The real checkPinnedUpgrade loads the pin itself; an empty dir has no
445
+ // plugin, so it reports the fetch error rather than throwing "not supported".
446
+ const r = await lexiconUpgrade({
447
+ lexicon: "sql",
448
+ lexiconDir: join(tmpdir(), "chant-3266-none"),
449
+ mode: "report",
450
+ _loadPin: loadPin,
451
+ });
452
+ expect(loadPin).toHaveBeenCalledOnce();
453
+ expect(r.validationOk).toBe(false);
454
+ });
455
+
456
+ test("an alsoMoves pin is report-only: newer version and manual step, no bump, no git", async () => {
457
+ const manual = pinnedResult({
458
+ validation: null,
459
+ from: "25.8.1.1-lts",
460
+ to: "26.3.1.1-lts",
461
+ manualPin: { file: "src/spec/pin.ts", instructions: "move CLICKHOUSE_IMAGE_DIGEST too" },
462
+ });
463
+ const applyBump = vi.fn();
464
+ const { gh, calls } = recordingGh();
465
+ for (const mode of ["report", "issue", "pull-request"] as const) {
466
+ const r = await lexiconUpgrade({
467
+ lexicon: "sql",
468
+ mode,
469
+ _checkPinned: vi.fn(async () => manual),
470
+ _applyBump: applyBump,
471
+ _gh: gh,
472
+ });
473
+ expect(r.hasUpgrade).toBe(true);
474
+ expect(r.mode).toBe("report");
475
+ expect(r.prUrl).toBeUndefined();
476
+ expect(r.issueUrl).toBeUndefined();
477
+ expect(r.summary).toContain("26.3.1.1-lts");
478
+ expect(r.summary).toContain("move CLICKHOUSE_IMAGE_DIGEST too");
479
+ expect(r.summary).toContain("src/spec/pin.ts");
480
+ }
481
+ expect(applyBump).not.toHaveBeenCalled();
482
+ expect(calls).toHaveLength(0);
483
+ });
484
+ });
485
+
392
486
  describe("out-of-scope lexicons", () => {
393
487
  test("helm/forgejo throw", async () => {
394
488
  for (const l of ["helm", "forgejo"]) {
@@ -585,3 +679,63 @@ describe("lexiconUpgrade pull-request mode: package.json version bump", () => {
585
679
  }
586
680
  });
587
681
  });
682
+
683
+ describe("a lexicon with several pins (#3288)", () => {
684
+ const manual = (pin: string, from: string, to: string): UpgradeCheckResult =>
685
+ pinnedResult({ pin, validation: null, from, to, manualPin: { file: "src/spec/postgres-pin.ts", instructions: `move the ${pin} digest too` } });
686
+ const current = (pin: string, v: string): UpgradeCheckResult => pinnedResult({ pin, validation: null, hasUpgrade: false, from: v, to: v });
687
+
688
+ test("every pin is reported; manual pins stay report-only in every mode", async () => {
689
+ const results = [current("clickhouse", "26.8.15.10"), manual("postgres-17", "17.11", "17.12"), manual("postgres-14", "14.24", "14.25")];
690
+ const applyBump = vi.fn();
691
+ const { gh, calls } = recordingGh();
692
+ for (const mode of ["report", "issue", "pull-request"] as const) {
693
+ const r = await lexiconUpgrade({ lexicon: "sql", mode, _checkPinned: vi.fn(async () => results), _applyBump: applyBump, _gh: gh });
694
+ expect(r.hasUpgrade).toBe(true);
695
+ expect(r.mode).toBe("report");
696
+ expect(r.summary).toContain("## Lexicon upgrade: sql (clickhouse)");
697
+ expect(r.summary).toContain("## Lexicon upgrade: sql (postgres-17)");
698
+ expect(r.summary).toContain("move the postgres-17 digest too");
699
+ expect(r.summary).toContain("move the postgres-14 digest too");
700
+ }
701
+ expect(applyBump).not.toHaveBeenCalled();
702
+ expect(calls).toHaveLength(0);
703
+ });
704
+
705
+ test("no pin has an upgrade: nothing to do", async () => {
706
+ const r = await lexiconUpgrade({
707
+ lexicon: "sql",
708
+ _checkPinned: vi.fn(async () => [current("clickhouse", "1.0"), current("postgres-18", "18.6")]),
709
+ });
710
+ expect(r.hasUpgrade).toBe(false);
711
+ expect(r.validationOk).toBe(true);
712
+ });
713
+
714
+ test("the pin label is forwarded to the check", async () => {
715
+ const check = vi.fn(async () => [current("postgres-16", "16.15")]);
716
+ await lexiconUpgrade({ lexicon: "sql", pin: "postgres-16", _checkPinned: check });
717
+ expect(check).toHaveBeenCalledWith(expect.objectContaining({ lexicon: "sql", pin: "postgres-16" }));
718
+ });
719
+
720
+ test("a rewritable pin leads: the PR branch, title and bump name the pin", async () => {
721
+ const results = [manual("clickhouse", "1.0", "2.0"), pinnedResult({ pin: "postgres-17", from: "17.11", to: "17.12" })];
722
+ const applyBump = vi.fn(async () => ({ filePath: "/x/pin.ts" }));
723
+ const { gh, calls } = recordingGh();
724
+ const dir = join(tmpdir(), `chant-3288-${process.pid}`);
725
+ mkdirSync(dir, { recursive: true });
726
+ writeFileSync(join(dir, "package.json"), JSON.stringify({ version: "0.1.0" }));
727
+ await lexiconUpgrade({
728
+ lexicon: "sql",
729
+ mode: "pull-request",
730
+ lexiconDir: dir,
731
+ _checkPinned: vi.fn(async () => results),
732
+ _applyBump: applyBump,
733
+ _bumpPackageVersion: vi.fn(() => "/x/package.json"),
734
+ _gh: gh,
735
+ });
736
+ expect(applyBump).toHaveBeenCalledWith("sql", dir, "17.12", "postgres-17");
737
+ expect(calls.some((c) => c.includes("lexicon-upgrade/sql-postgres-17"))).toBe(true);
738
+ expect(upgradeBranchName("sql", "postgres-17")).toBe("lexicon-upgrade/sql-postgres-17");
739
+ expect(upgradePrTitle("sql", "17.11", "17.12", "postgres-17")).toBe("feat(sql): upgrade postgres-17 17.11 to 17.12");
740
+ });
741
+ });