@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
@@ -227,7 +227,8 @@
227
227
  "runtimes-config-unreadable",
228
228
  "environments-none-declared",
229
229
  "environments-ledger-undeclared",
230
- "environments-ledger-unreadable"
230
+ "environments-ledger-unreadable",
231
+ "environments-component-undeclared"
231
232
  ]
232
233
  },
233
234
  "message": {
@@ -428,7 +429,7 @@
428
429
  }
429
430
  },
430
431
  "environments": {
431
- "description": "The environments the component may deploy to with chant run --components --env in its member: local first, the default, then each name the member's chant.config.ts declares in environments (patterns left out), in config order, then each environment with a release ledger for the member on chant/lifecycle that the config covers, sorted. The component contract declares no environments.",
432
+ "description": "The environments the component may deploy to with chant run --components --env in its member: local first, the default, then each name the member's chant.config.ts declares in environments (patterns left out), in config order, then each environment with a release ledger for the member on chant/lifecycle that the config covers, sorted, then each environment only the component's own environments declare (#3153) that the config covers, in contract order. An environment the component declares carries its site.",
432
433
  "type": "array",
433
434
  "minItems": 1,
434
435
  "items": {
@@ -569,6 +570,7 @@
569
570
  "name",
570
571
  "default",
571
572
  "source",
573
+ "site",
572
574
  "command"
573
575
  ],
574
576
  "properties": {
@@ -584,9 +586,26 @@
584
586
  "enum": [
585
587
  "config",
586
588
  "ledger",
587
- "builtin"
589
+ "builtin",
590
+ "component"
588
591
  ],
589
- "description": "Where the name came from, the first of these that names it: config for the member's chant.config.ts environments, ledger for a release ledger on chant/lifecycle, builtin for local when neither names it."
592
+ "description": "Where the name came from, the first of these that names it: config for the member's chant.config.ts environments, ledger for a release ledger on chant/lifecycle, builtin for local when neither names it, and component for an environment only the component's own environments declare (#3153, added by chant 0.103.0)."
593
+ },
594
+ "site": {
595
+ "description": "Where a release of the component goes in this environment, as the component's environments declare it (#3153): the runtime that hosts it, the URL it answers on, a custom domain and the git remote its lifecycle records are pushed to, each null when not declared. Null when the component declares nothing for this environment. Added by chant 0.103.0.",
596
+ "oneOf": [
597
+ { "type": "null" },
598
+ {
599
+ "type": "object",
600
+ "required": ["runtime", "url", "domain", "lifecycle"],
601
+ "properties": {
602
+ "runtime": { "type": ["string", "null"] },
603
+ "url": { "type": ["string", "null"] },
604
+ "domain": { "type": ["string", "null"] },
605
+ "lifecycle": { "type": ["string", "null"] }
606
+ }
607
+ }
608
+ ]
590
609
  },
591
610
  "command": {
592
611
  "type": "string",
@@ -61,7 +61,7 @@ const componentIr = (components: { name: string; composites?: string[]; archetyp
61
61
  const local = (name: string) => ({ name: "local", lexicon: null, default: true, command: `chant run --components ${name}` });
62
62
 
63
63
  /** The default environment, named by nothing but chant itself. */
64
- const localEnv = (name: string) => ({ name: "local", default: true, source: "builtin", command: `chant run --components ${name}` });
64
+ const localEnv = (name: string) => ({ name: "local", default: true, source: "builtin", site: null, command: `chant run --components ${name}` });
65
65
 
66
66
  /**
67
67
  * app declares three instances and exports ImageUri; delivery links to it and
@@ -381,11 +381,11 @@ describe("the environments each component may deploy to (#2695)", () => {
381
381
  const g = result((await workspaceComposites({ cwd: declared() })).doc);
382
382
  expectValid(g);
383
383
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments).toEqual([
384
- { name: "local", default: true, source: "builtin", command: "chant run --components queue-runner" },
385
- { name: "staging", default: false, source: "config", command: "chant run --components queue-runner --env staging" },
386
- { name: "prod", default: false, source: "config", command: "chant run --components queue-runner --env prod" },
384
+ { name: "local", default: true, source: "builtin", site: null, command: "chant run --components queue-runner" },
385
+ { name: "staging", default: false, source: "config", site: null, command: "chant run --components queue-runner --env staging" },
386
+ { name: "prod", default: false, source: "config", site: null, command: "chant run --components queue-runner --env prod" },
387
387
  // The pattern pr-* is no environment by itself, and it covers the ledger's pr-42.
388
- { name: "pr-42", default: false, source: "ledger", command: "chant run --components queue-runner --env pr-42" },
388
+ { name: "pr-42", default: false, source: "ledger", site: null, command: "chant run --components queue-runner --env pr-42" },
389
389
  ]);
390
390
  const jobs = g.members.find((m) => m.name === "jobs")!;
391
391
  expect(jobs.environmentReasons).toEqual([{ code: "environments-ledger-undeclared", message: expect.stringContaining("retired") }]);
@@ -397,7 +397,7 @@ describe("the environments each component may deploy to (#2695)", () => {
397
397
  // delivery has no _members/delivery, so the flat staging ledger is its; its config declares nothing, so anything goes.
398
398
  expect(g.components.find((c) => c.id === "delivery/edge")!.environments).toEqual([
399
399
  localEnv("edge"),
400
- { name: "staging", default: false, source: "ledger", command: "chant run --components edge --env staging" },
400
+ { name: "staging", default: false, source: "ledger", site: null, command: "chant run --components edge --env staging" },
401
401
  ]);
402
402
  expect(g.members.find((m) => m.name === "delivery")!.environmentReasons.map((r) => r.code)).toEqual(["environments-none-declared"]);
403
403
  expect(g.members.find((m) => m.name === "docs")!.environmentReasons).toEqual([]);
@@ -409,8 +409,8 @@ describe("the environments each component may deploy to (#2695)", () => {
409
409
  const g = result((await workspaceComposites({ cwd: root })).doc);
410
410
  expectValid(g);
411
411
  expect(g.components.find((c) => c.id === "jobs/queue-runner")!.environments).toEqual([
412
- { name: "local", default: true, source: "config", command: "chant run --components queue-runner" },
413
- { name: "prod", default: false, source: "config", command: "chant run --components queue-runner --env prod" },
412
+ { name: "local", default: true, source: "config", site: null, command: "chant run --components queue-runner" },
413
+ { name: "prod", default: false, source: "config", site: null, command: "chant run --components queue-runner --env prod" },
414
414
  ]);
415
415
  expect(g.members.find((m) => m.name === "jobs")!.environmentReasons).toEqual([]);
416
416
  });
@@ -49,6 +49,7 @@ import type { LinkRow } from "./links";
49
49
  import { emitDocument, type UnitResult } from "./member-commands";
50
50
  import { readerVersion, WORKSPACE_ERROR_CODES, type ErrorLocation, type WorkspaceErrorCode } from "./declaration";
51
51
  import type { ReasonCode } from "./reason-codes";
52
+ import type { ComponentEnvironmentDeclaration } from "../components/component";
52
53
  import { componentEnvironments, ENVIRONMENT_REASON_CODES, memberEnvironments, readLedgerEnvironments, type ComponentEnvironment, type EnvironmentReason, type LedgerEnvironmentReader, type MemberEnvironments } from "./environments";
53
54
  import { componentRuntimes, readRuntimesIn, RUNTIME_REASON_CODES, type ComponentRuntime, type MemberRuntimes, type PluginLoader, type RuntimeReason } from "./runtimes";
54
55
 
@@ -109,7 +110,7 @@ export interface ComponentEntry {
109
110
  file: string | null;
110
111
  /** The runtimes it can deploy on: `local` first, then each hosting lexicon its member configures. */
111
112
  runtimes: ComponentRuntime[];
112
- /** The environments it may deploy to: `local` first, then the config's, then the ledger's. */
113
+ /** The environments it may deploy to: `local` first, then the config's, then the ledger's, then the ones only the component declares, each with the site the component declares for it. */
113
114
  environments: ComponentEnvironment[];
114
115
  }
115
116
 
@@ -183,12 +184,22 @@ function readComponents(member: string, dir: string, run: UnitResult, runtimes:
183
184
  composites: composites && composites.length > 0 ? composites : null,
184
185
  file: file ? (dir === "." ? file : `${dir}/${file}`) : null,
185
186
  runtimes: componentRuntimes(n.id, runtimes),
186
- environments: componentEnvironments(n.id, environments, runtimes?.default),
187
+ // The component's own environments (#3153): their sites, and any the member doesn't list; a reason lands on the member.
188
+ environments: componentEnvironments(n.id, environments, runtimes?.default, declaredEnvironments(attrs.environments), runtimes?.lexicons ?? [], environments?.reasons ?? []),
187
189
  });
188
190
  }
189
191
  return { components };
190
192
  }
191
193
 
194
+ /** A component's declared environments from its IR attrs (#3153), keeping only well-formed entries. */
195
+ function declaredEnvironments(raw: unknown): ComponentEnvironmentDeclaration[] {
196
+ if (!Array.isArray(raw)) return [];
197
+ const text = (v: unknown) => (typeof v === "string" && v.length > 0 ? v : undefined);
198
+ return raw
199
+ .filter((e): e is Record<string, unknown> => e !== null && typeof e === "object" && typeof (e as Record<string, unknown>).name === "string")
200
+ .map((e) => ({ name: e.name as string, runtime: text(e.runtime), url: text(e.url), domain: text(e.domain), lifecycle: text(e.lifecycle) }));
201
+ }
202
+
192
203
  /** The composite instances in a composed graph, without their components. */
193
204
  export function compositeInstances(graph: Pick<WorkspaceGraph, "nodes">): CompositeInstanceRow[] {
194
205
  const byId = new Map<string, { member: string; kinds: Set<string>; lexicons: Set<string>; nodes: string[] }>();
@@ -233,12 +233,12 @@
233
233
  "pattern": "^sha256:[0-9a-f]{64}$"
234
234
  },
235
235
  "constrains": {
236
- "description": "What this decision governs: issues (owner/repo#n), other decisions (their ids), workspace members (member:<name>) or files and directories in the workspace (path:<path>, the grammar of an evidence path, #2549). At least one entry: a decision that governs nothing is refused.",
236
+ "description": "What this decision governs: issues (owner/repo#n), other decisions (their ids), workspace members (member:<name>), contracts (contract:<id>, #3148) or files and directories in the workspace (path:<path>, the grammar of an evidence path, #2549). At least one entry: a decision that governs nothing is refused.",
237
237
  "type": "array",
238
238
  "minItems": 1,
239
239
  "items": {
240
240
  "type": "string",
241
- "pattern": "^([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+#[0-9]+|[a-z][a-z0-9]{0,15}-[0-9]{3,}|member:[a-z0-9][a-z0-9-]{0,39}|path:(?!/)(?!(?:[^/]*/)*\\.{1,2}(?:/|$))(?!.*//)[^\\\\\\u0000-\\u001f]*[^/\\\\\\u0000-\\u001f])$"
241
+ "pattern": "^([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+#[0-9]+|[a-z][a-z0-9]{0,15}-[0-9]{3,}|member:[a-z0-9][a-z0-9-]{0,39}|contract:[A-Za-z0-9][A-Za-z0-9._-]{0,99}|path:(?!/)(?!(?:[^/]*/)*\\.{1,2}(?:/|$))(?!.*//)[^\\\\\\u0000-\\u001f]*[^/\\\\\\u0000-\\u001f])$"
242
242
  }
243
243
  },
244
244
  "out_of_scope": {
@@ -0,0 +1,207 @@
1
+ /**
2
+ * The reference broker (#3164, ws-097): the smallest broker of the broker
3
+ * protocol (`../broker-protocol.ts`) that passes the conformance suite
4
+ * (`./broker.ts`), serving all four standard capabilities. It keeps every
5
+ * report in memory and buffers each request, which a real broker should not;
6
+ * it exists to show the suite can be passed and what passing takes, and for
7
+ * a test to run a box against without studio's lobby.
8
+ *
9
+ * It is a test fixture and the package does not export it: chant never runs
10
+ * a broker or listens on a port (ws-052, ws-086), so it lives under
11
+ * `__fixtures__`, which test/no-listener.test.ts leaves out like a test file.
12
+ * `startReferenceBroker(env)` has the shape of the suite's `start`.
13
+ */
14
+
15
+ import { randomBytes } from "node:crypto";
16
+ import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
17
+ import type { AddressInfo } from "node:net";
18
+ import {
19
+ BROKER_ROUTES,
20
+ DECLARATION_LIMITS,
21
+ declaredRefusal,
22
+ EGRESS_CAPABILITY,
23
+ FEEDBACK_CAPABILITY,
24
+ FOUNTAIN_CAPABILITY,
25
+ INFERENCE_CAPABILITY,
26
+ parseDeclarationReport,
27
+ formatPayerHeader,
28
+ PAYER_HEADER,
29
+ type BrokerPayer,
30
+ type KeptDeclaration,
31
+ } from "../../broker-protocol";
32
+ import type { BrokerConformanceEnv, StartedBroker } from "../broker";
33
+
34
+ const MAX_BODY = 1024 * 1024;
35
+ /** Request headers that describe the hop or carry the caller's credential. */
36
+ const DROP = new Set(["host", "connection", "keep-alive", "content-length", "transfer-encoding", "authorization", "x-api-key", "cookie", "proxy-authorization", "te", "upgrade", "forwarded", "x-forwarded-for", "x-forwarded-host", "x-forwarded-proto", "x-real-ip"]);
37
+ /** Response headers fetch has already undone. */
38
+ const HOP = new Set(["connection", "keep-alive", "content-length", "content-encoding", "transfer-encoding"]);
39
+
40
+ function send(res: ServerResponse, status: number, value: unknown, extra: Record<string, string> = {}): void {
41
+ res.writeHead(status, { "content-type": "application/json", "cache-control": "no-store", ...extra });
42
+ res.end(JSON.stringify(value));
43
+ }
44
+
45
+ const refuse = (res: ServerResponse, status: number, message: string) => send(res, status, { error: { type: status === 403 ? "permission_error" : "invalid_request", message } });
46
+
47
+ async function readBody(req: IncomingMessage): Promise<string | null> {
48
+ let raw = "";
49
+ for await (const chunk of req) {
50
+ raw += chunk;
51
+ if (raw.length > MAX_BODY) return null;
52
+ }
53
+ return raw;
54
+ }
55
+
56
+ /** The headers to forward, without the box's credential, plus `extra`. */
57
+ function forwarded(req: IncomingMessage, extra: Record<string, string>): Record<string, string> {
58
+ const headers: Record<string, string> = {};
59
+ for (const [k, v] of Object.entries(req.headers)) if (!DROP.has(k) && v !== undefined) headers[k] = [v].flat().join(", ");
60
+ return { ...headers, ...extra };
61
+ }
62
+
63
+ async function relay(res: ServerResponse, answer: Response, swap?: [string, string], extra: Record<string, string> = {}): Promise<void> {
64
+ const headers: Record<string, string> = {};
65
+ answer.headers.forEach((v, k) => {
66
+ if (!HOP.has(k)) headers[k] = swap ? v.split(swap[0]).join(swap[1]) : v;
67
+ });
68
+ Object.assign(headers, extra);
69
+ res.writeHead(answer.status, headers);
70
+ res.end(Buffer.from(await answer.arrayBuffer()));
71
+ }
72
+
73
+ /** Start the reference broker for `env`, with a fresh token per box. */
74
+ export async function startReferenceBroker(env: BrokerConformanceEnv): Promise<StartedBroker> {
75
+ const tokens: Record<string, string> = Object.fromEntries(env.boxes.map((b) => [b, `box_${randomBytes(32).toString("hex")}`]));
76
+ const boxOf = new Map(Object.entries(tokens).map(([box, token]) => [token, box]));
77
+ const reports = new Map<string, KeptDeclaration>();
78
+ const anthropic = new URL(env.anthropic.url);
79
+ // It spends the operator's one credential for every box, so the payer is shared (#3474), said on the report's answer and on each inference and decide answer.
80
+ const payer: BrokerPayer = { kind: "shared", principal: null };
81
+ const paid = { [PAYER_HEADER]: formatPayerHeader(payer) };
82
+ const fountain = new URL(env.fountain.url);
83
+
84
+ const tokenOf = (req: IncomingMessage): string | undefined => {
85
+ const bearer = /^Bearer\s+(\S+)$/i.exec(String(req.headers.authorization ?? ""))?.[1];
86
+ return bearer ?? (typeof req.headers["x-api-key"] === "string" ? req.headers["x-api-key"] : undefined);
87
+ };
88
+
89
+ const server = createServer(async (req, res) => {
90
+ try {
91
+ const url = new URL(req.url ?? "/", "http://broker");
92
+ const path = url.pathname;
93
+ const method = req.method ?? "GET";
94
+ const token = tokenOf(req);
95
+ const box = token ? boxOf.get(token) : undefined;
96
+ const raw = await readBody(req);
97
+ if (raw === null) return refuse(res, 413, "The request is larger than 1 MB.");
98
+
99
+ if (path === BROKER_ROUTES.declaration) {
100
+ if (method !== "POST") return refuse(res, 405, `POST ${BROKER_ROUTES.declaration}`);
101
+ if (!box) return refuse(res, 403, "This token is no box this broker knows.");
102
+ if (raw.length > DECLARATION_LIMITS.bytes) return refuse(res, 413, "The declaration is larger than 32 KB.");
103
+ let body: unknown;
104
+ try {
105
+ body = JSON.parse(raw);
106
+ } catch {
107
+ return refuse(res, 400, "The body is not JSON.");
108
+ }
109
+ const parsed = parseDeclarationReport(body, env.broker);
110
+ if ("error" in parsed) return refuse(res, 400, parsed.error);
111
+ const kept: KeptDeclaration = { capabilities: parsed.capabilities, at: new Date().toISOString() };
112
+ reports.set(box, kept);
113
+ return send(res, 200, { ...kept, payer });
114
+ }
115
+
116
+ let body: unknown;
117
+ try {
118
+ body = raw ? JSON.parse(raw) : undefined;
119
+ } catch {
120
+ body = undefined;
121
+ }
122
+ const request = { method, path, body };
123
+ for (const spec of [INFERENCE_CAPABILITY, EGRESS_CAPABILITY, FEEDBACK_CAPABILITY, FOUNTAIN_CAPABILITY]) {
124
+ if (!spec.routes.some((r) => path === r || path.startsWith(`${r}/`))) continue;
125
+ const words = spec.scopes(request);
126
+ if (words === null) return refuse(res, 404, `This broker does not serve ${method} ${path}.`);
127
+ if (words.length === 0 && spec !== FEEDBACK_CAPABILITY) return send(res, 200, {});
128
+ if (!box) return refuse(res, spec === INFERENCE_CAPABILITY && path === BROKER_ROUTES.decide ? 401 : 403, "This request carried no token of a box this broker knows.");
129
+ if (words.length === 0) return refuse(res, 400, "A batch carries entries (scope agent), counts (scope passive), or both.");
130
+ for (const word of words) {
131
+ const refused = declaredRefusal(reports.get(box), env.broker, spec.name, word);
132
+ if (refused) return refuse(res, 403, refused);
133
+ }
134
+
135
+ if (spec === INFERENCE_CAPABILITY && path === BROKER_ROUTES.decide) return decide(res, body);
136
+ if (spec === INFERENCE_CAPABILITY) {
137
+ const rest = path.slice(BROKER_ROUTES.inference.length);
138
+ const answer = await fetch(new URL(`${rest}${url.search}`, anthropic), { method, headers: forwarded(req, { "x-api-key": env.anthropic.credential }), body: raw || undefined });
139
+ return relay(res, answer, undefined, paid);
140
+ }
141
+ if (spec === EGRESS_CAPABILITY) {
142
+ const [name] = words;
143
+ const secret = env.secrets.find((s) => s.box === box && s.name === name);
144
+ if (!secret) return refuse(res, 403, `This broker holds no value for ${name}.`);
145
+ const rest = path.slice(BROKER_ROUTES.egress.length + 1 + name.length) || "/";
146
+ // The box's token stands in for the value wherever the API expects its key, so it is swapped in every header.
147
+ const headers: Record<string, string> = {};
148
+ for (const [k, v] of Object.entries(req.headers)) if (v !== undefined && !HOP.has(k) && k !== "host") headers[k] = [v].flat().join(", ").split(token!).join(secret.value);
149
+ const search = url.search.split(token!).join(secret.value);
150
+ const answer = await fetch(new URL(`${rest}${search}`, secret.host), { method, headers, body: raw || undefined });
151
+ return relay(res, answer, [secret.value, token!]);
152
+ }
153
+ if (spec === FEEDBACK_CAPABILITY) return send(res, 202, { taken: true });
154
+ if (spec === FOUNTAIN_CAPABILITY) {
155
+ const rest = path.slice(BROKER_ROUTES.fountain.length);
156
+ const answer = await fetch(new URL(`${rest}${url.search}`, fountain), { method, headers: forwarded(req, { authorization: `Bearer ${env.fountain.credential}` }), body: raw || undefined });
157
+ return relay(res, answer);
158
+ }
159
+ }
160
+ if (path === BROKER_ROUTES.feedback) return refuse(res, 400, "A batch carries entries (scope agent), counts (scope passive), or both.");
161
+ return refuse(res, 404, "not found");
162
+ } catch (err) {
163
+ if (!res.headersSent) refuse(res, 502, `The broker could not answer: ${err instanceof Error ? err.message : String(err)}`);
164
+ }
165
+ });
166
+
167
+ /** One Messages call per question, asking for JSON in a schema made from it, as studio's lobby does. */
168
+ async function decide(res: ServerResponse, body: unknown): Promise<void> {
169
+ const b = body as { model?: unknown; state?: unknown; questions?: Record<string, { type?: string; instructions?: string; criteria?: Record<string, string> }> } | undefined;
170
+ if (!b || typeof b.model !== "string" || b.state === undefined || !b.questions || typeof b.questions !== "object" || Object.keys(b.questions).length === 0) {
171
+ return refuse(res, 422, "The body is { model, state, questions } with at least one question.");
172
+ }
173
+ const answers: Record<string, unknown> = {};
174
+ for (const [name, q] of Object.entries(b.questions)) {
175
+ if (q.type !== "choice" && q.type !== "noul") {
176
+ answers[name] = { type: "unsupported" };
177
+ continue;
178
+ }
179
+ const options = q.type === "noul" ? ["true", "false"] : Object.keys(q.criteria ?? {});
180
+ const schema = {
181
+ type: "object",
182
+ properties: { reason: { type: "string" }, choice: { type: "string", enum: options }, probabilities: { type: "object", properties: Object.fromEntries(options.map((o) => [o, { type: "number" }])) } },
183
+ required: ["reason", "choice", "probabilities"],
184
+ };
185
+ const asked = await fetch(new URL("/v1/messages", anthropic), {
186
+ method: "POST",
187
+ headers: { "content-type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": env.anthropic.credential },
188
+ body: JSON.stringify({ model: b.model, max_tokens: 1024, messages: [{ role: "user", content: `${name}: ${q.instructions}\n${JSON.stringify(b.state)}` }], output_config: { format: { type: "json_schema", schema } } }),
189
+ });
190
+ if (!asked.ok) return refuse(res, 502, `The model answered ${asked.status}.`);
191
+ const message = (await asked.json()) as { content?: { type: string; text?: string }[] };
192
+ const out = JSON.parse(message.content?.find((c) => c.type === "text")?.text ?? "{}") as { reason?: string; choice?: string; probabilities?: Record<string, number> };
193
+ const total = options.reduce((t, o) => t + Number(out.probabilities?.[o] ?? 0), 0) || 1;
194
+ const p = Object.fromEntries(options.map((o) => [o, Number(out.probabilities?.[o] ?? 0) / total]));
195
+ const reason = typeof out.reason === "string" ? { reason: out.reason } : {};
196
+ answers[name] = q.type === "noul" ? { type: "noul", noul: p.true, ...reason } : { type: "choice", choice: out.choice, probabilities: p, confidence: p[out.choice ?? ""] ?? 0, ...reason };
197
+ }
198
+ send(res, 200, { model: b.model, answers }, paid);
199
+ }
200
+
201
+ await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", () => resolve()));
202
+ return {
203
+ url: `http://127.0.0.1:${(server.address() as AddressInfo).port}`,
204
+ tokens,
205
+ close: () => new Promise<void>((resolve) => server.close(() => resolve())),
206
+ };
207
+ }
@@ -3,14 +3,19 @@
3
3
  //
4
4
  // chant workspace points --open --json
5
5
  // chant workspace points ask slice-tier --inputs inputs.json --subject W-002
6
- // chant workspace points answer <id> --answer medium --by <name>
6
+ // chant workspace points answer <id> --answer medium --by <name> --note <text>
7
+ // chant workspace points retract <id> --by <name> --note <why>
8
+ // chant workspace points ask agent-question --inputs '{"ask.id":"req-1"}' --candidates question.json
7
9
  //
8
10
  // The points are declared in ../decisions/points.json: typed questions, the
9
11
  // read-contract outputs they read, and a table, model and quorum chain. Each
10
12
  // answer is one record here, named for the point and its inputs' hash, so the
11
13
  // same question is answered once. A table's answer is answered, a model's is
12
14
  // proposed until a person confirms it, and a question nobody before the
13
- // quorum answered is escalated to people. answer.schema.json is a copy of the
15
+ // quorum answered is escalated to people. An answer never changes in place:
16
+ // retracting it escalates the question again and keeps the answer in the
17
+ // record's retractions (#3351). An ad-hoc point's question and candidates
18
+ // come with each ask and stay in the record as asked (#3403). answer.schema.json is a copy of the
14
19
  // one @intentius/chant ships as workspace/point-answer.schema.json.
15
20
  export const recordKind = {
16
21
  name: "answer",
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "urn:intentius:chant:point-answer:1",
4
4
  "title": "Decision point answer front matter",
5
- "description": "The front matter of one answer to a decision point (ws-058, #2739), as `chant workspace points ask` and `points answer` write it into the records directory of a record kind with an `answers` block. One record per point, declaration and inputs: its id is the point's name and the first 12 hex digits of the inputs hash, so the same question is answered once. A table's answer is `answered`; a model's answer at or above its threshold is `proposed` until a person confirms it; a question no decider before the quorum answered is `escalated`, open for people. A workspace copies this file next to its answer kind, as reference-workspace/answers/ does. Values stay within what YAML and JSON share. Fields other than these must start with x-.",
5
+ "description": "The front matter of one answer to a decision point (ws-058, #2739), as `chant workspace points ask` and `points answer` write it into the records directory of a record kind with an `answers` block. One record per point, declaration and inputs: its id is the point's name and the first 12 hex digits of the inputs hash, so the same question is answered once. A table's answer is `answered`; a model's answer at or above its threshold is `proposed` until a person confirms it; a question no decider before the quorum answered is `escalated`, open for people. An answer never changes in place: `points retract` escalates the question to people again and moves the answer into `retractions` (#3351, ws-084). An ad-hoc point's answer keeps the question and candidates its ask gave as `asked`, and they are part of the inputs hash (#3403). A workspace copies this file next to its answer kind, as reference-workspace/answers/ does. Values stay within what YAML and JSON share. Fields other than these must start with x-.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "patternProperties": { "^x-": {} },
@@ -14,6 +14,21 @@
14
14
  "point_version": { "$ref": "#/definitions/sha256", "description": "sha256 of the point's declaration when it was asked. Editing the question or any decider changes it." },
15
15
  "question_type": { "enum": ["noul", "choice", "score"] },
16
16
  "candidates": { "type": "array", "minItems": 2, "items": { "type": ["string", "boolean"] }, "description": "The answers the question allowed: true and false, a choice's options, or a score's levels." },
17
+ "asked": {
18
+ "type": "object",
19
+ "description": "An ad-hoc point's question as its ask gave it (`points ask --candidates`, #3403): its text, and its criteria in the point's question type's shape, from which candidates come. People's answer is one of them.",
20
+ "required": ["question", "criteria"],
21
+ "properties": {
22
+ "question": { "type": "string", "minLength": 1 },
23
+ "criteria": {
24
+ "oneOf": [
25
+ { "type": "object", "minProperties": 2, "maxProperties": 255, "additionalProperties": { "type": "string" } },
26
+ { "type": "array", "minItems": 2, "maxItems": 10, "uniqueItems": true, "items": { "type": "string", "minLength": 1 } }
27
+ ]
28
+ }
29
+ },
30
+ "additionalProperties": false
31
+ },
17
32
  "inputs": { "type": "object", "description": "The state the deciders saw, input name to value." },
18
33
  "inputs_hash": { "$ref": "#/definitions/sha256", "description": "sha256 of the point, its version and its inputs. The same hash is answered once." },
19
34
  "constrains": { "type": "array", "items": { "type": "string", "minLength": 1 }, "description": "What the question is about, such as a work item's id." },
@@ -36,12 +51,20 @@
36
51
  "probabilities": { "$ref": "#/definitions/probabilities" },
37
52
  "confidence": { "$ref": "#/definitions/unit" },
38
53
  "threshold": { "$ref": "#/definitions/unit" },
54
+ "reason": { "type": "string", "minLength": 1, "description": "model: the model's own explanation of its answer, when the backend gave one (#3345). Kept when a person confirms the proposal; moved into the escalations as model_reason when people answer otherwise." },
39
55
  "escalations": {
40
56
  "type": "array",
41
57
  "description": "The deciders asked before the one that answered, or before the quorum, and why each did not answer.",
42
58
  "items": { "$ref": "#/definitions/escalation" }
43
59
  },
44
60
  "answered_by": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "description": "The people who answered or confirmed it." },
61
+ "note": { "type": "string", "minLength": 1, "description": "What the people who answered wrote with their answer (`points answer --note`, #3351). Moved into the retraction when the answer is retracted." },
62
+ "relayed_by": { "type": "string", "minLength": 1, "description": "Who carried the answer to chant for the people in answered_by (`points answer --relayed-by`, #3402), such as a follower relaying a person's answer through hud. It does not count toward the quorum. Moved into the retraction as answer_relayed_by when the answer is retracted." },
63
+ "retractions": {
64
+ "type": "array",
65
+ "description": "Answers people took back, oldest first (`points retract`, #3351): each answer as it was, and who retracted it, when and why. A retracted question is escalated to people again.",
66
+ "items": { "$ref": "#/definitions/retraction" }
67
+ },
45
68
  "asked_on": { "$ref": "#/definitions/date" },
46
69
  "answered_on": { "$ref": "#/definitions/date" },
47
70
  "source": {
@@ -76,6 +99,22 @@
76
99
  "unit": { "type": "number", "minimum": 0, "maximum": 1 },
77
100
  "date": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
78
101
  "probabilities": { "type": "object", "additionalProperties": { "$ref": "#/definitions/unit" } },
102
+ "retraction": {
103
+ "type": "object",
104
+ "required": ["answer", "decider", "by", "on"],
105
+ "properties": {
106
+ "answer": { "type": ["string", "boolean"], "description": "The answer retracted." },
107
+ "decider": { "type": "object", "required": ["kind"], "properties": { "kind": { "enum": ["table", "model", "quorum"] } }, "description": "Who gave it: the record's decider as it was." },
108
+ "answered_by": { "type": "array", "items": { "type": "string", "minLength": 1 } },
109
+ "answered_on": { "$ref": "#/definitions/date" },
110
+ "answer_note": { "type": "string", "minLength": 1, "description": "The note the answer carried." },
111
+ "answer_relayed_by": { "type": "string", "minLength": 1, "description": "Who relayed the answer (#3402)." },
112
+ "by": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "description": "Who retracted it." },
113
+ "on": { "$ref": "#/definitions/date" },
114
+ "note": { "type": "string", "minLength": 1, "description": "Why it was retracted (`points retract --note`)." }
115
+ },
116
+ "additionalProperties": false
117
+ },
79
118
  "escalation": {
80
119
  "type": "object",
81
120
  "required": ["kind", "reason"],
@@ -87,7 +126,8 @@
87
126
  "answer": { "type": ["string", "boolean", "null"] },
88
127
  "probabilities": { "$ref": "#/definitions/probabilities" },
89
128
  "confidence": { "$ref": "#/definitions/unit" },
90
- "threshold": { "$ref": "#/definitions/unit" }
129
+ "threshold": { "$ref": "#/definitions/unit" },
130
+ "model_reason": { "type": "string", "minLength": 1, "description": "model: the model's own explanation of the answer it gave, when the backend gave one (#3345). reason is why the decider did not answer." }
91
131
  },
92
132
  "additionalProperties": false
93
133
  }
@@ -0,0 +1,37 @@
1
+ // The contract record kind (#3148, ws-082), data only: no imports, no code.
2
+ //
3
+ // chant workspace records --kind design/contracts/contract.kind.mjs --current --json
4
+ //
5
+ // A contract is an acceptance criterion made formal: what a person can do,
6
+ // the criteria a check proves, and the check itself, a file pinned by hash.
7
+ // It is drafted, approved once its reviewers agree, and retired when nothing
8
+ // should be built against it any more. Studio's factory builds work items
9
+ // against approved contracts; a work kind links an item to its contract with
10
+ // work.contract (#3147). Each check run's result is an evidence record
11
+ // (../evidence/evidence.kind.mjs) that names the contract and its criteria.
12
+ export const recordKind = {
13
+ name: "contract",
14
+ location: { dir: ".", match: "^C-[0-9]{3,}-.+\\.md$" },
15
+ format: "markdown-front-matter",
16
+ schema: { id: "urn:intentius:chant:contract:1", path: "contract.schema.json" },
17
+ idField: "id",
18
+ stateField: "state",
19
+ states: ["draft", "approved", "retired"],
20
+ // An approved contract changes only in its state, its check pins and its
21
+ // reviews: anything else is a new contract that supersedes it. A retired
22
+ // contract is final and sealed.
23
+ closedStates: ["retired"],
24
+ approval: { draft: 0, approved: 1, retired: 1 },
25
+ seal: { field: "closed_digest" },
26
+ // A bare id or a list of ids, as studio's contracts write it. A link takes
27
+ // effect from an approved or retired contract.
28
+ supersedes: { field: "supersedes" },
29
+ // The contract's checks, each a workspace file pinned by the hash of its
30
+ // bytes (#2549): records reports a check edited after it was pinned.
31
+ pins: { field: "checks" },
32
+ // Verdicts on the contract, given in a review session (whose subjects
33
+ // include this kind) or with records review, and who approved it.
34
+ reviews: { field: "reviews", decider: "approved_by" },
35
+ // records new --by names a draft's proposer here (#2756).
36
+ proposedBy: { field: "proposed_by" },
37
+ };