@intentius/chant 0.101.0 → 0.104.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 (739) 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/core.d.ts +2 -0
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts.map +1 -1
  6. package/dist/audit/nginx.d.ts.map +1 -1
  7. package/dist/audit/prior-art.d.ts +48 -0
  8. package/dist/audit/prior-art.d.ts.map +1 -1
  9. package/dist/behaviour-kinds.d.ts.map +1 -1
  10. package/dist/builder.d.ts.map +1 -1
  11. package/dist/change-set.d.ts +232 -0
  12. package/dist/change-set.d.ts.map +1 -0
  13. package/dist/cli/commands/audit.d.ts +7 -0
  14. package/dist/cli/commands/audit.d.ts.map +1 -1
  15. package/dist/cli/commands/build.d.ts.map +1 -1
  16. package/dist/cli/commands/carve-emit.d.ts.map +1 -1
  17. package/dist/cli/commands/check-lexicon.d.ts +16 -0
  18. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  19. package/dist/cli/commands/import.d.ts +8 -0
  20. package/dist/cli/commands/import.d.ts.map +1 -1
  21. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  22. package/dist/cli/commands/init.d.ts +19 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -1
  24. package/dist/cli/commands/onboard.d.ts +9 -0
  25. package/dist/cli/commands/onboard.d.ts.map +1 -1
  26. package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
  27. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
  28. package/dist/cli/handlers/build.d.ts.map +1 -1
  29. package/dist/cli/handlers/change-set.d.ts +11 -0
  30. package/dist/cli/handlers/change-set.d.ts.map +1 -0
  31. package/dist/cli/handlers/dev.d.ts.map +1 -1
  32. package/dist/cli/handlers/fan-out.d.ts +33 -0
  33. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  34. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  35. package/dist/cli/handlers/misc.d.ts.map +1 -1
  36. package/dist/cli/handlers/operator.d.ts +16 -0
  37. package/dist/cli/handlers/operator.d.ts.map +1 -1
  38. package/dist/cli/handlers/pr.d.ts +31 -0
  39. package/dist/cli/handlers/pr.d.ts.map +1 -0
  40. package/dist/cli/handlers/run.d.ts.map +1 -1
  41. package/dist/cli/lsp/server.d.ts.map +1 -1
  42. package/dist/cli/main.d.ts.map +1 -1
  43. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  44. package/dist/cli/mcp/server.d.ts.map +1 -1
  45. package/dist/cli/mcp/tools/build.d.ts.map +1 -1
  46. package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
  47. package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
  48. package/dist/cli/mcp/tools/import.d.ts.map +1 -1
  49. package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
  50. package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
  51. package/dist/cli/mcp/tools/search.d.ts.map +1 -1
  52. package/dist/cli/mcp/workspace-tools.d.ts +4 -3
  53. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  54. package/dist/cli/registry.d.ts +74 -3
  55. package/dist/cli/registry.d.ts.map +1 -1
  56. package/dist/codegen/fetch.d.ts.map +1 -1
  57. package/dist/codegen/naming.d.ts.map +1 -1
  58. package/dist/codegen/pinned-upgrade.d.ts +67 -3
  59. package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
  60. package/dist/codegen/registry.d.ts.map +1 -1
  61. package/dist/components/auto-release.d.ts +16 -0
  62. package/dist/components/auto-release.d.ts.map +1 -1
  63. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  64. package/dist/components/capability.d.ts +53 -0
  65. package/dist/components/capability.d.ts.map +1 -1
  66. package/dist/components/changed-files.d.ts +31 -0
  67. package/dist/components/changed-files.d.ts.map +1 -0
  68. package/dist/components/cli-support.d.ts +9 -1
  69. package/dist/components/cli-support.d.ts.map +1 -1
  70. package/dist/components/component.d.ts +24 -0
  71. package/dist/components/component.d.ts.map +1 -1
  72. package/dist/components/deploy-units.d.ts.map +1 -1
  73. package/dist/components/driver.d.ts +13 -0
  74. package/dist/components/driver.d.ts.map +1 -1
  75. package/dist/components/fan-out-output.d.ts +6 -0
  76. package/dist/components/fan-out-output.d.ts.map +1 -1
  77. package/dist/components/fan-out-record.d.ts +41 -0
  78. package/dist/components/fan-out-record.d.ts.map +1 -0
  79. package/dist/components/fan-out-run.d.ts +65 -0
  80. package/dist/components/fan-out-run.d.ts.map +1 -1
  81. package/dist/components/fan-out-support.d.ts +2 -0
  82. package/dist/components/fan-out-support.d.ts.map +1 -1
  83. package/dist/components/fan-out.d.ts +6 -0
  84. package/dist/components/fan-out.d.ts.map +1 -1
  85. package/dist/components/gated-wave-pipeline.d.ts +50 -0
  86. package/dist/components/gated-wave-pipeline.d.ts.map +1 -0
  87. package/dist/components/index.d.ts +1 -1
  88. package/dist/components/index.d.ts.map +1 -1
  89. package/dist/components/layers.d.ts +25 -0
  90. package/dist/components/layers.d.ts.map +1 -0
  91. package/dist/components/pr-pipeline.d.ts +61 -0
  92. package/dist/components/pr-pipeline.d.ts.map +1 -0
  93. package/dist/components/pr-run.d.ts +119 -0
  94. package/dist/components/pr-run.d.ts.map +1 -0
  95. package/dist/components/promote.d.ts.map +1 -1
  96. package/dist/components/starter-plugin.d.ts.map +1 -1
  97. package/dist/components/verbs/process-runner.d.ts.map +1 -1
  98. package/dist/components/verbs/r2-sync.d.ts.map +1 -1
  99. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  100. package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
  101. package/dist/components/verbs/shell.d.ts.map +1 -1
  102. package/dist/components/verbs/sign.d.ts.map +1 -1
  103. package/dist/components/verbs/verify.d.ts.map +1 -1
  104. package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
  105. package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
  106. package/dist/components/verbs/wrangler.d.ts.map +1 -1
  107. package/dist/components/wave-plan.d.ts +49 -0
  108. package/dist/components/wave-plan.d.ts.map +1 -0
  109. package/dist/composite.d.ts.map +1 -1
  110. package/dist/config.d.ts +19 -7
  111. package/dist/config.d.ts.map +1 -1
  112. package/dist/declarable.d.ts +16 -0
  113. package/dist/declarable.d.ts.map +1 -1
  114. package/dist/discovery/cache.d.ts.map +1 -1
  115. package/dist/discovery/fold-import.d.ts +15 -5
  116. package/dist/discovery/fold-import.d.ts.map +1 -1
  117. package/dist/discovery/param-deps.d.ts +16 -0
  118. package/dist/discovery/param-deps.d.ts.map +1 -1
  119. package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
  120. package/dist/discovery/specifier-resolve.d.ts +37 -0
  121. package/dist/discovery/specifier-resolve.d.ts.map +1 -0
  122. package/dist/errors.d.ts.map +1 -1
  123. package/dist/fold/fold.d.ts +3 -1
  124. package/dist/fold/fold.d.ts.map +1 -1
  125. package/dist/gated-waves.d.ts +147 -0
  126. package/dist/gated-waves.d.ts.map +1 -0
  127. package/dist/graph-detail.d.ts.map +1 -1
  128. package/dist/graph-ir.d.ts.map +1 -1
  129. package/dist/graph-layout.d.ts.map +1 -1
  130. package/dist/import/base-parser.d.ts.map +1 -1
  131. package/dist/import/embedded.d.ts +7 -0
  132. package/dist/import/embedded.d.ts.map +1 -1
  133. package/dist/import/parser-options.d.ts +14 -0
  134. package/dist/import/parser-options.d.ts.map +1 -0
  135. package/dist/import/parser.d.ts +17 -0
  136. package/dist/import/parser.d.ts.map +1 -1
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/kubectl-context.d.ts.map +1 -1
  140. package/dist/lexicon-output.d.ts.map +1 -1
  141. package/dist/lexicon-schema.d.ts +3 -3
  142. package/dist/lexicon.d.ts +103 -3
  143. package/dist/lexicon.d.ts.map +1 -1
  144. package/dist/lifecycle/assert-live.d.ts.map +1 -1
  145. package/dist/lifecycle/gate-ledger.d.ts +22 -0
  146. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  147. package/dist/lifecycle/git.d.ts +46 -1
  148. package/dist/lifecycle/git.d.ts.map +1 -1
  149. package/dist/lifecycle/member-ledger.d.ts.map +1 -1
  150. package/dist/lifecycle/observe.d.ts.map +1 -1
  151. package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
  152. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  153. package/dist/lifecycle/scenario.d.ts.map +1 -1
  154. package/dist/lifecycle/status.d.ts.map +1 -1
  155. package/dist/lifecycle/work-lease.d.ts +61 -2
  156. package/dist/lifecycle/work-lease.d.ts.map +1 -1
  157. package/dist/lint/config.d.ts +8 -8
  158. package/dist/lint/engine.d.ts +6 -0
  159. package/dist/lint/engine.d.ts.map +1 -1
  160. package/dist/lint/post-synth.d.ts +18 -0
  161. package/dist/lint/post-synth.d.ts.map +1 -1
  162. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  163. package/dist/op/activities/activity-contracts.d.ts +137 -11
  164. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  165. package/dist/op/activities/apply.d.ts +79 -9
  166. package/dist/op/activities/apply.d.ts.map +1 -1
  167. package/dist/op/activities/change-set.d.ts +62 -0
  168. package/dist/op/activities/change-set.d.ts.map +1 -0
  169. package/dist/op/activities/decide.d.ts +2 -0
  170. package/dist/op/activities/decide.d.ts.map +1 -1
  171. package/dist/op/activities/factory.d.ts +137 -0
  172. package/dist/op/activities/factory.d.ts.map +1 -0
  173. package/dist/op/activities/index.d.ts +5 -1
  174. package/dist/op/activities/index.d.ts.map +1 -1
  175. package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
  176. package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
  177. package/dist/op/activity-contract.d.ts +1 -1
  178. package/dist/op/activity-contract.d.ts.map +1 -1
  179. package/dist/op/activity-failure.d.ts.map +1 -1
  180. package/dist/op/activity-profiles.d.ts.map +1 -1
  181. package/dist/op/builders.d.ts +11 -0
  182. package/dist/op/builders.d.ts.map +1 -1
  183. package/dist/op/composites/apply-op.d.ts +12 -5
  184. package/dist/op/composites/apply-op.d.ts.map +1 -1
  185. package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
  186. package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
  187. package/dist/op/decide-backend.d.ts +5 -1
  188. package/dist/op/decide-backend.d.ts.map +1 -1
  189. package/dist/op/decide-read-inputs.d.ts +4 -2
  190. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  191. package/dist/op/emulator-lifecycle.d.ts +21 -2
  192. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  193. package/dist/op/factory.d.ts +83 -0
  194. package/dist/op/factory.d.ts.map +1 -0
  195. package/dist/op/gate-approval.d.ts +5 -1
  196. package/dist/op/gate-approval.d.ts.map +1 -1
  197. package/dist/op/gate-wait.d.ts.map +1 -1
  198. package/dist/op/gate.d.ts +19 -3
  199. package/dist/op/gate.d.ts.map +1 -1
  200. package/dist/op/index.d.ts +6 -1
  201. package/dist/op/index.d.ts.map +1 -1
  202. package/dist/op/local-executor.d.ts +6 -0
  203. package/dist/op/local-executor.d.ts.map +1 -1
  204. package/dist/op/op-ir.d.ts +2 -0
  205. package/dist/op/op-ir.d.ts.map +1 -1
  206. package/dist/op/op-verb-class.d.ts.map +1 -1
  207. package/dist/op/operator.d.ts.map +1 -1
  208. package/dist/op/run-context.d.ts +39 -0
  209. package/dist/op/run-context.d.ts.map +1 -0
  210. package/dist/op/run-live.d.ts.map +1 -1
  211. package/dist/op/runtimes/local.d.ts.map +1 -1
  212. package/dist/op/source-archive.d.ts.map +1 -1
  213. package/dist/op/steward-beside.d.ts.map +1 -1
  214. package/dist/op/steward-points.d.ts.map +1 -1
  215. package/dist/op/steward.d.ts.map +1 -1
  216. package/dist/op/types.d.ts +16 -3
  217. package/dist/op/types.d.ts.map +1 -1
  218. package/dist/op/work-lease-run.d.ts +24 -4
  219. package/dist/op/work-lease-run.d.ts.map +1 -1
  220. package/dist/plan-summary.d.ts +207 -0
  221. package/dist/plan-summary.d.ts.map +1 -0
  222. package/dist/pr-forge.d.ts +105 -0
  223. package/dist/pr-forge.d.ts.map +1 -0
  224. package/dist/pr-loop.d.ts +178 -0
  225. package/dist/pr-loop.d.ts.map +1 -0
  226. package/dist/project-root.d.ts.map +1 -1
  227. package/dist/provenance.d.ts +21 -0
  228. package/dist/provenance.d.ts.map +1 -1
  229. package/dist/pseudo-parameter.d.ts.map +1 -1
  230. package/dist/reconcile.d.ts.map +1 -1
  231. package/dist/secret-materialization.d.ts.map +1 -1
  232. package/dist/telemetry-attribution.d.ts +31 -0
  233. package/dist/telemetry-attribution.d.ts.map +1 -1
  234. package/dist/terraform/emit-source.d.ts.map +1 -1
  235. package/dist/terraform/hcl2json.d.ts +45 -0
  236. package/dist/terraform/hcl2json.d.ts.map +1 -0
  237. package/dist/terraform/parse.d.ts +4 -40
  238. package/dist/terraform/parse.d.ts.map +1 -1
  239. package/dist/testing.d.ts.map +1 -1
  240. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  241. package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
  242. package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
  243. package/dist/workspace/agent-cli.d.ts +5 -0
  244. package/dist/workspace/agent-cli.d.ts.map +1 -1
  245. package/dist/workspace/box-cli.d.ts +12 -0
  246. package/dist/workspace/box-cli.d.ts.map +1 -0
  247. package/dist/workspace/box-factory.d.ts +97 -0
  248. package/dist/workspace/box-factory.d.ts.map +1 -0
  249. package/dist/workspace/box-listing.d.ts +85 -0
  250. package/dist/workspace/box-listing.d.ts.map +1 -0
  251. package/dist/workspace/box-publish.d.ts +147 -0
  252. package/dist/workspace/box-publish.d.ts.map +1 -0
  253. package/dist/workspace/broker-protocol.d.ts +190 -0
  254. package/dist/workspace/broker-protocol.d.ts.map +1 -0
  255. package/dist/workspace/changes.d.ts.map +1 -1
  256. package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
  257. package/dist/workspace/checks/boxes.d.ts.map +1 -1
  258. package/dist/workspace/checks.d.ts +5 -0
  259. package/dist/workspace/checks.d.ts.map +1 -1
  260. package/dist/workspace/compose-graph.d.ts +10 -0
  261. package/dist/workspace/compose-graph.d.ts.map +1 -1
  262. package/dist/workspace/composites.d.ts +2 -2
  263. package/dist/workspace/composites.d.ts.map +1 -1
  264. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +17 -0
  265. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -0
  266. package/dist/workspace/conformance/broker.d.ts +202 -0
  267. package/dist/workspace/conformance/broker.d.ts.map +1 -0
  268. package/dist/workspace/conformance/index.d.ts +68 -6
  269. package/dist/workspace/conformance/index.d.ts.map +1 -1
  270. package/dist/workspace/conformance/vitest.d.ts +19 -3
  271. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  272. package/dist/workspace/conformance/writer.d.ts +462 -0
  273. package/dist/workspace/conformance/writer.d.ts.map +1 -0
  274. package/dist/workspace/decide.d.ts +126 -12
  275. package/dist/workspace/decide.d.ts.map +1 -1
  276. package/dist/workspace/decision-points.schema.json +11 -4
  277. package/dist/workspace/declaration.d.ts +156 -4
  278. package/dist/workspace/declaration.d.ts.map +1 -1
  279. package/dist/workspace/declaration.schema.json +383 -5
  280. package/dist/workspace/environments.d.ts +36 -10
  281. package/dist/workspace/environments.d.ts.map +1 -1
  282. package/dist/workspace/factory-rules.d.ts +180 -0
  283. package/dist/workspace/factory-rules.d.ts.map +1 -0
  284. package/dist/workspace/generated-files.d.ts.map +1 -1
  285. package/dist/workspace/graph-cache.d.ts.map +1 -1
  286. package/dist/workspace/graph-cli.d.ts +2 -0
  287. package/dist/workspace/graph-cli.d.ts.map +1 -1
  288. package/dist/workspace/identity.d.ts +111 -0
  289. package/dist/workspace/identity.d.ts.map +1 -0
  290. package/dist/workspace/init.d.ts +21 -0
  291. package/dist/workspace/init.d.ts.map +1 -1
  292. package/dist/workspace/intent-cli.d.ts +7 -4
  293. package/dist/workspace/intent-cli.d.ts.map +1 -1
  294. package/dist/workspace/intent-record.d.ts +36 -2
  295. package/dist/workspace/intent-record.d.ts.map +1 -1
  296. package/dist/workspace/intent.d.ts +249 -8
  297. package/dist/workspace/intent.d.ts.map +1 -1
  298. package/dist/workspace/json-edit.d.ts +24 -0
  299. package/dist/workspace/json-edit.d.ts.map +1 -0
  300. package/dist/workspace/jsonc.d.ts +11 -0
  301. package/dist/workspace/jsonc.d.ts.map +1 -1
  302. package/dist/workspace/kinds.d.ts +66 -4
  303. package/dist/workspace/kinds.d.ts.map +1 -1
  304. package/dist/workspace/lexicon-symbols.d.ts +29 -0
  305. package/dist/workspace/lexicon-symbols.d.ts.map +1 -0
  306. package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
  307. package/dist/workspace/lineage-lock.d.ts +2 -2
  308. package/dist/workspace/lineage-lock.d.ts.map +1 -1
  309. package/dist/workspace/lineage-upgrade.d.ts +31 -2
  310. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  311. package/dist/workspace/links.d.ts.map +1 -1
  312. package/dist/workspace/point-answer.schema.json +42 -2
  313. package/dist/workspace/points-cli.d.ts +10 -3
  314. package/dist/workspace/points-cli.d.ts.map +1 -1
  315. package/dist/workspace/points.d.ts +66 -5
  316. package/dist/workspace/points.d.ts.map +1 -1
  317. package/dist/workspace/principal-classes.d.ts +114 -0
  318. package/dist/workspace/principal-classes.d.ts.map +1 -0
  319. package/dist/workspace/reason-codes.d.ts +53 -4
  320. package/dist/workspace/reason-codes.d.ts.map +1 -1
  321. package/dist/workspace/record-assets.d.ts.map +1 -1
  322. package/dist/workspace/record-sessions.d.ts +5 -7
  323. package/dist/workspace/record-sessions.d.ts.map +1 -1
  324. package/dist/workspace/records-checkout.d.ts +54 -0
  325. package/dist/workspace/records-checkout.d.ts.map +1 -0
  326. package/dist/workspace/records-cli.d.ts +34 -1
  327. package/dist/workspace/records-cli.d.ts.map +1 -1
  328. package/dist/workspace/records-close.d.ts +7 -3
  329. package/dist/workspace/records-close.d.ts.map +1 -1
  330. package/dist/workspace/records-since.d.ts.map +1 -1
  331. package/dist/workspace/records-write.d.ts +68 -7
  332. package/dist/workspace/records-write.d.ts.map +1 -1
  333. package/dist/workspace/records.d.ts +43 -9
  334. package/dist/workspace/records.d.ts.map +1 -1
  335. package/dist/workspace/runs-cli.d.ts +296 -0
  336. package/dist/workspace/runs-cli.d.ts.map +1 -0
  337. package/dist/workspace/runs.d.ts +544 -0
  338. package/dist/workspace/runs.d.ts.map +1 -0
  339. package/dist/workspace/session-kinds.d.ts +1 -1
  340. package/dist/workspace/session-kinds.d.ts.map +1 -1
  341. package/dist/workspace/source-block.d.ts +1 -1
  342. package/dist/workspace/source-block.d.ts.map +1 -1
  343. package/dist/workspace/squash.d.ts +80 -0
  344. package/dist/workspace/squash.d.ts.map +1 -0
  345. package/dist/workspace/status-gates.d.ts +17 -3
  346. package/dist/workspace/status-gates.d.ts.map +1 -1
  347. package/dist/workspace/status.d.ts +64 -0
  348. package/dist/workspace/status.d.ts.map +1 -1
  349. package/dist/workspace/symbols.d.ts +63 -0
  350. package/dist/workspace/symbols.d.ts.map +1 -0
  351. package/dist/workspace/trailer-joins.d.ts +46 -0
  352. package/dist/workspace/trailer-joins.d.ts.map +1 -0
  353. package/dist/workspace/trailers.d.ts +89 -0
  354. package/dist/workspace/trailers.d.ts.map +1 -0
  355. package/dist/workspace/trust/attestor.d.ts.map +1 -1
  356. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  357. package/dist/workspace/trust/run-commits.d.ts +46 -0
  358. package/dist/workspace/trust/run-commits.d.ts.map +1 -0
  359. package/dist/workspace/trust/run-statement.d.ts +128 -0
  360. package/dist/workspace/trust/run-statement.d.ts.map +1 -0
  361. package/dist/workspace/trust/seal.d.ts +38 -0
  362. package/dist/workspace/trust/seal.d.ts.map +1 -1
  363. package/dist/workspace/trust/test-repo.d.ts.map +1 -1
  364. package/dist/workspace/trust/verify-cli.d.ts +1 -1
  365. package/dist/workspace/trust/verify.d.ts +16 -2
  366. package/dist/workspace/trust/verify.d.ts.map +1 -1
  367. package/dist/workspace/wip-cli.d.ts +21 -0
  368. package/dist/workspace/wip-cli.d.ts.map +1 -0
  369. package/dist/workspace/wip.d.ts +268 -0
  370. package/dist/workspace/wip.d.ts.map +1 -0
  371. package/dist/workspace/work-cli.d.ts +39 -3
  372. package/dist/workspace/work-cli.d.ts.map +1 -1
  373. package/dist/workspace/work-evidence.d.ts +1 -1
  374. package/dist/workspace/work-evidence.d.ts.map +1 -1
  375. package/dist/workspace/work-size.d.ts +55 -0
  376. package/dist/workspace/work-size.d.ts.map +1 -0
  377. package/dist/workspace/work.d.ts +26 -1
  378. package/dist/workspace/work.d.ts.map +1 -1
  379. package/dist/workspace/workspace-kinds.schema.json +122 -0
  380. package/dist/workspace/workspace-principals.schema.json +60 -0
  381. package/dist/workspace/write-lock-cli.d.ts +60 -0
  382. package/dist/workspace/write-lock-cli.d.ts.map +1 -0
  383. package/dist/workspace/write-lock.d.ts +152 -0
  384. package/dist/workspace/write-lock.d.ts.map +1 -0
  385. package/dist/workspace/write-scope.d.ts +83 -19
  386. package/dist/workspace/write-scope.d.ts.map +1 -1
  387. package/dist/yaml.d.ts +21 -39
  388. package/dist/yaml.d.ts.map +1 -1
  389. package/package.json +7 -2
  390. package/src/__fixtures__/change-set/lifecycle-plan.floci.json +59 -0
  391. package/src/__fixtures__/change-set/lifecycle-v1.ts.txt +4 -0
  392. package/src/__fixtures__/change-set/lifecycle-v2.ts.txt +4 -0
  393. package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +95 -0
  394. package/src/__fixtures__/change-set/warden.change-set.golden.json +106 -0
  395. package/src/__fixtures__/change-set/warden.plan.json +44 -0
  396. package/src/__fixtures__/plan-summary/normalization-vectors.json +1593 -0
  397. package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +31 -0
  398. package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +31 -0
  399. package/src/__fixtures__/pr-loop/pr-plan.golden.json +127 -0
  400. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +37 -0
  401. package/src/audit/core.test.ts +15 -0
  402. package/src/audit/core.ts +8 -2
  403. package/src/audit/discover.test.ts +11 -0
  404. package/src/audit/discover.ts +7 -2
  405. package/src/audit/prior-art.ts +15 -0
  406. package/src/change-set-bundle.test.ts +57 -0
  407. package/src/change-set.test.ts +215 -0
  408. package/src/change-set.ts +445 -0
  409. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
  410. package/src/cli/commands/audit-opt-in.test.ts +54 -0
  411. package/src/cli/commands/audit.ts +29 -2
  412. package/src/cli/commands/build.test.ts +19 -0
  413. package/src/cli/commands/build.ts +3 -2
  414. package/src/cli/commands/carve-emit-state.test.ts +4 -0
  415. package/src/cli/commands/carve-emit.ts +5 -19
  416. package/src/cli/commands/check-lexicon.test.ts +62 -1
  417. package/src/cli/commands/check-lexicon.ts +36 -6
  418. package/src/cli/commands/import-live.test.ts +18 -0
  419. package/src/cli/commands/import-parser-options.test.ts +133 -0
  420. package/src/cli/commands/import.ts +21 -3
  421. package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
  422. package/src/cli/commands/init.test.ts +8 -1
  423. package/src/cli/commands/init.ts +23 -8
  424. package/src/cli/commands/lint.test.ts +37 -0
  425. package/src/cli/commands/onboard.test.ts +53 -3
  426. package/src/cli/commands/onboard.ts +52 -4
  427. package/src/cli/commands/pinned-upgrade.ts +73 -33
  428. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
  429. package/src/cli/handlers/build.ts +13 -1
  430. package/src/cli/handlers/change-set.test.ts +56 -0
  431. package/src/cli/handlers/change-set.ts +44 -0
  432. package/src/cli/handlers/components.ts +2 -2
  433. package/src/cli/handlers/dev.ts +8 -6
  434. package/src/cli/handlers/fan-out.test.ts +221 -2
  435. package/src/cli/handlers/fan-out.ts +165 -75
  436. package/src/cli/handlers/graph.ts +3 -0
  437. package/src/cli/handlers/lifecycle.test.ts +67 -0
  438. package/src/cli/handlers/lifecycle.ts +6 -5
  439. package/src/cli/handlers/misc.ts +2 -0
  440. package/src/cli/handlers/operator.test.ts +21 -0
  441. package/src/cli/handlers/operator.ts +121 -6
  442. package/src/cli/handlers/pr.ts +373 -0
  443. package/src/cli/handlers/run.test.ts +111 -0
  444. package/src/cli/handlers/run.ts +86 -24
  445. package/src/cli/handlers/scenario.ts +2 -2
  446. package/src/cli/main.test.ts +21 -0
  447. package/src/cli/main.ts +246 -30
  448. package/src/cli/mcp/never-over-mcp.test.ts +101 -0
  449. package/src/cli/mcp/op-tools.ts +24 -2
  450. package/src/cli/mcp/workspace-tools.test.ts +5 -2
  451. package/src/cli/mcp/workspace-tools.ts +62 -7
  452. package/src/cli/registry.ts +74 -3
  453. package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
  454. package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
  455. package/src/codegen/pinned-upgrade.test.ts +104 -0
  456. package/src/codegen/pinned-upgrade.ts +250 -56
  457. package/src/codegen/testdata/postgres-tags.json +242 -0
  458. package/src/components/auto-release.ts +1 -1
  459. package/src/components/capability.ts +55 -0
  460. package/src/components/changed-files.ts +66 -0
  461. package/src/components/cli-support.ts +28 -5
  462. package/src/components/component.schema.json +17 -0
  463. package/src/components/component.ts +25 -0
  464. package/src/components/deploy-units.ts +3 -0
  465. package/src/components/driver.test.ts +52 -0
  466. package/src/components/driver.ts +31 -13
  467. package/src/components/fan-out-output.ts +42 -1
  468. package/src/components/fan-out-record.ts +69 -0
  469. package/src/components/fan-out-run.test.ts +17 -0
  470. package/src/components/fan-out-run.ts +304 -1
  471. package/src/components/fan-out-support.ts +3 -0
  472. package/src/components/fan-out-waves.test.ts +375 -0
  473. package/src/components/fan-out.ts +18 -17
  474. package/src/components/gated-wave-pipeline.ts +82 -0
  475. package/src/components/index.ts +1 -0
  476. package/src/components/layers.ts +30 -0
  477. package/src/components/pr-pipeline.ts +91 -0
  478. package/src/components/pr-run.test.ts +205 -0
  479. package/src/components/pr-run.ts +280 -0
  480. package/src/components/promote.ts +5 -0
  481. package/src/components/verbs/shell.test.ts +11 -0
  482. package/src/components/verbs/shell.ts +7 -3
  483. package/src/components/wave-plan.ts +124 -0
  484. package/src/config.test.ts +21 -0
  485. package/src/config.ts +15 -0
  486. package/src/declarable.test.ts +21 -0
  487. package/src/declarable.ts +19 -0
  488. package/src/discovery/fold-composite.test.ts +41 -5
  489. package/src/discovery/fold-import.test.ts +129 -0
  490. package/src/discovery/fold-import.ts +643 -131
  491. package/src/discovery/fold-package-composite.test.ts +264 -0
  492. package/src/discovery/fold-specifiers.test.ts +191 -0
  493. package/src/discovery/param-deps.test.ts +66 -1
  494. package/src/discovery/param-deps.ts +45 -0
  495. package/src/discovery/sandbox/bundle.ts +1 -1
  496. package/src/discovery/specifier-resolve.ts +344 -0
  497. package/src/fold/fold.test.ts +39 -1
  498. package/src/fold/fold.ts +83 -9
  499. package/src/fold-provenance.test.ts +140 -0
  500. package/src/gated-waves-bundle.test.ts +62 -0
  501. package/src/gated-waves.test.ts +118 -0
  502. package/src/gated-waves.ts +240 -0
  503. package/src/graph-ir.test.ts +83 -0
  504. package/src/graph-ir.ts +29 -0
  505. package/src/import/embedded.test.ts +20 -0
  506. package/src/import/embedded.ts +13 -0
  507. package/src/import/parser-options.ts +46 -0
  508. package/src/import/parser.ts +20 -0
  509. package/src/index.ts +1 -0
  510. package/src/lexicon.ts +106 -3
  511. package/src/lifecycle/gate-ledger.ts +23 -0
  512. package/src/lifecycle/git.ts +102 -16
  513. package/src/lifecycle/observe.test.ts +18 -0
  514. package/src/lifecycle/observe.ts +6 -4
  515. package/src/lifecycle/snapshot.ts +2 -2
  516. package/src/lifecycle/work-lease.test.ts +1 -1
  517. package/src/lifecycle/work-lease.ts +93 -2
  518. package/src/lint/engine.test.ts +55 -0
  519. package/src/lint/engine.ts +30 -4
  520. package/src/lint/output-docs.test.ts +59 -56
  521. package/src/lint/post-synth.test.ts +19 -0
  522. package/src/lint/post-synth.ts +19 -1
  523. package/src/meta/declared-imports.test.ts +1 -1
  524. package/src/op/activities/activity-contracts.ts +84 -0
  525. package/src/op/activities/apply.test.ts +189 -5
  526. package/src/op/activities/apply.ts +230 -8
  527. package/src/op/activities/change-set.ts +114 -0
  528. package/src/op/activities/decide.ts +3 -0
  529. package/src/op/activities/factory.ts +675 -0
  530. package/src/op/activities/index.ts +9 -0
  531. package/src/op/activities/lexicon-upgrade.test.ts +160 -6
  532. package/src/op/activities/lexicon-upgrade.ts +154 -51
  533. package/src/op/activity-contract.test.ts +21 -1
  534. package/src/op/activity-contract.ts +19 -9
  535. package/src/op/builders.ts +23 -0
  536. package/src/op/composites/apply-op.ts +12 -5
  537. package/src/op/composites/composites.test.ts +30 -3
  538. package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
  539. package/src/op/composites/lexicon-upgrade-op.ts +29 -8
  540. package/src/op/decide-backend.ts +5 -1
  541. package/src/op/decide-read-inputs.ts +33 -5
  542. package/src/op/emulator-lifecycle.test.ts +23 -0
  543. package/src/op/emulator-lifecycle.ts +52 -14
  544. package/src/op/factory.e2e.test.ts +232 -0
  545. package/src/op/factory.ts +118 -0
  546. package/src/op/gate-approval.test.ts +74 -0
  547. package/src/op/gate-approval.ts +5 -1
  548. package/src/op/gate.ts +31 -5
  549. package/src/op/index.ts +11 -1
  550. package/src/op/local-executor.test.ts +15 -0
  551. package/src/op/local-executor.ts +40 -12
  552. package/src/op/local-output.ts +1 -1
  553. package/src/op/op-ir.test.ts +9 -0
  554. package/src/op/op-ir.ts +4 -0
  555. package/src/op/op-verb-class.ts +3 -0
  556. package/src/op/operator.ts +4 -0
  557. package/src/op/run-context.test.ts +141 -0
  558. package/src/op/run-context.ts +51 -0
  559. package/src/op/runtimes/local.test.ts +14 -0
  560. package/src/op/runtimes/local.ts +2 -0
  561. package/src/op/steward-beside.ts +2 -0
  562. package/src/op/types.ts +16 -3
  563. package/src/op/work-lease-run.test.ts +47 -0
  564. package/src/op/work-lease-run.ts +76 -11
  565. package/src/plan-summary.test.ts +263 -0
  566. package/src/plan-summary.ts +845 -0
  567. package/src/pr-forge.test.ts +237 -0
  568. package/src/pr-forge.ts +332 -0
  569. package/src/pr-loop-bundle.test.ts +46 -0
  570. package/src/pr-loop.test.ts +171 -0
  571. package/src/pr-loop.ts +324 -0
  572. package/src/provenance.ts +35 -0
  573. package/src/telemetry-attribution.test.ts +23 -1
  574. package/src/telemetry-attribution.ts +51 -0
  575. package/src/terraform/hcl2json.test.ts +21 -0
  576. package/src/terraform/hcl2json.ts +81 -0
  577. package/src/terraform/parse.ts +11 -74
  578. package/src/workspace/agent-cli.ts +7 -3
  579. package/src/workspace/agent.schema.json +22 -0
  580. package/src/workspace/app-kind.test.ts +196 -0
  581. package/src/workspace/box-cli.ts +113 -0
  582. package/src/workspace/box-factory.test.ts +323 -0
  583. package/src/workspace/box-factory.ts +136 -0
  584. package/src/workspace/box-listing-write.schema.json +204 -0
  585. package/src/workspace/box-listing.test.ts +234 -0
  586. package/src/workspace/box-listing.ts +324 -0
  587. package/src/workspace/box-publish.schema.json +498 -0
  588. package/src/workspace/box-publish.test.ts +204 -0
  589. package/src/workspace/box-publish.ts +420 -0
  590. package/src/workspace/box-ship.test.ts +96 -0
  591. package/src/workspace/broker-protocol.schema.json +186 -0
  592. package/src/workspace/broker-protocol.test.ts +122 -0
  593. package/src/workspace/broker-protocol.ts +317 -0
  594. package/src/workspace/builder-tiers.test.ts +146 -0
  595. package/src/workspace/change-set.schema.json +460 -0
  596. package/src/workspace/changes.schema.json +12 -20
  597. package/src/workspace/changes.ts +4 -1
  598. package/src/workspace/chant-migrations.e2e.test.ts +17 -5
  599. package/src/workspace/checks/boxes.test.ts +5 -0
  600. package/src/workspace/checks.ts +39 -8
  601. package/src/workspace/component-environments.test.ts +105 -0
  602. package/src/workspace/compose-graph.ts +7 -0
  603. package/src/workspace/composites.schema.json +23 -4
  604. package/src/workspace/composites.test.ts +8 -8
  605. package/src/workspace/composites.ts +13 -2
  606. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +2 -2
  607. package/src/workspace/conformance/__fixtures__/reference-broker.ts +207 -0
  608. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +32 -0
  609. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +135 -0
  610. package/src/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
  611. package/src/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
  612. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +228 -0
  613. package/src/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
  614. package/src/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
  615. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +54 -0
  616. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +289 -0
  617. package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
  618. package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
  619. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +603 -0
  620. package/src/workspace/conformance/broker.test.ts +122 -0
  621. package/src/workspace/conformance/broker.ts +792 -0
  622. package/src/workspace/conformance/conformance.e2e.test.ts +36 -3
  623. package/src/workspace/conformance/index.mjs +51 -3
  624. package/src/workspace/conformance/index.ts +107 -15
  625. package/src/workspace/conformance/vitest.ts +76 -2
  626. package/src/workspace/conformance/writer-conformance.e2e.test.ts +177 -0
  627. package/src/workspace/conformance/writer.test.ts +186 -0
  628. package/src/workspace/conformance/writer.ts +1191 -0
  629. package/src/workspace/decide.test.ts +208 -4
  630. package/src/workspace/decide.ts +433 -73
  631. package/src/workspace/decision-points.schema.json +11 -4
  632. package/src/workspace/declaration.schema.json +383 -5
  633. package/src/workspace/declaration.ts +315 -10
  634. package/src/workspace/environments.ts +66 -14
  635. package/src/workspace/factory-rules.test.ts +62 -0
  636. package/src/workspace/factory-rules.ts +218 -0
  637. package/src/workspace/graph-cli.ts +6 -3
  638. package/src/workspace/graph.schema.json +314 -0
  639. package/src/workspace/identity.test.ts +219 -0
  640. package/src/workspace/identity.ts +186 -0
  641. package/src/workspace/init.ts +82 -2
  642. package/src/workspace/intent-cli.ts +84 -13
  643. package/src/workspace/intent-patch-id.test.ts +162 -0
  644. package/src/workspace/intent-record.schema.json +336 -1
  645. package/src/workspace/intent-record.ts +96 -16
  646. package/src/workspace/intent-reviews.test.ts +76 -0
  647. package/src/workspace/intent-squash.test.ts +213 -0
  648. package/src/workspace/intent-why.test.ts +274 -0
  649. package/src/workspace/intent.schema.json +950 -9
  650. package/src/workspace/intent.test.ts +11 -2
  651. package/src/workspace/intent.ts +722 -65
  652. package/src/workspace/json-edit.test.ts +121 -0
  653. package/src/workspace/json-edit.ts +142 -0
  654. package/src/workspace/jsonc.ts +20 -0
  655. package/src/workspace/kinds.ts +201 -39
  656. package/src/workspace/lexicon-symbols.test.ts +148 -0
  657. package/src/workspace/lexicon-symbols.ts +125 -0
  658. package/src/workspace/lineage-upgrade.test.ts +63 -0
  659. package/src/workspace/lineage-upgrade.ts +146 -5
  660. package/src/workspace/plan-summary.schema.json +155 -0
  661. package/src/workspace/point-answer.schema.json +42 -2
  662. package/src/workspace/points-cli.ts +42 -16
  663. package/src/workspace/points-write.schema.json +163 -4
  664. package/src/workspace/points.schema.json +157 -2
  665. package/src/workspace/points.test.ts +13 -1
  666. package/src/workspace/points.ts +118 -12
  667. package/src/workspace/pr-report.schema.json +95 -0
  668. package/src/workspace/principal-classes.test.ts +175 -0
  669. package/src/workspace/principal-classes.ts +307 -0
  670. package/src/workspace/profiles.test.ts +159 -0
  671. package/src/workspace/read-contract.test.ts +2 -2
  672. package/src/workspace/reason-codes.test.ts +23 -3
  673. package/src/workspace/reason-codes.ts +61 -4
  674. package/src/workspace/record-sessions.ts +9 -8
  675. package/src/workspace/records-amend.schema.json +80 -0
  676. package/src/workspace/records-checkout.ts +106 -0
  677. package/src/workspace/records-cli.ts +123 -17
  678. package/src/workspace/records-close.schema.json +79 -0
  679. package/src/workspace/records-close.ts +31 -7
  680. package/src/workspace/records-concurrent.e2e.test.ts +135 -0
  681. package/src/workspace/records-concurrent.test.ts +145 -0
  682. package/src/workspace/records-formats.test.ts +46 -10
  683. package/src/workspace/records-new.schema.json +9 -0
  684. package/src/workspace/records-review.schema.json +80 -0
  685. package/src/workspace/records-sessions-write.test.ts +69 -0
  686. package/src/workspace/records-sessions.test.ts +22 -0
  687. package/src/workspace/records-uncommitted.test.ts +171 -0
  688. package/src/workspace/records-write.ts +219 -30
  689. package/src/workspace/records.schema.json +88 -1
  690. package/src/workspace/records.ts +76 -8
  691. package/src/workspace/reference-kinds/app/package.json +10 -0
  692. package/src/workspace/reference-kinds/app/workspace-kinds.json +45 -0
  693. package/src/workspace/run-statement.schema.json +622 -0
  694. package/src/workspace/runs-cli.ts +710 -0
  695. package/src/workspace/runs-statement.test.ts +239 -0
  696. package/src/workspace/runs-write.schema.json +994 -0
  697. package/src/workspace/runs.schema.json +1257 -0
  698. package/src/workspace/runs.test.ts +281 -0
  699. package/src/workspace/runs.ts +970 -0
  700. package/src/workspace/session-kinds.ts +3 -3
  701. package/src/workspace/squash.ts +198 -0
  702. package/src/workspace/status-contract.test.ts +10 -2
  703. package/src/workspace/status-gates.ts +25 -9
  704. package/src/workspace/status.schema.json +252 -4
  705. package/src/workspace/status.ts +110 -1
  706. package/src/workspace/symbols.ts +191 -0
  707. package/src/workspace/trailer-joins.test.ts +171 -0
  708. package/src/workspace/trailer-joins.ts +82 -0
  709. package/src/workspace/trailers.ts +148 -0
  710. package/src/workspace/trust/run-commits.ts +115 -0
  711. package/src/workspace/trust/run-statement.ts +226 -0
  712. package/src/workspace/trust/seal.ts +58 -1
  713. package/src/workspace/trust/verify-cli.ts +6 -5
  714. package/src/workspace/trust/verify.ts +39 -6
  715. package/src/workspace/wip-cli.ts +97 -0
  716. package/src/workspace/wip-write.schema.json +504 -0
  717. package/src/workspace/wip.schema.json +334 -0
  718. package/src/workspace/wip.test.ts +294 -0
  719. package/src/workspace/wip.ts +753 -0
  720. package/src/workspace/work-cli.e2e.test.ts +28 -1
  721. package/src/workspace/work-cli.ts +136 -8
  722. package/src/workspace/work-evidence.schema.json +5 -0
  723. package/src/workspace/work-history.schema.json +73 -1
  724. package/src/workspace/work-history.test.ts +48 -5
  725. package/src/workspace/work-kind-fields.test.ts +154 -0
  726. package/src/workspace/work-lease.schema.json +14 -1
  727. package/src/workspace/work-size.test.ts +77 -0
  728. package/src/workspace/work-size.ts +93 -0
  729. package/src/workspace/work.ts +82 -0
  730. package/src/workspace/workspace-kinds.schema.json +122 -0
  731. package/src/workspace/workspace-principals.schema.json +60 -0
  732. package/src/workspace/write-lock-cli.ts +131 -0
  733. package/src/workspace/write-lock.schema.json +172 -0
  734. package/src/workspace/write-lock.test.ts +189 -0
  735. package/src/workspace/write-lock.ts +474 -0
  736. package/src/workspace/write-scope.test.ts +144 -3
  737. package/src/workspace/write-scope.ts +192 -45
  738. package/src/yaml.test.ts +111 -1
  739. package/src/yaml.ts +141 -549
@@ -0,0 +1,234 @@
1
+ /**
2
+ * `chant workspace box listing set` (#3308): a box's listing written through
3
+ * chant, in place, on workspaces built in throwaway git repositories whose
4
+ * main holds the declaration the write scope is read from.
5
+ */
6
+
7
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { afterAll, describe, expect, test } from "vitest";
10
+ import { cleanScratch, contract, git, repo, scratchDir } from "./__fixtures__/contract-repo";
11
+ import { boxListingSet, BOX_LISTING_ERROR_CODES, type BoxListingWriteDocument, type BoxListingWriteRequest } from "./box-listing";
12
+ import schema from "./box-listing-write.schema.json";
13
+ import { workspaceStatus } from "./status";
14
+
15
+ afterAll(cleanScratch);
16
+
17
+ const { expectValid } = contract(schema);
18
+
19
+ /** A 1x1 PNG, and the first bytes of a JPEG. */
20
+ const PNG = Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", "base64");
21
+ const JPEG = Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0, 0x10, 0x4a, 0x46, 0x49, 0x46]);
22
+
23
+ const JSONC = `{
24
+ // the box this workspace runs
25
+ "name": "demo",
26
+ "schema": 1,
27
+ "members": [
28
+ {
29
+ "name": "app",
30
+ "dir": "app",
31
+ "kind": "other",
32
+ "because": "a plain Node server", // not a chant project
33
+ "box": {
34
+ "services": [{ "name": "web", "cmd": "node server.mjs" }],
35
+ },
36
+ },
37
+ { "name": "docs", "dir": "docs", "kind": "other", "because": "prose" },
38
+ ],
39
+ }
40
+ `;
41
+
42
+ function declaration(extra: Record<string, unknown> = {}, box: Record<string, unknown> = { services: [{ name: "web", cmd: "node server.mjs" }] }): string {
43
+ return `${JSON.stringify(
44
+ {
45
+ name: "demo",
46
+ schema: 1,
47
+ members: [
48
+ { name: "app", dir: "app", kind: "other", because: "a plain Node server", box },
49
+ { name: "docs", dir: "docs", kind: "other", because: "prose" },
50
+ ],
51
+ ...extra,
52
+ },
53
+ null,
54
+ 2,
55
+ )}\n`;
56
+ }
57
+
58
+ /** A workspace committed on main, so main is the base the write scope is read from. */
59
+ function workspace(files: Record<string, string>): string {
60
+ const root = repo({ "app/server.mjs": "export {};\n", "docs/README.md": "# Docs\n", ...files }, true);
61
+ git(root, "branch", "-M", "main");
62
+ return root;
63
+ }
64
+
65
+ function inputs(): string {
66
+ const dir = scratchDir("chant-listing-inputs-");
67
+ writeFileSync(join(dir, "cover.png"), PNG);
68
+ writeFileSync(join(dir, "cover.jpg"), JPEG);
69
+ writeFileSync(join(dir, "notes.txt"), "not a picture");
70
+ return dir;
71
+ }
72
+
73
+ function set(req: Omit<BoxListingWriteRequest, "member"> & { member?: string }, fields?: unknown): BoxListingWriteDocument {
74
+ const doc = boxListingSet({ member: "app", ...req, ...(fields !== undefined ? { fields: JSON.stringify(fields) } : {}) });
75
+ expectValid(doc);
76
+ return doc;
77
+ }
78
+
79
+ const ok = (doc: BoxListingWriteDocument) => {
80
+ if ("error" in doc) throw new Error(`${doc.error.code}: ${doc.error.message}`);
81
+ return doc;
82
+ };
83
+ const code = (doc: BoxListingWriteDocument) => ("error" in doc ? doc.error.code : null);
84
+
85
+ describe("box listing set writes the listing in place (#3308)", () => {
86
+ test("adds a listing to a box that has none, copies the cover in, and keeps the file's comments and layout", async () => {
87
+ const root = workspace({ "chant.workspace.jsonc": JSONC });
88
+ const dir = inputs();
89
+ const doc = ok(set({ cwd: root, cover: join(dir, "cover.png") }, { title: "Demo", line: "A box that demos" }));
90
+ expect(doc.paths).toEqual(["app/listing/cover.png", "chant.workspace.jsonc"]);
91
+ expect(doc.changed).toBe(true);
92
+ expect(doc.previous).toBeNull();
93
+ const sha = doc.cover!.sha256;
94
+ expect(doc.listing).toEqual({ published: true, title: "Demo", line: "A box that demos", cover: { path: "app/listing/cover.png", sha256: sha } });
95
+ expect(doc.cover).toEqual({ path: "app/listing/cover.png", sha256: sha, bytes: PNG.length, type: "image/png", replaced: null });
96
+ expect(readFileSync(join(root, "app/listing/cover.png"))).toEqual(PNG);
97
+ const text = readFileSync(join(root, "chant.workspace.jsonc"), "utf-8");
98
+ expect(text).toBe(
99
+ JSONC.replace(
100
+ ` "services": [{ "name": "web", "cmd": "node server.mjs" }],\n`,
101
+ ` "services": [{ "name": "web", "cmd": "node server.mjs" }],\n "listing": {\n "title": "Demo",\n "line": "A box that demos",\n "cover": "app/listing/cover.png"\n },\n`,
102
+ ),
103
+ );
104
+ // What the read contract prints is what the write printed.
105
+ const status = await workspaceStatus({ env: "dev", cwd: root });
106
+ if ("error" in status) throw new Error(status.error.message);
107
+ expect(status.members.find((m) => m.name === "app")!.box!.listing).toEqual(doc.listing);
108
+ });
109
+
110
+ test("changes only the fields given; null takes one out, and the same values again change nothing", () => {
111
+ const root = workspace({ "chant.workspace.json": declaration({}, { listing: { title: "Old", line: "old line", "x-studio": { slot: 1 } } }) });
112
+ const before = readFileSync(join(root, "chant.workspace.json"), "utf-8");
113
+ const doc = ok(set({ cwd: root }, { title: "New", line: null, published: false }));
114
+ expect(doc.previous).toEqual({ published: true, title: "Old", line: "old line", cover: null });
115
+ expect(doc.listing).toEqual({ published: false, title: "New", line: "", cover: null });
116
+ const after = readFileSync(join(root, "chant.workspace.json"), "utf-8");
117
+ expect(JSON.parse(after).members[0].box.listing).toEqual({ title: "New", "x-studio": { slot: 1 }, published: false });
118
+ expect(after).toBe(
119
+ before.replace(
120
+ `"title": "Old",\n "line": "old line",\n "x-studio": {\n "slot": 1\n }\n`,
121
+ `"title": "New",\n "x-studio": {\n "slot": 1\n },\n "published": false\n`,
122
+ ),
123
+ );
124
+ const again = ok(set({ cwd: root }, { title: "New", published: false }));
125
+ expect(again.changed).toBe(false);
126
+ expect(again.paths).toEqual([]);
127
+ expect(readFileSync(join(root, "chant.workspace.json"), "utf-8")).toBe(after);
128
+ });
129
+
130
+ test("a new cover with another format goes to its own path and names the one it replaced; --cover-path chooses the path", () => {
131
+ const root = workspace({ "chant.workspace.json": declaration() });
132
+ const dir = inputs();
133
+ ok(set({ cwd: root, cover: join(dir, "cover.png") }));
134
+ const jpeg = ok(set({ cwd: root, cover: join(dir, "cover.jpg") }));
135
+ expect(jpeg.cover).toMatchObject({ path: "app/listing/cover.jpg", type: "image/jpeg", replaced: "app/listing/cover.png" });
136
+ expect(existsSync(join(root, "app/listing/cover.png"))).toBe(true);
137
+ const chosen = ok(set({ cwd: root, cover: join(dir, "cover.png"), coverPath: "app/public/box.png" }));
138
+ expect(chosen.listing.cover!.path).toBe("app/public/box.png");
139
+ expect(chosen.paths).toEqual(["app/public/box.png", "chant.workspace.json"]);
140
+ });
141
+
142
+ test("a cover field names a picture already in the workspace, and null takes the cover out, leaving the file", () => {
143
+ const root = workspace({ "chant.workspace.json": declaration() });
144
+ writeFileSync(join(root, "docs/shot.png"), PNG);
145
+ expect(ok(set({ cwd: root }, { cover: "docs/shot.png" })).listing.cover!.path).toBe("docs/shot.png");
146
+ expect(ok(set({ cwd: root }, { cover: null })).listing.cover).toBeNull();
147
+ expect(existsSync(join(root, "docs/shot.png"))).toBe(true);
148
+ });
149
+
150
+ test("--dry-run prints the document and writes nothing", () => {
151
+ const root = workspace({ "chant.workspace.json": declaration() });
152
+ const before = readFileSync(join(root, "chant.workspace.json"), "utf-8");
153
+ const doc = ok(set({ cwd: root, dryRun: true, cover: join(inputs(), "cover.png") }, { title: "Dry" }));
154
+ expect(doc.dryRun).toBe(true);
155
+ expect(doc.paths).toEqual(["app/listing/cover.png", "chant.workspace.json"]);
156
+ expect(doc.listing.title).toBe("Dry");
157
+ expect(readFileSync(join(root, "chant.workspace.json"), "utf-8")).toBe(before);
158
+ expect(existsSync(join(root, "app/listing"))).toBe(false);
159
+ });
160
+
161
+ test("refuses, writing nothing, what it can't write", () => {
162
+ const root = workspace({ "chant.workspace.json": declaration() });
163
+ const dir = inputs();
164
+ const before = readFileSync(join(root, "chant.workspace.json"), "utf-8");
165
+ expect(code(set({ cwd: root, member: "nope" }, { title: "x" }))).toBe("listing-member-unknown");
166
+ expect(code(set({ cwd: root, member: "docs" }, { title: "x" }))).toBe("listing-box-missing");
167
+ expect(code(set({ cwd: root }))).toBe("write-usage-invalid");
168
+ expect(code(set({ cwd: root, coverPath: "a.png" }, {}))).toBe("write-usage-invalid");
169
+ expect(code(set({ cwd: root, cover: join(dir, "cover.png") }, { cover: "x.png" }))).toBe("write-usage-invalid");
170
+ expect(code(set({ cwd: root }, { subtitle: "x" }))).toBe("write-input-invalid");
171
+ expect(code(set({ cwd: root }, { published: "yes" }))).toBe("write-input-invalid");
172
+ expect(code(set({ cwd: root }, ["title"]))).toBe("write-input-invalid");
173
+ expect(code(boxListingSet({ cwd: root, member: "app", fields: "{" }))).toBe("write-input-invalid");
174
+ const long = set({ cwd: root }, { title: "t".repeat(61) });
175
+ expect(code(long)).toBe("write-input-invalid");
176
+ expect("error" in long && long.error.message).toMatch(/would make the declaration invalid: .*60 characters/);
177
+ expect(code(set({ cwd: root, cover: join(dir, "notes.txt") }))).toBe("listing-cover-invalid");
178
+ expect(code(set({ cwd: root, cover: join(dir, "missing.png") }))).toBe("listing-cover-invalid");
179
+ expect(code(set({ cwd: root, cover: join(dir, "cover.png"), coverPath: "../out.png" }))).toBe("listing-cover-invalid");
180
+ expect(code(set({ cwd: root, cover: join(dir, "cover.png"), coverPath: "app/cover.jpg" }))).toBe("listing-cover-invalid");
181
+ expect(code(set({ cwd: root }, { cover: "docs/README.md" }))).toBe("listing-cover-invalid");
182
+ expect(code(set({ cwd: root }, { cover: "docs/none.png" }))).toBe("listing-cover-invalid");
183
+ expect(readFileSync(join(root, "chant.workspace.json"), "utf-8")).toBe(before);
184
+ expect(existsSync(join(root, "app/listing"))).toBe(false);
185
+ expect(code(boxListingSet({ cwd: scratchDir(), member: "app", fields: "{}" }))).toBe("declaration-missing");
186
+ });
187
+
188
+ test("every error code it can print is in its schema", () => {
189
+ const enumCodes = (schema.$defs.failure.properties.error.properties.code as { enum: string[] }).enum;
190
+ expect([...enumCodes].sort()).toEqual([...BOX_LISTING_ERROR_CODES].sort());
191
+ });
192
+ });
193
+
194
+ describe("box listing set honours the write scope and identity rule at base (#3308, ws-067, ws-080)", () => {
195
+ const PROTECTED = (except?: string[]) => ({ writeScope: { human: { protected: [except ? { path: "chant.workspace.json", except } : "chant.workspace.json"] } } });
196
+
197
+ test("a protected declaration refuses the write, unless except names the listing by JSON Pointer", () => {
198
+ const root = workspace({ "chant.workspace.json": declaration(PROTECTED()) });
199
+ expect(code(set({ cwd: root, by: "alice@example.com" }, { title: "x" }))).toBe("write-scope-protected");
200
+
201
+ const members = workspace({ "chant.workspace.json": declaration(PROTECTED(["/members/*/box/intent"])) });
202
+ const refused = set({ cwd: members, by: "alice@example.com" }, { title: "x" });
203
+ expect(code(refused)).toBe("write-scope-protected");
204
+ expect("error" in refused && refused.error.message).toMatch(/outside what its except allows \(\/members\/\*\/box\/intent\)/);
205
+
206
+ const allowed = workspace({ "chant.workspace.json": declaration(PROTECTED(["/members/*/box/listing"])) });
207
+ expect(ok(set({ cwd: allowed, by: "alice@example.com", cover: join(inputs(), "cover.png") }, { title: "x" })).listing.title).toBe("x");
208
+ });
209
+
210
+ test("the scope is read at base: a working-tree edit can't widen it", () => {
211
+ const root = workspace({ "chant.workspace.json": declaration(PROTECTED()) });
212
+ writeFileSync(join(root, "chant.workspace.json"), declaration(PROTECTED(["/members/*/box/listing"])));
213
+ expect(code(set({ cwd: root }, { title: "x" }))).toBe("write-scope-protected");
214
+ });
215
+
216
+ test("a protected cover path is refused like any other file", () => {
217
+ const root = workspace({ "chant.workspace.json": declaration({ writeScope: { human: { protected: ["app/listing/**"] } } }) });
218
+ expect(code(set({ cwd: root, cover: join(inputs(), "cover.png") }))).toBe("write-scope-protected");
219
+ expect(ok(set({ cwd: root, cover: join(inputs(), "cover.png"), coverPath: "app/public/cover.png" })).paths).toContain("app/public/cover.png");
220
+ });
221
+
222
+ test("an agent session writes only its own member, and the declaration is in none", () => {
223
+ const root = workspace({ "chant.workspace.json": declaration({ agents: [{ name: "app-agent", member: "app" }] }) });
224
+ const doc = set({ cwd: root, agent: "app-agent" }, { title: "x" });
225
+ expect(code(doc)).toBe("write-scope-member");
226
+ expect(code(set({ cwd: root, agent: "ghost" }, { title: "x" }))).toBe("agent-unknown");
227
+ });
228
+
229
+ test("under identity.attribution identified, --by names a forge identity or a signer", () => {
230
+ const root = workspace({ "chant.workspace.json": declaration({ identity: { attribution: "identified" } }) });
231
+ expect(code(set({ cwd: root, by: "alice" }, { title: "x" }))).toBe("principal-unidentified");
232
+ expect(ok(set({ cwd: root, by: "github:alice" }, { title: "x" })).listing.title).toBe("x");
233
+ });
234
+ });
@@ -0,0 +1,324 @@
1
+ /**
2
+ * `chant workspace box listing set <member>` (#3308): change a box's listing,
3
+ * `members[i].box.listing` in the declaration (#3146, ws-077), through chant.
4
+ *
5
+ * ws-074 makes the repo the database and lets a tool write only through
6
+ * chant's write contract. The listing is configuration on the box block, so
7
+ * before this a tool such as hud would have had to edit `chant.workspace.json`
8
+ * itself. This write changes only the listing's own properties, in place, so
9
+ * the file keeps its formatting, key order and, in a `.jsonc` file, its
10
+ * comments. It can copy a cover image into the repository and point the
11
+ * listing at it. It validates the declaration it would write, judges every
12
+ * path it writes by the write scope at base (`judgePath`, the rule `check
13
+ * --changes` applies to the commit later), refuses a bare `--by` name under
14
+ * `identity.attribution: "identified"` (ws-080), prints one JSON document
15
+ * (`box-listing-write.schema.json`) and never commits: the caller commits, as
16
+ * after the records writes. Like them it holds the working tree's write lock
17
+ * (#3173, ws-089) from reading the declaration to writing it.
18
+ */
19
+
20
+ import { createHash } from "node:crypto";
21
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
22
+ import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
23
+ import { listingView, treeBytes, type ListingView } from "./box-factory";
24
+ import { parseDeclaration, readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type Declaration, type Member } from "./declaration";
25
+ import { IDENTITY_CODES, IdentityError, refuseUnidentified } from "./identity";
26
+ import { removeProperty, setProperty, valueAt } from "./json-edit";
27
+ import { parseJsonText } from "./jsonc";
28
+ import type { ReasonCode } from "./reason-codes";
29
+ import { workingTree } from "./tree";
30
+ import { locateWorkspace } from "./which-chant";
31
+ import { judgePath, resolveWriter, scopeSource, unknownClassVerdict, WriteScopeError } from "./write-scope";
32
+ import { withWriteLockSync, WriteLockError, WRITE_LOCK_CODES } from "./write-lock";
33
+
34
+ /** The version of the document this write prints. */
35
+ export const BOX_LISTING_CONTRACT_VERSION = 1;
36
+
37
+ /** `$id` of the JSON Schema for what `box listing set` prints, shipped beside this file. */
38
+ export const BOX_LISTING_WRITE_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/box-listing-write/v1/box-listing-write.schema.json";
39
+
40
+ /** Why a listing write wrote nothing. Closed. */
41
+ export const BOX_LISTING_ERROR_CODES = [
42
+ ...WORKSPACE_ERROR_CODES,
43
+ "write-usage-invalid",
44
+ "write-input-invalid",
45
+ "listing-member-unknown",
46
+ "listing-box-missing",
47
+ "listing-cover-invalid",
48
+ "write-scope-member",
49
+ "write-scope-protected",
50
+ "write-scope-class-unknown",
51
+ "agent-unknown",
52
+ ...IDENTITY_CODES,
53
+ ...WRITE_LOCK_CODES,
54
+ ] as const satisfies readonly ReasonCode[];
55
+ export type BoxListingErrorCode = (typeof BOX_LISTING_ERROR_CODES)[number];
56
+
57
+ /** The listing properties a write may set. Any `x-` key is passed through as well. */
58
+ export const LISTING_FIELDS = ["published", "title", "line", "cover"] as const;
59
+
60
+ /** The largest cover image the write copies, in bytes. */
61
+ export const COVER_MAX_BYTES = 5 * 1024 * 1024;
62
+
63
+ /** The picture formats a cover may be, by the extension its file takes. */
64
+ const COVER_TYPES = [
65
+ { type: "image/png", ext: "png", exts: ["png"], magic: (b: Uint8Array) => b.length >= 8 && [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a].every((x, i) => b[i] === x) },
66
+ { type: "image/jpeg", ext: "jpg", exts: ["jpg", "jpeg"], magic: (b: Uint8Array) => b.length >= 3 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff },
67
+ {
68
+ type: "image/webp",
69
+ ext: "webp",
70
+ exts: ["webp"],
71
+ magic: (b: Uint8Array) => b.length >= 12 && String.fromCharCode(...b.slice(0, 4)) === "RIFF" && String.fromCharCode(...b.slice(8, 12)) === "WEBP",
72
+ },
73
+ ] as const;
74
+ type CoverType = (typeof COVER_TYPES)[number];
75
+
76
+ /** What `box listing set` prints. */
77
+ export type BoxListingWriteDocument =
78
+ | {
79
+ $schema: string;
80
+ contract: number;
81
+ chant: string;
82
+ member: string;
83
+ /** The declaration file, from the repository root, and the sha256 of its bytes after the write (or as it would be, with dryRun). */
84
+ declaration: { path: string; sha256: string };
85
+ /** Every file the write changed, from the repository root: the declaration and the cover it copied. Empty when nothing changed. */
86
+ paths: string[];
87
+ changed: boolean;
88
+ dryRun: boolean;
89
+ /** The listing before the write, as status --json prints it, or null when the box declared none. */
90
+ previous: ListingView | null;
91
+ /** The listing after the write, as status --json prints it. */
92
+ listing: ListingView;
93
+ /** The cover copied with --cover, or null. */
94
+ cover: { path: string; sha256: string; bytes: number; type: string; replaced: string | null } | null;
95
+ }
96
+ | { $schema: string; contract: number; chant: string; member: string | null; error: { code: BoxListingErrorCode; message: string } };
97
+
98
+ export interface BoxListingWriteRequest {
99
+ cwd: string;
100
+ member: string;
101
+ /** The listing fields as JSON text (`--from`), or undefined. */
102
+ fields?: string;
103
+ /** An image to copy in as the cover (`--cover`), from `cwd`. */
104
+ cover?: string;
105
+ /** Where to copy it, from the workspace root (`--cover-path`). */
106
+ coverPath?: string;
107
+ /** Who is writing (`--by`). */
108
+ by?: string;
109
+ /** The agent session (`CHANT_AGENT`). */
110
+ agent?: string;
111
+ dryRun?: boolean;
112
+ }
113
+
114
+ class ListingWriteError extends Error {
115
+ constructor(
116
+ readonly code: BoxListingErrorCode,
117
+ message: string,
118
+ ) {
119
+ super(message);
120
+ }
121
+ }
122
+
123
+ const toPosix = (p: string) => (sep === "/" ? p : p.split(sep).join("/"));
124
+ const sha256 = (b: Uint8Array | string) => createHash("sha256").update(b).digest("hex");
125
+ const isObject = (v: unknown): v is Record<string, unknown> => v !== null && typeof v === "object" && !Array.isArray(v);
126
+
127
+ /** A path from the workspace root that stays inside it, with / separators, or a reason it isn't one. */
128
+ function workspacePath(raw: string, what: string): string {
129
+ const p = toPosix(raw).replace(/^\.\//, "");
130
+ const norm = posix.normalize(p);
131
+ if (p === "" || isAbsolute(raw) || norm.startsWith("../") || norm === ".." || norm !== p || p.endsWith("/")) {
132
+ throw new ListingWriteError("listing-cover-invalid", `${what} ${JSON.stringify(raw)} is not a file path from the workspace root: give it relative, normalised and inside the workspace`);
133
+ }
134
+ if (p.split("/")[0] === ".git") throw new ListingWriteError("listing-cover-invalid", `${what} ${JSON.stringify(raw)} is inside .git`);
135
+ return p;
136
+ }
137
+
138
+ function sniff(bytes: Uint8Array, what: string): CoverType {
139
+ const t = COVER_TYPES.find((c) => c.magic(bytes));
140
+ if (!t) throw new ListingWriteError("listing-cover-invalid", `${what} is not a PNG, JPEG or WebP picture`);
141
+ return t;
142
+ }
143
+
144
+ /** The listing fields given with --from: a JSON object of published, title, line, cover and x- keys; null for a key takes it out. */
145
+ function readFields(text: string | undefined): Record<string, unknown> {
146
+ if (text === undefined || text.trim() === "") return {};
147
+ let value: unknown;
148
+ try {
149
+ value = JSON.parse(text);
150
+ } catch (err) {
151
+ throw new ListingWriteError("write-input-invalid", `the listing fields are not JSON: ${err instanceof Error ? err.message : String(err)}`);
152
+ }
153
+ if (!isObject(value)) throw new ListingWriteError("write-input-invalid", "the listing fields are a JSON object, such as {\"title\": \"...\", \"line\": \"...\"}");
154
+ for (const [k, v] of Object.entries(value)) {
155
+ if (k.startsWith("x-")) continue;
156
+ if (!(LISTING_FIELDS as readonly string[]).includes(k)) throw new ListingWriteError("write-input-invalid", `${k} is not a listing field; a listing takes ${LISTING_FIELDS.join(", ")} and x- keys`);
157
+ if (v === null) continue;
158
+ if (k === "published" && typeof v !== "boolean") throw new ListingWriteError("write-input-invalid", "published is true or false, or null to take it out");
159
+ if (k !== "published" && typeof v !== "string") throw new ListingWriteError("write-input-invalid", `${k} is a string, or null to take it out`);
160
+ }
161
+ return value;
162
+ }
163
+
164
+ /** Write `bytes` to `file` through a sibling temporary file, so a reader never sees half of it. */
165
+ function writeAtomically(file: string, bytes: Uint8Array | string): void {
166
+ mkdirSync(dirname(file), { recursive: true });
167
+ const tmp = `${file}.chant-${process.pid}.tmp`;
168
+ writeFileSync(tmp, bytes);
169
+ renameSync(tmp, file);
170
+ }
171
+
172
+ /** Run the write and build the document it prints. Never throws for a refusal. */
173
+ export function boxListingSet(req: BoxListingWriteRequest): BoxListingWriteDocument {
174
+ const head = { $schema: BOX_LISTING_WRITE_SCHEMA_ID, contract: BOX_LISTING_CONTRACT_VERSION, chant: readerVersion() };
175
+ try {
176
+ return withWriteLockSync(req.cwd, { verb: "box listing set", by: req.by ?? null, agent: req.agent || null }, req.dryRun, () => write(req, head));
177
+ } catch (err) {
178
+ const fail = (code: BoxListingErrorCode, message: string): BoxListingWriteDocument => ({ ...head, member: req.member || null, error: { code, message } });
179
+ if (err instanceof ListingWriteError) return fail(err.code, err.message);
180
+ // A path write is never judged write-scope-kind, which is for record kinds.
181
+ if (err instanceof WriteScopeError) return fail(err.code as BoxListingErrorCode, err.message);
182
+ if (err instanceof IdentityError) return fail(err.code, err.message);
183
+ if (err instanceof WriteLockError) return fail(err.code, err.message);
184
+ if (err instanceof WorkspaceReadError) return fail(err.code as BoxListingErrorCode, err.describe());
185
+ throw err;
186
+ }
187
+ }
188
+
189
+ function write(req: BoxListingWriteRequest, head: { $schema: string; contract: number; chant: string }): BoxListingWriteDocument {
190
+ if (!req.member) throw new ListingWriteError("write-usage-invalid", "box listing set needs the member whose box it lists: box listing set <member>");
191
+ if (req.coverPath !== undefined && req.cover === undefined) throw new ListingWriteError("write-usage-invalid", "--cover-path says where --cover's image goes, and no --cover was given");
192
+ const fields = readFields(req.fields);
193
+ if (req.cover !== undefined && "cover" in fields) throw new ListingWriteError("write-usage-invalid", "give the cover once: --cover copies an image in, and a cover field names a file already in the workspace");
194
+ if (req.cover === undefined && req.fields === undefined) throw new ListingWriteError("write-usage-invalid", "box listing set needs --from <file|-> with the listing fields, --cover <image>, or both");
195
+
196
+ const located = locateWorkspace(req.cwd);
197
+ const declaration = readDeclaration(located.tree);
198
+ const member = declaration.members.find((m) => m.name === req.member);
199
+ if (!member) {
200
+ throw new ListingWriteError("listing-member-unknown", `no member is named ${req.member}; the declaration's members are ${declaration.members.map((m) => m.name).join(", ") || "none"}`);
201
+ }
202
+ if (!member.box) throw new ListingWriteError("listing-box-missing", `member ${member.name} declares no box block, so it has no listing; a listing is what a box shows of itself (#3146)`);
203
+
204
+ const repoBase = located.top ?? located.rootOnDisk;
205
+ const fromRepo = (pathInWorkspace: string) => toPosix(relative(repoBase, join(located.rootOnDisk, ...pathInWorkspace.split("/"))));
206
+ const declFile = join(located.rootOnDisk, declaration.file);
207
+ const before = readFileSync(declFile, "utf-8");
208
+ const jsonc = declaration.file.endsWith(".jsonc");
209
+
210
+ // The cover: an image copied in, or a file already in the workspace.
211
+ let copy: { path: string; bytes: Uint8Array; type: CoverType; replaced: string | null } | null = null;
212
+ if (req.cover !== undefined) {
213
+ const src = resolve(req.cwd, req.cover);
214
+ let bytes: Uint8Array;
215
+ try {
216
+ if (statSync(src).size > COVER_MAX_BYTES) throw new ListingWriteError("listing-cover-invalid", `--cover ${req.cover} is larger than ${COVER_MAX_BYTES} bytes; a cover is a picture for a listing, so make it smaller`);
217
+ bytes = readFileSync(src);
218
+ } catch (err) {
219
+ if (err instanceof ListingWriteError) throw err;
220
+ throw new ListingWriteError("listing-cover-invalid", `--cover ${req.cover} can't be read: ${err instanceof Error ? err.message : String(err)}`);
221
+ }
222
+ const type = sniff(bytes, `--cover ${req.cover}`);
223
+ const current = member.box.listing?.cover ?? null;
224
+ const dir = member.dir === "." ? "" : `${member.dir}/`;
225
+ const path =
226
+ req.coverPath !== undefined
227
+ ? workspacePath(req.coverPath, "--cover-path")
228
+ : current !== null && (type.exts as readonly string[]).includes(posix.extname(current).slice(1).toLowerCase())
229
+ ? current
230
+ : `${dir}listing/cover.${type.ext}`;
231
+ const ext = posix.extname(path).slice(1).toLowerCase();
232
+ if (!(type.exts as readonly string[]).includes(ext)) throw new ListingWriteError("listing-cover-invalid", `--cover ${req.cover} is ${type.type}, and ${path} has the extension .${ext}; name it .${type.ext}`);
233
+ if (path === declaration.file) throw new ListingWriteError("listing-cover-invalid", `--cover-path names the declaration itself`);
234
+ copy = { path, bytes, type, replaced: current !== null && current !== path ? current : null };
235
+ fields.cover = path;
236
+ } else if (typeof fields.cover === "string") {
237
+ const path = workspacePath(fields.cover, "cover");
238
+ const bytes = treeBytes(workingTree(located.rootOnDisk))(path);
239
+ if (bytes === null) throw new ListingWriteError("listing-cover-invalid", `cover ${path} is not a file in the workspace; copy an image in with --cover <image> instead`);
240
+ sniff(bytes, `cover ${path}`);
241
+ fields.cover = path;
242
+ }
243
+
244
+ // The declaration, with only the listing's own properties changed.
245
+ const boxPointer = member.box.pointer;
246
+ const listingPointer = `${boxPointer}/listing`;
247
+ const parsedBefore = parseJsonText(before, { jsonc });
248
+ if (!parsedBefore.ok) throw new WorkspaceReadError("declaration-unparseable", parsedBefore.message);
249
+ const rawListing = valueAt(parsedBefore.value, listingPointer);
250
+ let after = before;
251
+ const expected = structuredClone(parsedBefore.value) as Record<string, unknown>;
252
+ const expectedBox = valueAt(expected, boxPointer) as Record<string, unknown>;
253
+ if (rawListing === undefined) {
254
+ const value = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== null));
255
+ after = setProperty(after, jsonc, boxPointer, "listing", value);
256
+ expectedBox.listing = value;
257
+ } else {
258
+ const expectedListing = expectedBox.listing as Record<string, unknown>;
259
+ for (const [k, v] of Object.entries(fields)) {
260
+ if (v === null) {
261
+ after = removeProperty(after, jsonc, listingPointer, k);
262
+ delete expectedListing[k];
263
+ } else {
264
+ after = setProperty(after, jsonc, listingPointer, k, v);
265
+ expectedListing[k] = v;
266
+ }
267
+ }
268
+ }
269
+ const parsedAfter = parseJsonText(after, { jsonc });
270
+ if (!parsedAfter.ok || JSON.stringify(parsedAfter.value) !== JSON.stringify(expected)) {
271
+ // An edit that does not read back as the value meant is never written.
272
+ throw new Error(`box listing set: editing ${declaration.file} in place did not give the declaration intended; nothing was written`);
273
+ }
274
+ let next: Declaration;
275
+ try {
276
+ next = parseDeclaration(after, declaration.file);
277
+ } catch (err) {
278
+ if (err instanceof WorkspaceReadError) throw new ListingWriteError("write-input-invalid", `the listing would make the declaration invalid: ${err.describe()}`);
279
+ throw err;
280
+ }
281
+ const nextMember = next.members.find((m) => m.name === member.name) as Member;
282
+
283
+ // The write scope at base, for each file written, and who --by names.
284
+ const source = scopeSource(req.cwd);
285
+ if (req.by !== undefined) refuseUnidentified(source, [req.by], "--by", { agent: req.agent });
286
+ if (source.declaration !== null || (req.agent !== undefined && req.agent !== "")) {
287
+ const writer = resolveWriter(source.declaration, source.policy, { agent: req.agent || null, principal: req.by ?? null }, source.classes);
288
+ const unknown = unknownClassVerdict(source.declaration, writer, source.classes);
289
+ if (!unknown.ok) throw new WriteScopeError(unknown.code, unknown.message);
290
+ const judge = (path: string, change?: { before(): string | null; after(): string | null }) => {
291
+ const verdict = judgePath(source.declaration, writer, path, change);
292
+ if (!verdict.ok) throw new ListingWriteError(verdict.code as BoxListingErrorCode, verdict.message);
293
+ };
294
+ if (after !== before) judge(declaration.file, { before: () => before, after: () => after });
295
+ if (copy) judge(copy.path);
296
+ }
297
+
298
+ // Write: the cover first, so the declaration never names a cover that isn't there.
299
+ const paths: string[] = [];
300
+ const coverFile = copy ? join(located.rootOnDisk, ...copy.path.split("/")) : null;
301
+ const coverChanged = copy !== null && !(existsSync(coverFile!) && sha256(readFileSync(coverFile!)) === sha256(copy.bytes));
302
+ if (copy && coverChanged) {
303
+ if (!req.dryRun) writeAtomically(coverFile!, copy.bytes);
304
+ paths.push(fromRepo(copy.path));
305
+ }
306
+ if (after !== before) {
307
+ if (!req.dryRun) writeAtomically(declFile, after);
308
+ paths.push(fromRepo(declaration.file));
309
+ }
310
+
311
+ const read = treeBytes(workingTree(located.rootOnDisk));
312
+ const readAfter = (p: string) => (copy && p === copy.path ? copy.bytes : read(p));
313
+ return {
314
+ ...head,
315
+ member: member.name,
316
+ declaration: { path: fromRepo(declaration.file), sha256: sha256(after) },
317
+ paths,
318
+ changed: paths.length > 0,
319
+ dryRun: req.dryRun === true,
320
+ previous: listingView(member.box.listing, read),
321
+ listing: listingView(nextMember.box!.listing, readAfter)!,
322
+ cover: copy ? { path: copy.path, sha256: sha256(copy.bytes), bytes: copy.bytes.length, type: copy.type.type, replaced: copy.replaced } : null,
323
+ };
324
+ }