@intentius/chant 0.102.0 → 0.104.1

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 (471) hide show
  1. package/dist/audit/core.d.ts +2 -0
  2. package/dist/audit/core.d.ts.map +1 -1
  3. package/dist/audit/discover.d.ts.map +1 -1
  4. package/dist/change-set.d.ts +232 -0
  5. package/dist/change-set.d.ts.map +1 -0
  6. package/dist/cli/commands/audit.d.ts +7 -0
  7. package/dist/cli/commands/audit.d.ts.map +1 -1
  8. package/dist/cli/commands/build.d.ts.map +1 -1
  9. package/dist/cli/handlers/build.d.ts.map +1 -1
  10. package/dist/cli/handlers/change-set.d.ts +11 -0
  11. package/dist/cli/handlers/change-set.d.ts.map +1 -0
  12. package/dist/cli/handlers/fan-out.d.ts +33 -0
  13. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  14. package/dist/cli/handlers/operator.d.ts +7 -0
  15. package/dist/cli/handlers/operator.d.ts.map +1 -1
  16. package/dist/cli/handlers/pr.d.ts +31 -0
  17. package/dist/cli/handlers/pr.d.ts.map +1 -0
  18. package/dist/cli/handlers/run.d.ts.map +1 -1
  19. package/dist/cli/main.d.ts.map +1 -1
  20. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  21. package/dist/cli/mcp/workspace-tools.d.ts +3 -2
  22. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  23. package/dist/cli/registry.d.ts +63 -1
  24. package/dist/cli/registry.d.ts.map +1 -1
  25. package/dist/components/auto-release.d.ts +16 -0
  26. package/dist/components/auto-release.d.ts.map +1 -1
  27. package/dist/components/capability.d.ts +53 -0
  28. package/dist/components/capability.d.ts.map +1 -1
  29. package/dist/components/changed-files.d.ts +31 -0
  30. package/dist/components/changed-files.d.ts.map +1 -0
  31. package/dist/components/cli-support.d.ts +9 -1
  32. package/dist/components/cli-support.d.ts.map +1 -1
  33. package/dist/components/component.d.ts +24 -0
  34. package/dist/components/component.d.ts.map +1 -1
  35. package/dist/components/deploy-units.d.ts.map +1 -1
  36. package/dist/components/driver.d.ts +13 -0
  37. package/dist/components/driver.d.ts.map +1 -1
  38. package/dist/components/fan-out-output.d.ts +6 -0
  39. package/dist/components/fan-out-output.d.ts.map +1 -1
  40. package/dist/components/fan-out-record.d.ts +41 -0
  41. package/dist/components/fan-out-record.d.ts.map +1 -0
  42. package/dist/components/fan-out-run.d.ts +65 -0
  43. package/dist/components/fan-out-run.d.ts.map +1 -1
  44. package/dist/components/fan-out-support.d.ts +2 -0
  45. package/dist/components/fan-out-support.d.ts.map +1 -1
  46. package/dist/components/fan-out.d.ts +6 -0
  47. package/dist/components/fan-out.d.ts.map +1 -1
  48. package/dist/components/gated-wave-pipeline.d.ts +50 -0
  49. package/dist/components/gated-wave-pipeline.d.ts.map +1 -0
  50. package/dist/components/index.d.ts +1 -1
  51. package/dist/components/index.d.ts.map +1 -1
  52. package/dist/components/layers.d.ts +25 -0
  53. package/dist/components/layers.d.ts.map +1 -0
  54. package/dist/components/pr-pipeline.d.ts +61 -0
  55. package/dist/components/pr-pipeline.d.ts.map +1 -0
  56. package/dist/components/pr-run.d.ts +119 -0
  57. package/dist/components/pr-run.d.ts.map +1 -0
  58. package/dist/components/promote.d.ts.map +1 -1
  59. package/dist/components/verbs/shell.d.ts.map +1 -1
  60. package/dist/components/wave-plan.d.ts +49 -0
  61. package/dist/components/wave-plan.d.ts.map +1 -0
  62. package/dist/gated-waves.d.ts +147 -0
  63. package/dist/gated-waves.d.ts.map +1 -0
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/lexicon.d.ts +48 -0
  67. package/dist/lexicon.d.ts.map +1 -1
  68. package/dist/lifecycle/gate-ledger.d.ts +7 -0
  69. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  70. package/dist/lifecycle/git.d.ts +46 -1
  71. package/dist/lifecycle/git.d.ts.map +1 -1
  72. package/dist/lint/post-synth.d.ts +18 -0
  73. package/dist/lint/post-synth.d.ts.map +1 -1
  74. package/dist/op/activities/activity-contracts.d.ts +126 -0
  75. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  76. package/dist/op/activities/change-set.d.ts +62 -0
  77. package/dist/op/activities/change-set.d.ts.map +1 -0
  78. package/dist/op/activities/decide.d.ts +2 -0
  79. package/dist/op/activities/decide.d.ts.map +1 -1
  80. package/dist/op/activities/factory.d.ts +137 -0
  81. package/dist/op/activities/factory.d.ts.map +1 -0
  82. package/dist/op/activities/index.d.ts +4 -0
  83. package/dist/op/activities/index.d.ts.map +1 -1
  84. package/dist/op/activity-contract.d.ts.map +1 -1
  85. package/dist/op/builders.d.ts +11 -0
  86. package/dist/op/builders.d.ts.map +1 -1
  87. package/dist/op/decide-backend.d.ts +5 -1
  88. package/dist/op/decide-backend.d.ts.map +1 -1
  89. package/dist/op/decide-read-inputs.d.ts +4 -2
  90. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  91. package/dist/op/factory.d.ts +83 -0
  92. package/dist/op/factory.d.ts.map +1 -0
  93. package/dist/op/index.d.ts +4 -1
  94. package/dist/op/index.d.ts.map +1 -1
  95. package/dist/op/op-ir.d.ts +2 -0
  96. package/dist/op/op-ir.d.ts.map +1 -1
  97. package/dist/op/op-verb-class.d.ts.map +1 -1
  98. package/dist/op/types.d.ts +8 -0
  99. package/dist/op/types.d.ts.map +1 -1
  100. package/dist/op/work-lease-run.d.ts.map +1 -1
  101. package/dist/plan-summary.d.ts +207 -0
  102. package/dist/plan-summary.d.ts.map +1 -0
  103. package/dist/pr-forge.d.ts +105 -0
  104. package/dist/pr-forge.d.ts.map +1 -0
  105. package/dist/pr-loop.d.ts +178 -0
  106. package/dist/pr-loop.d.ts.map +1 -0
  107. package/dist/telemetry-attribution.d.ts +31 -0
  108. package/dist/telemetry-attribution.d.ts.map +1 -1
  109. package/dist/terraform/hcl2json.d.ts +45 -0
  110. package/dist/terraform/hcl2json.d.ts.map +1 -0
  111. package/dist/terraform/parse.d.ts +4 -40
  112. package/dist/terraform/parse.d.ts.map +1 -1
  113. package/dist/vendor/js-yaml-load.d.ts +56 -0
  114. package/dist/vendor/js-yaml-load.d.ts.map +1 -0
  115. package/dist/workspace/box-cli.d.ts +12 -0
  116. package/dist/workspace/box-cli.d.ts.map +1 -0
  117. package/dist/workspace/box-factory.d.ts +27 -3
  118. package/dist/workspace/box-factory.d.ts.map +1 -1
  119. package/dist/workspace/box-listing.d.ts +85 -0
  120. package/dist/workspace/box-listing.d.ts.map +1 -0
  121. package/dist/workspace/box-publish.d.ts +147 -0
  122. package/dist/workspace/box-publish.d.ts.map +1 -0
  123. package/dist/workspace/broker-protocol.d.ts +190 -0
  124. package/dist/workspace/broker-protocol.d.ts.map +1 -0
  125. package/dist/workspace/checks.d.ts.map +1 -1
  126. package/dist/workspace/composites.d.ts +2 -2
  127. package/dist/workspace/composites.d.ts.map +1 -1
  128. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +17 -0
  129. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -0
  130. package/dist/workspace/conformance/broker.d.ts +202 -0
  131. package/dist/workspace/conformance/broker.d.ts.map +1 -0
  132. package/dist/workspace/conformance/index.d.ts +2 -1
  133. package/dist/workspace/conformance/index.d.ts.map +1 -1
  134. package/dist/workspace/conformance/vitest.d.ts +9 -2
  135. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  136. package/dist/workspace/conformance/writer.d.ts +108 -14
  137. package/dist/workspace/conformance/writer.d.ts.map +1 -1
  138. package/dist/workspace/decide.d.ts +126 -12
  139. package/dist/workspace/decide.d.ts.map +1 -1
  140. package/dist/workspace/decision-points.schema.json +11 -4
  141. package/dist/workspace/declaration.d.ts +65 -0
  142. package/dist/workspace/declaration.d.ts.map +1 -1
  143. package/dist/workspace/declaration.schema.json +153 -4
  144. package/dist/workspace/environments.d.ts +36 -10
  145. package/dist/workspace/environments.d.ts.map +1 -1
  146. package/dist/workspace/factory-rules.d.ts +180 -0
  147. package/dist/workspace/factory-rules.d.ts.map +1 -0
  148. package/dist/workspace/init.d.ts +21 -0
  149. package/dist/workspace/init.d.ts.map +1 -1
  150. package/dist/workspace/intent-cli.d.ts +1 -1
  151. package/dist/workspace/intent-cli.d.ts.map +1 -1
  152. package/dist/workspace/intent-record.d.ts +28 -1
  153. package/dist/workspace/intent-record.d.ts.map +1 -1
  154. package/dist/workspace/intent.d.ts +102 -5
  155. package/dist/workspace/intent.d.ts.map +1 -1
  156. package/dist/workspace/json-edit.d.ts +24 -0
  157. package/dist/workspace/json-edit.d.ts.map +1 -0
  158. package/dist/workspace/jsonc.d.ts +11 -0
  159. package/dist/workspace/jsonc.d.ts.map +1 -1
  160. package/dist/workspace/kinds.d.ts +48 -0
  161. package/dist/workspace/kinds.d.ts.map +1 -1
  162. package/dist/workspace/lexicon-symbols.d.ts +29 -0
  163. package/dist/workspace/lexicon-symbols.d.ts.map +1 -0
  164. package/dist/workspace/lineage-upgrade.d.ts +31 -2
  165. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  166. package/dist/workspace/point-answer.schema.json +42 -2
  167. package/dist/workspace/points-cli.d.ts +10 -3
  168. package/dist/workspace/points-cli.d.ts.map +1 -1
  169. package/dist/workspace/points.d.ts +66 -5
  170. package/dist/workspace/points.d.ts.map +1 -1
  171. package/dist/workspace/reason-codes.d.ts +33 -4
  172. package/dist/workspace/reason-codes.d.ts.map +1 -1
  173. package/dist/workspace/record-sessions.d.ts +5 -7
  174. package/dist/workspace/record-sessions.d.ts.map +1 -1
  175. package/dist/workspace/records-cli.d.ts +7 -0
  176. package/dist/workspace/records-cli.d.ts.map +1 -1
  177. package/dist/workspace/records-close.d.ts +7 -3
  178. package/dist/workspace/records-close.d.ts.map +1 -1
  179. package/dist/workspace/records-write.d.ts +66 -6
  180. package/dist/workspace/records-write.d.ts.map +1 -1
  181. package/dist/workspace/records.d.ts +28 -9
  182. package/dist/workspace/records.d.ts.map +1 -1
  183. package/dist/workspace/runs-cli.d.ts +138 -6
  184. package/dist/workspace/runs-cli.d.ts.map +1 -1
  185. package/dist/workspace/runs.d.ts +132 -11
  186. package/dist/workspace/runs.d.ts.map +1 -1
  187. package/dist/workspace/session-kinds.d.ts +1 -1
  188. package/dist/workspace/session-kinds.d.ts.map +1 -1
  189. package/dist/workspace/squash.d.ts +80 -0
  190. package/dist/workspace/squash.d.ts.map +1 -0
  191. package/dist/workspace/status-gates.d.ts +2 -0
  192. package/dist/workspace/status-gates.d.ts.map +1 -1
  193. package/dist/workspace/status.d.ts +54 -0
  194. package/dist/workspace/status.d.ts.map +1 -1
  195. package/dist/workspace/symbols.d.ts +16 -5
  196. package/dist/workspace/symbols.d.ts.map +1 -1
  197. package/dist/workspace/trust/run-commits.d.ts +46 -0
  198. package/dist/workspace/trust/run-commits.d.ts.map +1 -0
  199. package/dist/workspace/trust/run-statement.d.ts +128 -0
  200. package/dist/workspace/trust/run-statement.d.ts.map +1 -0
  201. package/dist/workspace/trust/seal.d.ts +6 -1
  202. package/dist/workspace/trust/seal.d.ts.map +1 -1
  203. package/dist/workspace/trust/verify-cli.d.ts +1 -1
  204. package/dist/workspace/trust/verify.d.ts +16 -2
  205. package/dist/workspace/trust/verify.d.ts.map +1 -1
  206. package/dist/workspace/wip-cli.d.ts +21 -0
  207. package/dist/workspace/wip-cli.d.ts.map +1 -0
  208. package/dist/workspace/wip.d.ts +268 -0
  209. package/dist/workspace/wip.d.ts.map +1 -0
  210. package/dist/workspace/work-cli.d.ts.map +1 -1
  211. package/dist/workspace/work-evidence.d.ts +1 -1
  212. package/dist/workspace/work-evidence.d.ts.map +1 -1
  213. package/dist/workspace/work-size.d.ts +55 -0
  214. package/dist/workspace/work-size.d.ts.map +1 -0
  215. package/dist/workspace/workspace-kinds.schema.json +122 -0
  216. package/dist/workspace/write-lock-cli.d.ts +60 -0
  217. package/dist/workspace/write-lock-cli.d.ts.map +1 -0
  218. package/dist/workspace/write-lock.d.ts +152 -0
  219. package/dist/workspace/write-lock.d.ts.map +1 -0
  220. package/dist/workspace/write-scope.d.ts +6 -2
  221. package/dist/workspace/write-scope.d.ts.map +1 -1
  222. package/dist/yaml.d.ts +1 -1
  223. package/dist/yaml.d.ts.map +1 -1
  224. package/package.json +6 -1
  225. package/src/__fixtures__/change-set/lifecycle-plan.floci.json +59 -0
  226. package/src/__fixtures__/change-set/lifecycle-v1.ts.txt +4 -0
  227. package/src/__fixtures__/change-set/lifecycle-v2.ts.txt +4 -0
  228. package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +95 -0
  229. package/src/__fixtures__/change-set/warden.change-set.golden.json +106 -0
  230. package/src/__fixtures__/change-set/warden.plan.json +44 -0
  231. package/src/__fixtures__/plan-summary/normalization-vectors.json +1593 -0
  232. package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +31 -0
  233. package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +31 -0
  234. package/src/__fixtures__/pr-loop/pr-plan.golden.json +127 -0
  235. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +37 -0
  236. package/src/audit/core.test.ts +15 -0
  237. package/src/audit/core.ts +8 -2
  238. package/src/audit/discover.test.ts +11 -0
  239. package/src/audit/discover.ts +7 -2
  240. package/src/change-set-bundle.test.ts +57 -0
  241. package/src/change-set.test.ts +215 -0
  242. package/src/change-set.ts +445 -0
  243. package/src/cli/commands/audit-opt-in.test.ts +54 -0
  244. package/src/cli/commands/audit.ts +29 -2
  245. package/src/cli/commands/build.test.ts +19 -0
  246. package/src/cli/commands/build.ts +3 -2
  247. package/src/cli/handlers/build.ts +13 -1
  248. package/src/cli/handlers/change-set.test.ts +56 -0
  249. package/src/cli/handlers/change-set.ts +44 -0
  250. package/src/cli/handlers/fan-out.test.ts +221 -2
  251. package/src/cli/handlers/fan-out.ts +165 -75
  252. package/src/cli/handlers/graph.ts +3 -0
  253. package/src/cli/handlers/operator.test.ts +21 -0
  254. package/src/cli/handlers/operator.ts +34 -3
  255. package/src/cli/handlers/pr.ts +373 -0
  256. package/src/cli/handlers/run.ts +1 -0
  257. package/src/cli/main.test.ts +20 -0
  258. package/src/cli/main.ts +185 -23
  259. package/src/cli/mcp/never-over-mcp.test.ts +101 -0
  260. package/src/cli/mcp/op-tools.ts +24 -2
  261. package/src/cli/mcp/workspace-tools.test.ts +1 -1
  262. package/src/cli/mcp/workspace-tools.ts +34 -6
  263. package/src/cli/registry.ts +63 -1
  264. package/src/components/auto-release.ts +1 -1
  265. package/src/components/capability.ts +55 -0
  266. package/src/components/changed-files.ts +66 -0
  267. package/src/components/cli-support.ts +28 -5
  268. package/src/components/component.schema.json +17 -0
  269. package/src/components/component.ts +25 -0
  270. package/src/components/deploy-units.ts +3 -0
  271. package/src/components/driver.test.ts +52 -0
  272. package/src/components/driver.ts +31 -13
  273. package/src/components/fan-out-output.ts +42 -1
  274. package/src/components/fan-out-record.ts +69 -0
  275. package/src/components/fan-out-run.test.ts +17 -0
  276. package/src/components/fan-out-run.ts +304 -1
  277. package/src/components/fan-out-support.ts +3 -0
  278. package/src/components/fan-out-waves.test.ts +375 -0
  279. package/src/components/fan-out.ts +18 -17
  280. package/src/components/gated-wave-pipeline.ts +82 -0
  281. package/src/components/index.ts +1 -0
  282. package/src/components/layers.ts +30 -0
  283. package/src/components/pr-pipeline.ts +91 -0
  284. package/src/components/pr-run.test.ts +205 -0
  285. package/src/components/pr-run.ts +280 -0
  286. package/src/components/promote.ts +5 -0
  287. package/src/components/verbs/shell.test.ts +11 -0
  288. package/src/components/verbs/shell.ts +7 -3
  289. package/src/components/wave-plan.ts +124 -0
  290. package/src/discovery/sandbox/bundle.ts +1 -1
  291. package/src/gated-waves-bundle.test.ts +62 -0
  292. package/src/gated-waves.test.ts +118 -0
  293. package/src/gated-waves.ts +240 -0
  294. package/src/index.ts +1 -0
  295. package/src/lexicon.ts +51 -0
  296. package/src/lifecycle/gate-ledger.ts +7 -0
  297. package/src/lifecycle/git.ts +102 -16
  298. package/src/lint/post-synth.test.ts +19 -0
  299. package/src/lint/post-synth.ts +19 -1
  300. package/src/meta/declared-imports.test.ts +1 -1
  301. package/src/op/activities/activity-contracts.ts +84 -0
  302. package/src/op/activities/change-set.ts +114 -0
  303. package/src/op/activities/decide.ts +3 -0
  304. package/src/op/activities/factory.ts +675 -0
  305. package/src/op/activities/index.ts +6 -0
  306. package/src/op/activity-contract.test.ts +21 -1
  307. package/src/op/activity-contract.ts +19 -9
  308. package/src/op/builders.ts +23 -0
  309. package/src/op/decide-backend.ts +5 -1
  310. package/src/op/decide-read-inputs.ts +33 -5
  311. package/src/op/factory.e2e.test.ts +232 -0
  312. package/src/op/factory.ts +118 -0
  313. package/src/op/index.ts +9 -1
  314. package/src/op/op-ir.test.ts +9 -0
  315. package/src/op/op-ir.ts +4 -0
  316. package/src/op/op-verb-class.ts +3 -0
  317. package/src/op/types.ts +8 -0
  318. package/src/op/work-lease-run.ts +3 -0
  319. package/src/plan-summary.test.ts +263 -0
  320. package/src/plan-summary.ts +845 -0
  321. package/src/pr-forge.test.ts +237 -0
  322. package/src/pr-forge.ts +332 -0
  323. package/src/pr-loop-bundle.test.ts +46 -0
  324. package/src/pr-loop.test.ts +171 -0
  325. package/src/pr-loop.ts +324 -0
  326. package/src/telemetry-attribution.test.ts +23 -1
  327. package/src/telemetry-attribution.ts +51 -0
  328. package/src/terraform/hcl2json.test.ts +21 -0
  329. package/src/terraform/hcl2json.ts +81 -0
  330. package/src/terraform/parse.ts +11 -74
  331. package/src/vendor/js-yaml-load.test.ts +115 -0
  332. package/src/vendor/js-yaml-load.ts +1939 -0
  333. package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
  334. package/src/workspace/app-kind.test.ts +196 -0
  335. package/src/workspace/box-cli.ts +113 -0
  336. package/src/workspace/box-factory.test.ts +4 -1
  337. package/src/workspace/box-factory.ts +37 -4
  338. package/src/workspace/box-listing-write.schema.json +204 -0
  339. package/src/workspace/box-listing.test.ts +234 -0
  340. package/src/workspace/box-listing.ts +324 -0
  341. package/src/workspace/box-publish.schema.json +498 -0
  342. package/src/workspace/box-publish.test.ts +204 -0
  343. package/src/workspace/box-publish.ts +420 -0
  344. package/src/workspace/box-ship.test.ts +96 -0
  345. package/src/workspace/broker-protocol.schema.json +186 -0
  346. package/src/workspace/broker-protocol.test.ts +122 -0
  347. package/src/workspace/broker-protocol.ts +317 -0
  348. package/src/workspace/builder-tiers.test.ts +146 -0
  349. package/src/workspace/change-set.schema.json +460 -0
  350. package/src/workspace/chant-migrations.e2e.test.ts +17 -5
  351. package/src/workspace/checks/boxes.test.ts +3 -0
  352. package/src/workspace/checks.ts +10 -3
  353. package/src/workspace/component-environments.test.ts +105 -0
  354. package/src/workspace/composites.schema.json +23 -4
  355. package/src/workspace/composites.test.ts +8 -8
  356. package/src/workspace/composites.ts +13 -2
  357. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +2 -2
  358. package/src/workspace/conformance/__fixtures__/reference-broker.ts +207 -0
  359. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +7 -2
  360. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +42 -2
  361. package/src/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
  362. package/src/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
  363. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +96 -0
  364. package/src/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
  365. package/src/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
  366. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +14 -8
  367. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +187 -6
  368. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +25 -0
  369. package/src/workspace/conformance/broker.test.ts +122 -0
  370. package/src/workspace/conformance/broker.ts +792 -0
  371. package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
  372. package/src/workspace/conformance/index.mjs +22 -1
  373. package/src/workspace/conformance/index.ts +5 -1
  374. package/src/workspace/conformance/vitest.ts +33 -1
  375. package/src/workspace/conformance/writer-conformance.e2e.test.ts +49 -3
  376. package/src/workspace/conformance/writer.test.ts +32 -2
  377. package/src/workspace/conformance/writer.ts +339 -28
  378. package/src/workspace/decide.test.ts +208 -4
  379. package/src/workspace/decide.ts +427 -73
  380. package/src/workspace/decision-points.schema.json +11 -4
  381. package/src/workspace/declaration.schema.json +153 -4
  382. package/src/workspace/declaration.ts +141 -0
  383. package/src/workspace/environments.ts +66 -14
  384. package/src/workspace/factory-rules.test.ts +62 -0
  385. package/src/workspace/factory-rules.ts +218 -0
  386. package/src/workspace/graph-cli.ts +2 -2
  387. package/src/workspace/graph.schema.json +67 -0
  388. package/src/workspace/identity.test.ts +23 -0
  389. package/src/workspace/init.ts +82 -2
  390. package/src/workspace/intent-cli.ts +26 -9
  391. package/src/workspace/intent-patch-id.test.ts +162 -0
  392. package/src/workspace/intent-record.schema.json +130 -3
  393. package/src/workspace/intent-record.ts +74 -28
  394. package/src/workspace/intent-reviews.test.ts +76 -0
  395. package/src/workspace/intent-squash.test.ts +213 -0
  396. package/src/workspace/intent-why.test.ts +5 -5
  397. package/src/workspace/intent.schema.json +234 -4
  398. package/src/workspace/intent.ts +260 -66
  399. package/src/workspace/json-edit.test.ts +121 -0
  400. package/src/workspace/json-edit.ts +142 -0
  401. package/src/workspace/jsonc.ts +20 -0
  402. package/src/workspace/kinds.ts +157 -2
  403. package/src/workspace/lexicon-symbols.test.ts +148 -0
  404. package/src/workspace/lexicon-symbols.ts +125 -0
  405. package/src/workspace/lineage-upgrade.test.ts +63 -0
  406. package/src/workspace/lineage-upgrade.ts +146 -5
  407. package/src/workspace/plan-summary.schema.json +155 -0
  408. package/src/workspace/point-answer.schema.json +42 -2
  409. package/src/workspace/points-cli.ts +42 -16
  410. package/src/workspace/points-write.schema.json +162 -4
  411. package/src/workspace/points.schema.json +157 -2
  412. package/src/workspace/points.test.ts +13 -1
  413. package/src/workspace/points.ts +118 -12
  414. package/src/workspace/pr-report.schema.json +95 -0
  415. package/src/workspace/profiles.test.ts +159 -0
  416. package/src/workspace/read-contract.test.ts +2 -2
  417. package/src/workspace/reason-codes.test.ts +13 -1
  418. package/src/workspace/reason-codes.ts +38 -4
  419. package/src/workspace/record-sessions.ts +9 -8
  420. package/src/workspace/records-amend.schema.json +78 -0
  421. package/src/workspace/records-cli.ts +20 -6
  422. package/src/workspace/records-close.schema.json +78 -0
  423. package/src/workspace/records-close.ts +31 -7
  424. package/src/workspace/records-concurrent.e2e.test.ts +135 -0
  425. package/src/workspace/records-concurrent.test.ts +145 -0
  426. package/src/workspace/records-formats.test.ts +46 -10
  427. package/src/workspace/records-new.schema.json +7 -0
  428. package/src/workspace/records-review.schema.json +78 -0
  429. package/src/workspace/records-sessions-write.test.ts +69 -0
  430. package/src/workspace/records-sessions.test.ts +22 -0
  431. package/src/workspace/records-write.ts +192 -25
  432. package/src/workspace/records.schema.json +16 -0
  433. package/src/workspace/records.ts +50 -9
  434. package/src/workspace/reference-kinds/app/package.json +10 -0
  435. package/src/workspace/reference-kinds/app/workspace-kinds.json +45 -0
  436. package/src/workspace/run-statement.schema.json +622 -0
  437. package/src/workspace/runs-cli.ts +343 -14
  438. package/src/workspace/runs-statement.test.ts +239 -0
  439. package/src/workspace/runs-write.schema.json +416 -5
  440. package/src/workspace/runs.schema.json +451 -4
  441. package/src/workspace/runs.test.ts +39 -0
  442. package/src/workspace/runs.ts +296 -35
  443. package/src/workspace/session-kinds.ts +3 -3
  444. package/src/workspace/squash.ts +198 -0
  445. package/src/workspace/status-contract.test.ts +5 -2
  446. package/src/workspace/status-gates.ts +3 -1
  447. package/src/workspace/status.schema.json +126 -3
  448. package/src/workspace/status.ts +90 -1
  449. package/src/workspace/symbols.ts +20 -9
  450. package/src/workspace/trust/run-commits.ts +115 -0
  451. package/src/workspace/trust/run-statement.ts +226 -0
  452. package/src/workspace/trust/seal.ts +8 -2
  453. package/src/workspace/trust/verify-cli.ts +6 -5
  454. package/src/workspace/trust/verify.ts +39 -6
  455. package/src/workspace/wip-cli.ts +97 -0
  456. package/src/workspace/wip-write.schema.json +504 -0
  457. package/src/workspace/wip.schema.json +334 -0
  458. package/src/workspace/wip.test.ts +294 -0
  459. package/src/workspace/wip.ts +753 -0
  460. package/src/workspace/work-cli.ts +3 -0
  461. package/src/workspace/work-evidence.schema.json +3 -0
  462. package/src/workspace/work-size.test.ts +77 -0
  463. package/src/workspace/work-size.ts +93 -0
  464. package/src/workspace/workspace-kinds.schema.json +122 -0
  465. package/src/workspace/write-lock-cli.ts +131 -0
  466. package/src/workspace/write-lock.schema.json +172 -0
  467. package/src/workspace/write-lock.test.ts +189 -0
  468. package/src/workspace/write-lock.ts +474 -0
  469. package/src/workspace/write-scope.test.ts +16 -1
  470. package/src/workspace/write-scope.ts +33 -11
  471. package/src/yaml.ts +31 -21
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://intentius.io/chant/schemas/workspace/decision-points/v1/decision-points.schema.json",
4
4
  "title": "Decision points",
5
- "description": "A workspace's decision points (ws-058, #2738): the recurring questions it asks of its own graph, declared as data in a JSON file that an answer record kind names in `answers.points`. Each point is a typed question (`noul`, `choice` with 2 to 255 options, or `score` with 2 to 10 ordered levels, the question types of the POST /v1/systemone wire format, #2491), the inputs it reads, each named as a read-contract output, and an ordered chain of deciders: `table` rows, `model` deciders with a backend, a pinned model id and a threshold, and a `quorum` of people, always last. What JSON Schema cannot say is checked in code (`points.ts`): an input's output is one the read contract has, a table row tests declared inputs and answers a candidate, a field belongs to its decider's kind, a model id is pinned rather than an alias, and the chain ends in its one quorum. Unknown fields are refused, except fields whose names start with x-.",
5
+ "description": "A workspace's decision points (ws-058, #2738): the recurring questions it asks of its own graph, declared as data in a JSON file that an answer record kind names in `answers.points`. Each point is a typed question (`noul`, `choice` with 2 to 255 options, or `score` with 2 to 10 ordered levels, the question types of the POST /v1/systemone wire format, #2491), the inputs it reads, each named as a read-contract output, and an ordered chain of deciders: `table` rows, `model` deciders with a backend, a pinned model id and a threshold, and a `quorum` of people, always last. What JSON Schema cannot say is checked in code (`points.ts`): an input's output is one the read contract has, a table row tests declared inputs and answers a candidate, a field belongs to its decider's kind, a model id is pinned rather than an alias, and the chain ends in its one quorum. A point declared `adhoc` (#3403) leaves out its question's criteria: the question's text and candidates come with each ask (`points ask --candidates`), and its chain is one quorum. Unknown fields are refused, except fields whose names start with x-.",
6
6
  "type": "object",
7
7
  "required": ["points"],
8
8
  "properties": {
@@ -23,10 +23,14 @@
23
23
  "required": ["title", "question", "inputs", "deciders"],
24
24
  "properties": {
25
25
  "title": { "$ref": "#/$defs/text" },
26
+ "adhoc": {
27
+ "type": "boolean",
28
+ "description": "An ad-hoc point (#3403): the question declares its type and what the asks are for, and no criteria. Each ask gives the question's text and its candidates, and the answer record keeps them, so people's answer is checked against them. Its chain is one quorum."
29
+ },
26
30
  "question": { "$ref": "#/$defs/question" },
27
31
  "inputs": {
28
32
  "type": "object",
29
- "description": "The state the deciders see, input name to description. A name is a read-contract output (record, decision, work-item, finding, region, commit, member, gate, release, environment, component), optionally followed by dotted field names, such as work-item.fits_small: a field of that output, or a value the caller derives from it. The caller supplies the values; a table row can only test these names.",
33
+ "description": "The state the deciders see, input name to description. A name is a read-contract output (record, decision, work-item, finding, region, commit, node, member, gate, release, environment, component, ask), optionally followed by dotted field names, such as work-item.fits_small: a field of that output, or a value the caller derives from it. The caller supplies the values; a table row can only test these names.",
30
34
  "minProperties": 1,
31
35
  "propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_-]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$" },
32
36
  "additionalProperties": { "type": "string" }
@@ -38,11 +42,14 @@
38
42
  }
39
43
  },
40
44
  "patternProperties": { "^x-": true },
41
- "additionalProperties": false
45
+ "additionalProperties": false,
46
+ "if": { "properties": { "adhoc": { "const": true } }, "required": ["adhoc"] },
47
+ "then": { "properties": { "question": { "not": { "required": ["criteria"] } } } },
48
+ "else": { "properties": { "question": { "required": ["criteria"] } } }
42
49
  },
43
50
  "question": {
44
51
  "type": "object",
45
- "required": ["type", "instructions", "criteria"],
52
+ "required": ["type", "instructions"],
46
53
  "properties": {
47
54
  "type": { "enum": ["noul", "choice", "score"] },
48
55
  "instructions": { "$ref": "#/$defs/text" },
@@ -369,6 +369,9 @@
369
369
  "$ref": "#/$defs/outputName"
370
370
  }
371
371
  },
372
+ "fields": {
373
+ "$ref": "#/$defs/memberFields"
374
+ },
372
375
  "links": {
373
376
  "type": "array",
374
377
  "description": "Member links (#2524 D6, #2539): the outputs of other members this member reads. A link is stated once, here on the consumer, and matched exactly against the producer's outputs. chant workspace check resolves it in source.",
@@ -438,6 +441,23 @@
438
441
  ]
439
442
  }
440
443
  },
444
+ "memberFields": {
445
+ "type": "object",
446
+ "description": "Values for the fields the member's kind declares (#3151), such as an app member's scripts, env and health. Each is a string, integer or boolean, or an object of those for a field the kind declares as an object. chant workspace check fails when the kind declares no such field or a value is of the wrong type (WSP005), and chant workspace status --json prints every field the kind declares with its defaults filled in. Added in schema 1 by chant 0.103.0, so a declaration that uses it sets minReader to 0.103.0 or newer.",
447
+ "propertyNames": { "pattern": "^[a-z][A-Za-z0-9]{0,39}$" },
448
+ "additionalProperties": {
449
+ "oneOf": [
450
+ { "type": "string" },
451
+ { "type": "integer" },
452
+ { "type": "boolean" },
453
+ {
454
+ "type": "object",
455
+ "propertyNames": { "pattern": "^[a-z][A-Za-z0-9]{0,39}$" },
456
+ "additionalProperties": { "oneOf": [{ "type": "string" }, { "type": "integer" }, { "type": "boolean" }] }
457
+ }
458
+ ]
459
+ }
460
+ },
441
461
  "group": {
442
462
  "type": "object",
443
463
  "description": "An example group (ws-051): every directory the glob matches that holds a chant project is built and linted. A group has no ledger, releases or links, and its matches are never members. A match may sit inside a member's directory; that member leaves it to the group.",
@@ -484,7 +504,7 @@
484
504
  },
485
505
  "box": {
486
506
  "type": "object",
487
- "description": "The member is a box, or holds a box's declarations (#2726). A box holds no credential: each capability it needs outside itself is reached through a broker, a runtime such as a lobby or a door, which holds the credential and enforces the scope. chant workspace check fails when a file in the member's directory carries a literal secret (WSP121) and when a capability names no broker (WSP122). chant workspace status --json lists the capabilities, so a broker can read the scopes. Its host, slot, ports, state and cookies state the box's isolation (#2727): chant derives each value from the box's identity, its host, the member's name and its slot, and prints them in chant workspace status --json, so a runtime reads them instead of choosing its own. chant workspace check fails when two boxes on a host resolve to the same value (WSP123) and when a box hard-codes a machine path (WSP124). A member that builds the fountain lexicon's Box declares the callback token fountain gives its persistent sandbox as the capability fountain-callback, brokered by fountain, with scope owner, or chant workspace check fails (WSP125, #2780). Its intent names the decision record that says what the box is for (#2850): chant workspace status --json reports the record's state and answer, and chant workspace check fails when no decision record has the id (WSP126) and warns when the record constrains no member or path of this workspace (WSP127, #2857). Its services list what the box runs under its supervisor (#2880), which the fly lexicon observes, restarts and applies through sprite-env, and which chant workspace status --json prints. Its factory declares what the box builds, how a build is checked, which member declares the builder agents and where a finished build is published (#3146, ws-077), and its listing what the box shows of itself (#3146). Added in schema 1 by chant 0.91.0, so a declaration that uses it sets minReader to 0.91.0 or newer.",
507
+ "description": "The member is a box, or holds a box's declarations (#2726). A box holds no credential: each capability it needs outside itself is reached through a broker, a runtime such as a lobby or a door, which holds the credential and enforces the scope. chant workspace check fails when a file in the member's directory carries a literal secret (WSP121) and when a capability names no broker (WSP122). chant workspace status --json lists the capabilities, so a broker can read the scopes. Its host, slot, ports, state and cookies state the box's isolation (#2727): chant derives each value from the box's identity, its host, the member's name and its slot, and prints them in chant workspace status --json, so a runtime reads them instead of choosing its own. chant workspace check fails when two boxes on a host resolve to the same value (WSP123) and when a box hard-codes a machine path (WSP124). A member that builds the fountain lexicon's Box declares the callback token fountain gives its persistent sandbox as the capability fountain-callback, brokered by fountain, with scope owner, or chant workspace check fails (WSP125, #2780). Its intent names the decision record that says what the box is for (#2850): chant workspace status --json reports the record's state and answer, and chant workspace check fails when no decision record has the id (WSP126) and warns when the record constrains no member or path of this workspace (WSP127, #2857). Its services list what the box runs under its supervisor (#2880), which the fly lexicon observes, restarts and applies through sprite-env, and which chant workspace status --json prints. Its factory declares what the box builds, how a build is checked, which member declares the builder agents and where a finished build is published (#3146, ws-077), its listing what the box shows of itself (#3146), and its replicate where the box's work in progress is pushed so a lost box loses none of it (#3172, ws-085). Added in schema 1 by chant 0.91.0, so a declaration that uses it sets minReader to 0.91.0 or newer.",
488
508
  "properties": {
489
509
  "intent": {
490
510
  "type": "string",
@@ -504,6 +524,18 @@
504
524
  "listing": {
505
525
  "$ref": "#/$defs/boxListing"
506
526
  },
527
+ "publisher": {
528
+ "type": "string",
529
+ "minLength": 1,
530
+ "pattern": "\\S",
531
+ "description": "The command that publishes the box's work (#3165, ws-088): it applies a built work item to the box's checkout and, when factory.publish names a target, pushes it and opens the pull request, and it sends the records a person kept uncommitted. It is run from the workspace root by chant workspace box publish, split into words as a shell splits plain words (quotes group, a backslash escapes) but never given to a shell, with a JSON request on stdin; box-publish.schema.json says what it reads and prints. An orchestrator such as studio supplies it. chant workspace status --json prints it under the member's box, so a surface offers a publish only when a box names one. Added in schema 1 by chant 0.103.0, so a declaration that uses it sets minReader to 0.103.0 or newer."
532
+ },
533
+ "replicate": {
534
+ "$ref": "#/$defs/boxReplicate"
535
+ },
536
+ "ship": {
537
+ "$ref": "#/$defs/boxShip"
538
+ },
507
539
  "capabilities": {
508
540
  "type": "array",
509
541
  "description": "Each capability the box reaches outside itself, named once. None when omitted.",
@@ -634,7 +666,41 @@
634
666
  },
635
667
  "builders": {
636
668
  "$ref": "#/$defs/name",
637
- "description": "The member that declares the builder agents, such as the one whose agents/ holds them. Which agent builds at which tier is #3152; the tier vocabulary is the work kind's work.tier.tiers (#3147)."
669
+ "description": "The member that declares the builder agents, such as the one whose agents/ holds them. tiers says which of them builds at which tier (#3152)."
670
+ },
671
+ "tiers": {
672
+ "type": "array",
673
+ "minItems": 1,
674
+ "description": "Which builder agent builds at which tier (#3152, ws-094), replacing studio's studio/role and studio/tier Agent metadata. Each entry names a tier, in the vocabulary of the work kind's work.tier.tiers (#3147), and an agent the builders member declares, by its name. An entry with kinds builds only members of those kinds, so an infra builder and an app builder can share a tier; an entry without kinds builds the tier for every other kind. A tier has at most one entry without kinds and at most one per kind, tiers needs builders, and a session names a declared agent session, or the declaration can't be read (declaration-invalid). chant workspace status --json prints builderFor, the agent for each built member and tier. Added in schema 1 by chant 0.103.0, so a declaration that uses it sets minReader to 0.103.0 or newer.",
675
+ "items": {
676
+ "type": "object",
677
+ "required": ["tier", "agent"],
678
+ "properties": {
679
+ "tier": {
680
+ "$ref": "#/$defs/kindName",
681
+ "description": "The tier, such as small, medium or large: a word of the work kind's work.tier.tiers, which a work item's tier or the slice-tier decision point answers."
682
+ },
683
+ "agent": {
684
+ "type": "string",
685
+ "minLength": 1,
686
+ "pattern": "^[^\\u0000-\\u001f]+$",
687
+ "description": "The builder agent's name, as the builders member declares it (such as an Agent of the fountain lexicon in its agents/)."
688
+ },
689
+ "kinds": {
690
+ "type": "array",
691
+ "minItems": 1,
692
+ "uniqueItems": true,
693
+ "items": { "$ref": "#/$defs/kindName" },
694
+ "description": "The member kinds this agent builds at the tier, such as app or terraform. Without it, every kind no other entry of the tier names."
695
+ },
696
+ "session": {
697
+ "$ref": "#/$defs/name",
698
+ "description": "The declared agent session (agents) the builder writes as, so its write scope is that session's. Optional: without it the orchestrator chooses."
699
+ }
700
+ },
701
+ "patternProperties": { "^x-": true },
702
+ "additionalProperties": false
703
+ }
638
704
  },
639
705
  "publish": {
640
706
  "type": "object",
@@ -712,6 +778,89 @@
712
778
  },
713
779
  "additionalProperties": false
714
780
  },
781
+ "boxReplicate": {
782
+ "type": "object",
783
+ "description": "Where the box's work in progress is replicated, so a box that is lost, reclaimed or corrupted before anyone commits loses nothing (#3172, ws-085). Work in progress is the work branches (chant/work/...), the kept attempts (refs/chant/kept/..., #3147), the snapshots chant workspace wip save takes of each branch's working tree, uncommitted records included (refs/chant/wip/<branch>), and the ledger branch, chant/lifecycle. chant pushes them to remote under their own names and never forces a push. Opt-in: a box block without it replicates nothing. At most one box block in a declaration declares one, or the declaration can't be read (declaration-invalid). chant workspace status --json prints it under the member's box, with every default filled in, and reports in replication how far each ref is from the remote. Added in schema 1 by chant 0.103.0, so a declaration that uses it sets minReader to 0.103.0 or newer.",
784
+ "properties": {
785
+ "remote": {
786
+ "type": "string",
787
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
788
+ "description": "The git remote to push to, by name: the repository's own remote, such as origin, or a mirror the host adds to the box's checkout with its credential, since a box holds none (#2726). origin when omitted."
789
+ },
790
+ "refs": {
791
+ "type": "array",
792
+ "minItems": 1,
793
+ "uniqueItems": true,
794
+ "description": "What is replicated: work (the branches under chant/work/), kept (the kept attempts under refs/chant/kept/), wip (the snapshots under refs/chant/wip/) and ledger (the chant/lifecycle branch). All four when omitted.",
795
+ "items": {
796
+ "enum": [
797
+ "work",
798
+ "kept",
799
+ "wip",
800
+ "ledger"
801
+ ]
802
+ }
803
+ },
804
+ "on": {
805
+ "type": "array",
806
+ "uniqueItems": true,
807
+ "description": "When chant pushes on its own: save, after each chant workspace wip save, and release, after a work lease is released (chant workspace work release, or a run of an Op under a work lease ending, which is when a kept attempt is written). Both when omitted; an empty list leaves every push to chant workspace wip push. A record write never pushes, because chant's record writes never run git (ws-074).",
808
+ "items": {
809
+ "enum": [
810
+ "save",
811
+ "release"
812
+ ]
813
+ }
814
+ },
815
+ "every": {
816
+ "type": "string",
817
+ "pattern": "^[1-9][0-9]*(s|m|h)$",
818
+ "description": "How often the host runs chant workspace wip push on a schedule of its own, such as 10m, beside the pushes on save and release. chant runs no scheduler; it only states the interval. None when omitted."
819
+ }
820
+ },
821
+ "patternProperties": {
822
+ "^x-": true
823
+ },
824
+ "additionalProperties": false
825
+ },
826
+ "boxShip": {
827
+ "type": "object",
828
+ "description": "How the box ships its staged work to its own site (ws-100): the people working on a box see the working tree at once, everyone else sees the last release, and a person ships. op is the Op in the box member that does it: its own steps commit the staged tree as one commit by the person shipping (named to it in CHANT_SHIP_BY) with a Chant-Record trailer for each work item it lands, then release that commit to the box's site, stopping at gate, and record the release in env's release ledger. chant runs none of it; a surface runs chant run <op> in the member's directory and approves the gate as for any Op. chant workspace status --json prints it under the member's box with the commit the site serves (the latest release in env's ledger) and what is waiting to ship (the files changed between that commit and the working tree, bookkeeping left out). Added in schema 1 by chant 0.104.0, so a declaration that uses it sets minReader to 0.104.0 or newer.",
829
+ "required": [
830
+ "op"
831
+ ],
832
+ "properties": {
833
+ "op": {
834
+ "type": "string",
835
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
836
+ "description": "The name of the Op in the box member that ships, as chant run takes it."
837
+ },
838
+ "gate": {
839
+ "type": "string",
840
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
841
+ "description": "The gate the Op stops at for a person's approval. ship when omitted."
842
+ },
843
+ "env": {
844
+ "type": "string",
845
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
846
+ "description": "The environment whose release ledger, under the box member, the Op records each release in. box when omitted."
847
+ },
848
+ "bookkeeping": {
849
+ "type": "array",
850
+ "uniqueItems": true,
851
+ "description": "Path prefixes from the workspace root whose changes are bookkeeping, such as the record directories, and never count as waiting to ship. None when omitted.",
852
+ "items": {
853
+ "type": "string",
854
+ "minLength": 1,
855
+ "pattern": "^(?!/)(?!.*(^|/)\\.\\.(/|$))[^\\n]+$"
856
+ }
857
+ }
858
+ },
859
+ "patternProperties": {
860
+ "^x-": true
861
+ },
862
+ "additionalProperties": false
863
+ },
715
864
  "boxService": {
716
865
  "type": "object",
717
866
  "description": "A service a box runs under its supervisor (#2880).",
@@ -1103,7 +1252,7 @@
1103
1252
  }
1104
1253
  },
1105
1254
  "protectedPath": {
1106
- "description": "A protected path: a glob from the workspace root, or { path, except } for a JSON file whose listed top-level keys may still change.",
1255
+ "description": "A protected path: a glob from the workspace root, or { path, except } for a JSON file whose listed top-level keys, or the values JSON Pointers name, may still change.",
1107
1256
  "oneOf": [
1108
1257
  {
1109
1258
  "$ref": "#/$defs/fileGlob"
@@ -1121,7 +1270,7 @@
1121
1270
  "type": "array",
1122
1271
  "minItems": 1,
1123
1272
  "uniqueItems": true,
1124
- "description": "Top-level keys of the JSON file a write may change. A change anywhere else in the file, or to a file that doesn't parse as a JSON object before and after, is still outside the scope.",
1273
+ "description": "What a write may change in the JSON file: a top-level key, or, for an entry starting with /, the values a JSON Pointer matches, where a * token matches every key or array index at its level, such as /members/*/box/listing for every box's listing (#3308). A change anywhere else in the file, or to a file that doesn't parse as a JSON object before and after, is still outside the scope.",
1125
1274
  "items": {
1126
1275
  "type": "string",
1127
1276
  "minLength": 1
@@ -295,6 +295,63 @@ export interface BoxDeclaration {
295
295
  factory: BoxFactory | null;
296
296
  /** What the box shows of itself on a home site (#3146), or null when the block declares no listing. */
297
297
  listing: BoxListing | null;
298
+ /**
299
+ * The command that publishes the box's work (#3165, ws-088), as declared,
300
+ * or null when the block names none. `chant workspace box publish` runs it
301
+ * (`box-publish.ts`).
302
+ */
303
+ publisher: string | null;
304
+ /** Where the box's work in progress is replicated (#3172, ws-085), or null when the block declares no policy. */
305
+ replicate: BoxReplicate | null;
306
+ /** How the box ships its staged work to its own site (ws-100), or null when the block declares none. */
307
+ ship: BoxShip | null;
308
+ /** The block's JSON Pointer in the file, for messages. */
309
+ pointer: string;
310
+ }
311
+
312
+ /** The gate a ship Op stops at when the block names none (ws-100). */
313
+ export const DEFAULT_SHIP_GATE = "ship";
314
+ /** The environment a ship Op records its releases in when the block names none (ws-100). */
315
+ export const DEFAULT_SHIP_ENV = "box";
316
+
317
+ /**
318
+ * How a box ships its staged work (ws-100): the Op in the box member that
319
+ * commits the staged tree and releases it to the box's own site, the gate
320
+ * it stops at, the environment whose release ledger it records each
321
+ * release in, and the paths that are bookkeeping rather than the app (left
322
+ * out of what is pending). Defaults are filled in.
323
+ */
324
+ export interface BoxShip {
325
+ op: string;
326
+ gate: string;
327
+ env: string;
328
+ /** Path prefixes from the workspace root, in file order, whose changes never count as waiting to ship. */
329
+ bookkeeping: string[];
330
+ /** The block's JSON Pointer in the file, for messages. */
331
+ pointer: string;
332
+ }
333
+
334
+ /** What a replicate policy pushes (#3172): work branches, kept attempts, work-in-progress snapshots and the ledger branch. */
335
+ export const REPLICATE_REF_CLASSES = ["work", "kept", "wip", "ledger"] as const;
336
+ export type ReplicateRefClass = (typeof REPLICATE_REF_CLASSES)[number];
337
+
338
+ /** When chant pushes on its own under a replicate policy (#3172). */
339
+ export const REPLICATE_TRIGGERS = ["save", "release"] as const;
340
+ export type ReplicateTrigger = (typeof REPLICATE_TRIGGERS)[number];
341
+
342
+ /** The remote a replicate policy pushes to when it names none. */
343
+ export const DEFAULT_REPLICATE_REMOTE = "origin";
344
+
345
+ /**
346
+ * Where a box's work in progress is replicated (#3172, ws-085), with the
347
+ * defaults filled in: the git remote, which refs, and when chant pushes on
348
+ * its own. `every` is an interval for the host's own scheduler.
349
+ */
350
+ export interface BoxReplicate {
351
+ remote: string;
352
+ refs: ReplicateRefClass[];
353
+ on: ReplicateTrigger[];
354
+ every: string | null;
298
355
  /** The block's JSON Pointer in the file, for messages. */
299
356
  pointer: string;
300
357
  }
@@ -317,12 +374,25 @@ export interface BoxFactory {
317
374
  checks: string | null;
318
375
  /** The member that declares the builder agents, or null when none is named. */
319
376
  builders: string | null;
377
+ /** Which builder agent builds at which tier, and for which member kinds (#3152, ws-094), in file order; empty when none is declared. */
378
+ tiers: FactoryTier[];
320
379
  /** Where a finished build is published, or null when the factory doesn't publish past the box. */
321
380
  publish: FactoryPublish | null;
322
381
  /** The block's JSON Pointer in the file, for messages. */
323
382
  pointer: string;
324
383
  }
325
384
 
385
+ /** One builder agent at one tier (#3152): `kinds` null means every kind no other entry of the tier names. */
386
+ export interface FactoryTier {
387
+ tier: string;
388
+ /** The agent's name, as the builders member declares it. */
389
+ agent: string;
390
+ /** The member kinds it builds at this tier, or null for the tier's default. */
391
+ kinds: string[] | null;
392
+ /** The declared agent session it writes as, or null for the orchestrator's choice. */
393
+ session: string | null;
394
+ }
395
+
326
396
  /** Where a finished build goes (#3146): its branch is pushed and a pull request opened against `repo`'s `base`. */
327
397
  export interface FactoryPublish {
328
398
  forge: "github";
@@ -403,6 +473,8 @@ export interface Member {
403
473
  generated: GeneratedFile[];
404
474
  /** Outputs the entry lists as link targets, or null when it lists none (#2539). */
405
475
  outputs: string[] | null;
476
+ /** The values the entry sets for its kind's fields (#3151), as written, or null when it sets none. Checked against the kind by `resolveMemberFields`. */
477
+ fields: Record<string, unknown> | null;
406
478
  /** The links this member states as a consumer, in file order (#2539). */
407
479
  links: LinkDeclaration[];
408
480
  /** The record kinds this member declares, in file order (#2680). */
@@ -690,6 +762,7 @@ export function parseDeclaration(text: string, file: string, reader: string = re
690
762
  roles,
691
763
  generated,
692
764
  outputs: e.outputs === undefined ? null : [...(e.outputs as string[])],
765
+ fields: e.fields === undefined ? null : structuredClone(e.fields as Record<string, unknown>),
693
766
  links,
694
767
  records,
695
768
  diagrams,
@@ -794,6 +867,15 @@ export function parseDeclaration(text: string, file: string, reader: string = re
794
867
  // One factory, building and naming declared members (#3146).
795
868
  const factory = factoryProblem(members);
796
869
  if (factory) throw new WorkspaceReadError("declaration-invalid", factory.message, at(factory.pointer));
870
+ // One replicate policy (#3172): the checkout is the whole repository's, so one remote takes its work in progress.
871
+ const replicating = members.filter((m) => m.box?.replicate);
872
+ if (replicating.length > 1) {
873
+ throw new WorkspaceReadError(
874
+ "declaration-invalid",
875
+ `members ${replicating[0].name} and ${replicating[1].name} both declare replicate in their box block; a checkout's work in progress goes to one remote, so a workspace has one replicate policy`,
876
+ at(replicating[1].box!.replicate!.pointer),
877
+ );
878
+ }
797
879
 
798
880
  // A diagram name is given once across the declaration (#2764): a reader keys diagrams by name.
799
881
  const ownDiagrams = diagramsOf(obj.diagrams, null, "/diagrams");
@@ -809,6 +891,8 @@ export function parseDeclaration(text: string, file: string, reader: string = re
809
891
  const hosts = hostsOf(obj, members, at);
810
892
  const writeScope = writeScopeOf(obj.writeScope, members, at);
811
893
  const agents = agentsOf(obj.agents, members, at);
894
+ const tierSession = factoryTierSessionProblem(members, agents);
895
+ if (tierSession) throw new WorkspaceReadError("declaration-invalid", tierSession.message, at(tierSession.pointer));
812
896
 
813
897
  const pins = ((obj.pins as Record<string, string>[] | undefined) ?? []).map((p) => ({
814
898
  package: p.package ?? null,
@@ -968,12 +1052,17 @@ function boxOf(raw: unknown, pointer: string): BoxDeclaration | null {
968
1052
  check?: string | { run: string; kind?: FactoryCheckKind };
969
1053
  checks?: string;
970
1054
  builders?: string;
1055
+ tiers?: { tier: string; agent: string; kinds?: string[]; session?: string }[];
971
1056
  publish?: { forge?: "github"; repo: string; base?: string; branchPrefix?: string; head?: string };
972
1057
  };
973
1058
  listing?: { published?: boolean; title?: string; line?: string; cover?: string };
1059
+ publisher?: string;
1060
+ replicate?: { remote?: string; refs?: ReplicateRefClass[]; on?: ReplicateTrigger[]; every?: string };
1061
+ ship?: { op: string; gate?: string; env?: string; bookkeeping?: string[] };
974
1062
  };
975
1063
  const f = b.factory;
976
1064
  const l = b.listing;
1065
+ const r = b.replicate;
977
1066
  return {
978
1067
  capabilities: (b.capabilities ?? []).map((c, i) => ({ name: c.name, broker: c.broker ?? null, scope: [...(c.scope ?? [])], pointer: `${pointer}/capabilities/${i}` })),
979
1068
  // The schema requires host and slot together, and host for ports, state and cookies.
@@ -1000,6 +1089,7 @@ function boxOf(raw: unknown, pointer: string): BoxDeclaration | null {
1000
1089
  check: f.check === undefined ? null : typeof f.check === "string" ? { run: f.check, kind: "test" } : { run: f.check.run, kind: f.check.kind ?? "test" },
1001
1090
  checks: f.checks ?? null,
1002
1091
  builders: f.builders ?? null,
1092
+ tiers: (f.tiers ?? []).map((t) => ({ tier: t.tier, agent: t.agent, kinds: t.kinds === undefined ? null : [...t.kinds], session: t.session ?? null })),
1003
1093
  publish:
1004
1094
  f.publish === undefined
1005
1095
  ? null
@@ -1013,6 +1103,28 @@ function boxOf(raw: unknown, pointer: string): BoxDeclaration | null {
1013
1103
  pointer: `${pointer}/factory`,
1014
1104
  },
1015
1105
  listing: l === undefined ? null : { published: l.published ?? true, title: l.title ?? "", line: l.line ?? "", cover: l.cover ?? null },
1106
+ publisher: b.publisher ?? null,
1107
+ ship:
1108
+ b.ship === undefined
1109
+ ? null
1110
+ : {
1111
+ op: b.ship.op,
1112
+ gate: b.ship.gate ?? DEFAULT_SHIP_GATE,
1113
+ env: b.ship.env ?? DEFAULT_SHIP_ENV,
1114
+ bookkeeping: (b.ship.bookkeeping ?? []).map((p) => p.replace(/^\.\//, "").replace(/\/+$/, "")),
1115
+ pointer: `${pointer}/ship`,
1116
+ },
1117
+ replicate:
1118
+ r === undefined
1119
+ ? null
1120
+ : {
1121
+ remote: r.remote ?? DEFAULT_REPLICATE_REMOTE,
1122
+ // In the order the closed lists give, so a reader compares them without sorting.
1123
+ refs: REPLICATE_REF_CLASSES.filter((c) => (r.refs ?? REPLICATE_REF_CLASSES).includes(c)),
1124
+ on: REPLICATE_TRIGGERS.filter((t) => (r.on ?? REPLICATE_TRIGGERS).includes(t)),
1125
+ every: r.every ?? null,
1126
+ pointer: `${pointer}/replicate`,
1127
+ },
1016
1128
  pointer,
1017
1129
  };
1018
1130
  }
@@ -1040,6 +1152,35 @@ function factoryProblem(members: readonly Member[]): { message: string; pointer:
1040
1152
  if (factory.builders !== null && !members.some((m) => m.name === factory.builders)) {
1041
1153
  return { message: `the factory's builders names ${JSON.stringify(factory.builders)}, which is not a declared member; declared members: ${known()}`, pointer: `${factory.pointer}/builders` };
1042
1154
  }
1155
+ // Builder tiers (#3152): their agents are the builders member's, and one entry answers each tier and kind.
1156
+ if (factory.tiers.length > 0 && factory.builders === null) {
1157
+ return { message: "the factory declares tiers and no builders; tiers name agents the builders member declares, so name it", pointer: `${factory.pointer}/tiers` };
1158
+ }
1159
+ const answered = new Map<string, number>();
1160
+ for (const [i, t] of factory.tiers.entries()) {
1161
+ for (const kind of t.kinds ?? [null]) {
1162
+ const key = `${t.tier}\0${kind ?? ""}`;
1163
+ const first = answered.get(key);
1164
+ if (first !== undefined) {
1165
+ const which = kind === null ? "with no kinds" : `for kind ${kind}`;
1166
+ return { message: `tiers/${first} and tiers/${i} both name a builder at tier ${t.tier} ${which}; a tier has one builder per kind, so one entry answers it`, pointer: `${factory.pointer}/tiers/${i}` };
1167
+ }
1168
+ answered.set(key, i);
1169
+ }
1170
+ }
1171
+ return null;
1172
+ }
1173
+
1174
+ /** The rule the schema can't say about the factory's tiers once the sessions are read (#3152): a session is a declared agent session. */
1175
+ function factoryTierSessionProblem(members: readonly Member[], agents: readonly AgentDeclaration[]): { message: string; pointer: string } | null {
1176
+ const factory = members.find((m) => m.box?.factory)?.box?.factory;
1177
+ if (!factory) return null;
1178
+ for (const [i, t] of factory.tiers.entries()) {
1179
+ if (t.session !== null && !agents.some((a) => a.name === t.session)) {
1180
+ const known = agents.map((a) => a.name).join(", ") || "none";
1181
+ return { message: `tiers/${i} names the session ${JSON.stringify(t.session)}, which is not a declared agent session; declared sessions: ${known}`, pointer: `${factory.pointer}/tiers/${i}/session` };
1182
+ }
1183
+ }
1043
1184
  return null;
1044
1185
  }
1045
1186
 
@@ -15,8 +15,13 @@
15
15
  * (`../cli/handlers/run.ts`), when neither the config nor the ledger names
16
16
  * it.
17
17
  *
18
- * The component contract declares no environments, so every component of a
19
- * member lists the member's environments.
18
+ * A component may also declare environments of its own (#3153, ws-095),
19
+ * each with the site a release goes to there: the runtime that hosts it, its
20
+ * URL, a custom domain and the git remote its lifecycle records are pushed
21
+ * to. Each declared environment the member already lists carries that site,
22
+ * and one it doesn't is added with source `component` when `chant run --env`
23
+ * would take it, or left out with `environments-component-undeclared` when
24
+ * the config's environments don't cover it.
20
25
  *
21
26
  * `chant run --env` refuses a name the config's `environments` doesn't cover
22
27
  * whenever it declares any (`unknownEnvError` in `../env.ts`), so a ledger
@@ -31,7 +36,8 @@
31
36
  */
32
37
 
33
38
  import { execFileSync } from "node:child_process";
34
- import { environmentName, isEnvironmentPattern, matchesDeclaredEnvironment } from "../config";
39
+ import { environmentName, isEnvironmentPattern, matchesDeclaredEnvironment, type EnvironmentDeclaration } from "../config";
40
+ import type { ComponentEnvironmentDeclaration } from "../components/component";
35
41
  import type { ReasonCode } from "./reason-codes";
36
42
  import { LOCAL_RUNTIME, type MemberRuntimes } from "./runtimes";
37
43
  import { ENV_PATTERN, LIFECYCLE_REF, MEMBERS_DIR } from "./status";
@@ -49,6 +55,8 @@ export const ENVIRONMENT_REASON_CODES = [
49
55
  "environments-ledger-undeclared",
50
56
  /** The chant/lifecycle branch exists and the member's ledger directories could not be listed. */
51
57
  "environments-ledger-unreadable",
58
+ /** A component declares an environment the config's environments don't cover, so chant run --env would refuse it (#3153). */
59
+ "environments-component-undeclared",
52
60
  ] as const satisfies readonly ReasonCode[];
53
61
  export type EnvironmentReasonCode = (typeof ENVIRONMENT_REASON_CODES)[number];
54
62
 
@@ -63,12 +71,22 @@ export interface ComponentEnvironment {
63
71
  name: string;
64
72
  /** True for the environment `chant run --components` deploys to without `--env`. */
65
73
  default: boolean;
66
- /** Where the name came from; the first of config, ledger and builtin that names it. */
67
- source: "config" | "ledger" | "builtin";
68
- /** The command that deploys the component there on the default runtime, run in the member's directory. */
74
+ /** Where the name came from; the first of config, ledger, builtin and the component's own environments that names it. */
75
+ source: "config" | "ledger" | "builtin" | "component";
76
+ /** Where a release of the component goes in this environment, as the component declares it (#3153), or null when it declares nothing for it. */
77
+ site: ComponentSite | null;
78
+ /** The command that deploys the component there, on the site's runtime when the member hosts it and else the default runtime, run in the member's directory. */
69
79
  command: string;
70
80
  }
71
81
 
82
+ /** A component's declared site in one environment (#3153): every field present, null when not declared. */
83
+ export interface ComponentSite {
84
+ runtime: string | null;
85
+ url: string | null;
86
+ domain: string | null;
87
+ lifecycle: string | null;
88
+ }
89
+
72
90
  /** The environments a member's ledger has, or why they couldn't be listed. */
73
91
  export interface LedgerEnvironments {
74
92
  envs: string[];
@@ -80,9 +98,11 @@ export type LedgerEnvironmentReader = (member: { name: string; dir: string }, cw
80
98
 
81
99
  /** What one member's config and ledger say about environments. */
82
100
  export interface MemberEnvironments {
83
- /** Each environment in list order, without the per-component command. */
84
- environments: Omit<ComponentEnvironment, "command">[];
101
+ /** Each environment in list order, without the per-component site and command. */
102
+ environments: Omit<ComponentEnvironment, "command" | "site">[];
85
103
  reasons: EnvironmentReason[];
104
+ /** The config's environments, for whether `chant run --env` takes a name: null when the config couldn't be read, `[]` when it declares none. */
105
+ declared?: EnvironmentDeclaration[] | null;
86
106
  }
87
107
 
88
108
  function git(cwd: string, args: string[], input?: string): string {
@@ -154,12 +174,44 @@ export function memberEnvironments(runtimes: MemberRuntimes | undefined, ledger:
154
174
  add(LOCAL_ENVIRONMENT, literals.includes(LOCAL_ENVIRONMENT) ? "config" : ledgerEnvs.includes(LOCAL_ENVIRONMENT) ? "ledger" : "builtin");
155
175
  for (const name of literals) add(name, "config");
156
176
  for (const name of ledgerEnvs) if (covered(name)) add(name, "ledger");
157
- return { environments: out, reasons };
177
+ return { environments: out, reasons, declared };
158
178
  }
159
179
 
160
- /** The environments one component may deploy to, each with its command line on the member's default runtime. */
161
- export function componentEnvironments(component: string, member: MemberEnvironments | undefined, runtime = LOCAL_RUNTIME): ComponentEnvironment[] {
162
- const list = member?.environments ?? [{ name: LOCAL_ENVIRONMENT, default: true, source: "builtin" as const }];
163
- const on = runtime === LOCAL_RUNTIME ? "" : ` --on ${runtime}`;
164
- return list.map((e) => ({ ...e, command: `chant run --components ${component}${on}${e.default ? "" : ` --env ${e.name}`}` }));
180
+ /**
181
+ * The environments one component may deploy to, each with its command line.
182
+ * `declared` is what the component's contract says (#3153): an environment
183
+ * the member lists takes its site, and one it doesn't is added after the
184
+ * member's when `chant run --env` takes it. `reasons` collects the declared
185
+ * environments left out because the config's environments don't cover them.
186
+ * `hosts` are the runtimes the member configures; a site's runtime is used
187
+ * for the command only when it is one of them.
188
+ */
189
+ export function componentEnvironments(
190
+ component: string,
191
+ member: MemberEnvironments | undefined,
192
+ runtime = LOCAL_RUNTIME,
193
+ declared: readonly ComponentEnvironmentDeclaration[] = [],
194
+ hosts: readonly string[] = [],
195
+ reasons: EnvironmentReason[] = [],
196
+ ): ComponentEnvironment[] {
197
+ const list: Omit<ComponentEnvironment, "command" | "site">[] = [...(member?.environments ?? [{ name: LOCAL_ENVIRONMENT, default: true, source: "builtin" as const }])];
198
+ const config = member?.declared;
199
+ const takes = (env: string): boolean => !config || config.length === 0 || matchesDeclaredEnvironment(config, env);
200
+ const sites = new Map<string, ComponentSite>();
201
+ for (const d of declared) {
202
+ sites.set(d.name, { runtime: d.runtime ?? null, url: d.url ?? null, domain: d.domain ?? null, lifecycle: d.lifecycle ?? null });
203
+ if (list.some((e) => e.name === d.name)) continue;
204
+ if (takes(d.name)) list.push({ name: d.name, default: false, source: "component" });
205
+ else {
206
+ reasons.push({
207
+ code: "environments-component-undeclared",
208
+ message: `component ${component} declares environment ${d.name}, which chant.config.ts's environments don't cover, so chant run --env refuses it`,
209
+ });
210
+ }
211
+ }
212
+ return list.map((e) => {
213
+ const site = sites.get(e.name) ?? null;
214
+ const on = site?.runtime && (site.runtime === LOCAL_RUNTIME || hosts.includes(site.runtime)) ? site.runtime : runtime;
215
+ return { ...e, site, command: `chant run --components ${component}${on === LOCAL_RUNTIME ? "" : ` --on ${on}`}${e.default ? "" : ` --env ${e.name}`}` };
216
+ });
165
217
  }