@intentius/chant 0.102.0 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) 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/workspace/box-cli.d.ts +12 -0
  114. package/dist/workspace/box-cli.d.ts.map +1 -0
  115. package/dist/workspace/box-factory.d.ts +27 -3
  116. package/dist/workspace/box-factory.d.ts.map +1 -1
  117. package/dist/workspace/box-listing.d.ts +85 -0
  118. package/dist/workspace/box-listing.d.ts.map +1 -0
  119. package/dist/workspace/box-publish.d.ts +147 -0
  120. package/dist/workspace/box-publish.d.ts.map +1 -0
  121. package/dist/workspace/broker-protocol.d.ts +190 -0
  122. package/dist/workspace/broker-protocol.d.ts.map +1 -0
  123. package/dist/workspace/checks.d.ts.map +1 -1
  124. package/dist/workspace/composites.d.ts +2 -2
  125. package/dist/workspace/composites.d.ts.map +1 -1
  126. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +17 -0
  127. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -0
  128. package/dist/workspace/conformance/broker.d.ts +202 -0
  129. package/dist/workspace/conformance/broker.d.ts.map +1 -0
  130. package/dist/workspace/conformance/index.d.ts +2 -1
  131. package/dist/workspace/conformance/index.d.ts.map +1 -1
  132. package/dist/workspace/conformance/vitest.d.ts +9 -2
  133. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  134. package/dist/workspace/conformance/writer.d.ts +108 -14
  135. package/dist/workspace/conformance/writer.d.ts.map +1 -1
  136. package/dist/workspace/decide.d.ts +126 -12
  137. package/dist/workspace/decide.d.ts.map +1 -1
  138. package/dist/workspace/decision-points.schema.json +11 -4
  139. package/dist/workspace/declaration.d.ts +65 -0
  140. package/dist/workspace/declaration.d.ts.map +1 -1
  141. package/dist/workspace/declaration.schema.json +153 -4
  142. package/dist/workspace/environments.d.ts +36 -10
  143. package/dist/workspace/environments.d.ts.map +1 -1
  144. package/dist/workspace/factory-rules.d.ts +180 -0
  145. package/dist/workspace/factory-rules.d.ts.map +1 -0
  146. package/dist/workspace/init.d.ts +21 -0
  147. package/dist/workspace/init.d.ts.map +1 -1
  148. package/dist/workspace/intent-cli.d.ts +1 -1
  149. package/dist/workspace/intent-cli.d.ts.map +1 -1
  150. package/dist/workspace/intent-record.d.ts +28 -1
  151. package/dist/workspace/intent-record.d.ts.map +1 -1
  152. package/dist/workspace/intent.d.ts +102 -5
  153. package/dist/workspace/intent.d.ts.map +1 -1
  154. package/dist/workspace/json-edit.d.ts +24 -0
  155. package/dist/workspace/json-edit.d.ts.map +1 -0
  156. package/dist/workspace/jsonc.d.ts +11 -0
  157. package/dist/workspace/jsonc.d.ts.map +1 -1
  158. package/dist/workspace/kinds.d.ts +48 -0
  159. package/dist/workspace/kinds.d.ts.map +1 -1
  160. package/dist/workspace/lexicon-symbols.d.ts +29 -0
  161. package/dist/workspace/lexicon-symbols.d.ts.map +1 -0
  162. package/dist/workspace/lineage-upgrade.d.ts +31 -2
  163. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  164. package/dist/workspace/point-answer.schema.json +42 -2
  165. package/dist/workspace/points-cli.d.ts +10 -3
  166. package/dist/workspace/points-cli.d.ts.map +1 -1
  167. package/dist/workspace/points.d.ts +66 -5
  168. package/dist/workspace/points.d.ts.map +1 -1
  169. package/dist/workspace/reason-codes.d.ts +33 -4
  170. package/dist/workspace/reason-codes.d.ts.map +1 -1
  171. package/dist/workspace/record-sessions.d.ts +5 -7
  172. package/dist/workspace/record-sessions.d.ts.map +1 -1
  173. package/dist/workspace/records-cli.d.ts +7 -0
  174. package/dist/workspace/records-cli.d.ts.map +1 -1
  175. package/dist/workspace/records-close.d.ts +7 -3
  176. package/dist/workspace/records-close.d.ts.map +1 -1
  177. package/dist/workspace/records-write.d.ts +66 -6
  178. package/dist/workspace/records-write.d.ts.map +1 -1
  179. package/dist/workspace/records.d.ts +28 -9
  180. package/dist/workspace/records.d.ts.map +1 -1
  181. package/dist/workspace/runs-cli.d.ts +138 -6
  182. package/dist/workspace/runs-cli.d.ts.map +1 -1
  183. package/dist/workspace/runs.d.ts +132 -11
  184. package/dist/workspace/runs.d.ts.map +1 -1
  185. package/dist/workspace/session-kinds.d.ts +1 -1
  186. package/dist/workspace/session-kinds.d.ts.map +1 -1
  187. package/dist/workspace/squash.d.ts +80 -0
  188. package/dist/workspace/squash.d.ts.map +1 -0
  189. package/dist/workspace/status-gates.d.ts +2 -0
  190. package/dist/workspace/status-gates.d.ts.map +1 -1
  191. package/dist/workspace/status.d.ts +54 -0
  192. package/dist/workspace/status.d.ts.map +1 -1
  193. package/dist/workspace/symbols.d.ts +16 -5
  194. package/dist/workspace/symbols.d.ts.map +1 -1
  195. package/dist/workspace/trust/run-commits.d.ts +46 -0
  196. package/dist/workspace/trust/run-commits.d.ts.map +1 -0
  197. package/dist/workspace/trust/run-statement.d.ts +128 -0
  198. package/dist/workspace/trust/run-statement.d.ts.map +1 -0
  199. package/dist/workspace/trust/seal.d.ts +6 -1
  200. package/dist/workspace/trust/seal.d.ts.map +1 -1
  201. package/dist/workspace/trust/verify-cli.d.ts +1 -1
  202. package/dist/workspace/trust/verify.d.ts +16 -2
  203. package/dist/workspace/trust/verify.d.ts.map +1 -1
  204. package/dist/workspace/wip-cli.d.ts +21 -0
  205. package/dist/workspace/wip-cli.d.ts.map +1 -0
  206. package/dist/workspace/wip.d.ts +268 -0
  207. package/dist/workspace/wip.d.ts.map +1 -0
  208. package/dist/workspace/work-cli.d.ts.map +1 -1
  209. package/dist/workspace/work-evidence.d.ts +1 -1
  210. package/dist/workspace/work-evidence.d.ts.map +1 -1
  211. package/dist/workspace/work-size.d.ts +55 -0
  212. package/dist/workspace/work-size.d.ts.map +1 -0
  213. package/dist/workspace/workspace-kinds.schema.json +122 -0
  214. package/dist/workspace/write-lock-cli.d.ts +60 -0
  215. package/dist/workspace/write-lock-cli.d.ts.map +1 -0
  216. package/dist/workspace/write-lock.d.ts +152 -0
  217. package/dist/workspace/write-lock.d.ts.map +1 -0
  218. package/dist/workspace/write-scope.d.ts +6 -2
  219. package/dist/workspace/write-scope.d.ts.map +1 -1
  220. package/package.json +6 -1
  221. package/src/__fixtures__/change-set/lifecycle-plan.floci.json +59 -0
  222. package/src/__fixtures__/change-set/lifecycle-v1.ts.txt +4 -0
  223. package/src/__fixtures__/change-set/lifecycle-v2.ts.txt +4 -0
  224. package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +95 -0
  225. package/src/__fixtures__/change-set/warden.change-set.golden.json +106 -0
  226. package/src/__fixtures__/change-set/warden.plan.json +44 -0
  227. package/src/__fixtures__/plan-summary/normalization-vectors.json +1593 -0
  228. package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +31 -0
  229. package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +31 -0
  230. package/src/__fixtures__/pr-loop/pr-plan.golden.json +127 -0
  231. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +37 -0
  232. package/src/audit/core.test.ts +15 -0
  233. package/src/audit/core.ts +8 -2
  234. package/src/audit/discover.test.ts +11 -0
  235. package/src/audit/discover.ts +7 -2
  236. package/src/change-set-bundle.test.ts +57 -0
  237. package/src/change-set.test.ts +215 -0
  238. package/src/change-set.ts +445 -0
  239. package/src/cli/commands/audit-opt-in.test.ts +54 -0
  240. package/src/cli/commands/audit.ts +29 -2
  241. package/src/cli/commands/build.test.ts +19 -0
  242. package/src/cli/commands/build.ts +3 -2
  243. package/src/cli/handlers/build.ts +13 -1
  244. package/src/cli/handlers/change-set.test.ts +56 -0
  245. package/src/cli/handlers/change-set.ts +44 -0
  246. package/src/cli/handlers/fan-out.test.ts +221 -2
  247. package/src/cli/handlers/fan-out.ts +165 -75
  248. package/src/cli/handlers/graph.ts +3 -0
  249. package/src/cli/handlers/operator.test.ts +21 -0
  250. package/src/cli/handlers/operator.ts +34 -3
  251. package/src/cli/handlers/pr.ts +373 -0
  252. package/src/cli/handlers/run.ts +1 -0
  253. package/src/cli/main.test.ts +20 -0
  254. package/src/cli/main.ts +185 -23
  255. package/src/cli/mcp/never-over-mcp.test.ts +101 -0
  256. package/src/cli/mcp/op-tools.ts +24 -2
  257. package/src/cli/mcp/workspace-tools.test.ts +1 -1
  258. package/src/cli/mcp/workspace-tools.ts +34 -6
  259. package/src/cli/registry.ts +63 -1
  260. package/src/components/auto-release.ts +1 -1
  261. package/src/components/capability.ts +55 -0
  262. package/src/components/changed-files.ts +66 -0
  263. package/src/components/cli-support.ts +28 -5
  264. package/src/components/component.schema.json +17 -0
  265. package/src/components/component.ts +25 -0
  266. package/src/components/deploy-units.ts +3 -0
  267. package/src/components/driver.test.ts +52 -0
  268. package/src/components/driver.ts +31 -13
  269. package/src/components/fan-out-output.ts +42 -1
  270. package/src/components/fan-out-record.ts +69 -0
  271. package/src/components/fan-out-run.test.ts +17 -0
  272. package/src/components/fan-out-run.ts +304 -1
  273. package/src/components/fan-out-support.ts +3 -0
  274. package/src/components/fan-out-waves.test.ts +375 -0
  275. package/src/components/fan-out.ts +18 -17
  276. package/src/components/gated-wave-pipeline.ts +82 -0
  277. package/src/components/index.ts +1 -0
  278. package/src/components/layers.ts +30 -0
  279. package/src/components/pr-pipeline.ts +91 -0
  280. package/src/components/pr-run.test.ts +205 -0
  281. package/src/components/pr-run.ts +280 -0
  282. package/src/components/promote.ts +5 -0
  283. package/src/components/verbs/shell.test.ts +11 -0
  284. package/src/components/verbs/shell.ts +7 -3
  285. package/src/components/wave-plan.ts +124 -0
  286. package/src/discovery/sandbox/bundle.ts +1 -1
  287. package/src/gated-waves-bundle.test.ts +62 -0
  288. package/src/gated-waves.test.ts +118 -0
  289. package/src/gated-waves.ts +240 -0
  290. package/src/index.ts +1 -0
  291. package/src/lexicon.ts +51 -0
  292. package/src/lifecycle/gate-ledger.ts +7 -0
  293. package/src/lifecycle/git.ts +102 -16
  294. package/src/lint/post-synth.test.ts +19 -0
  295. package/src/lint/post-synth.ts +19 -1
  296. package/src/meta/declared-imports.test.ts +1 -1
  297. package/src/op/activities/activity-contracts.ts +84 -0
  298. package/src/op/activities/change-set.ts +114 -0
  299. package/src/op/activities/decide.ts +3 -0
  300. package/src/op/activities/factory.ts +675 -0
  301. package/src/op/activities/index.ts +6 -0
  302. package/src/op/activity-contract.test.ts +21 -1
  303. package/src/op/activity-contract.ts +19 -9
  304. package/src/op/builders.ts +23 -0
  305. package/src/op/decide-backend.ts +5 -1
  306. package/src/op/decide-read-inputs.ts +33 -5
  307. package/src/op/factory.e2e.test.ts +232 -0
  308. package/src/op/factory.ts +118 -0
  309. package/src/op/index.ts +9 -1
  310. package/src/op/op-ir.test.ts +9 -0
  311. package/src/op/op-ir.ts +4 -0
  312. package/src/op/op-verb-class.ts +3 -0
  313. package/src/op/types.ts +8 -0
  314. package/src/op/work-lease-run.ts +3 -0
  315. package/src/plan-summary.test.ts +263 -0
  316. package/src/plan-summary.ts +845 -0
  317. package/src/pr-forge.test.ts +237 -0
  318. package/src/pr-forge.ts +332 -0
  319. package/src/pr-loop-bundle.test.ts +46 -0
  320. package/src/pr-loop.test.ts +171 -0
  321. package/src/pr-loop.ts +324 -0
  322. package/src/telemetry-attribution.test.ts +23 -1
  323. package/src/telemetry-attribution.ts +51 -0
  324. package/src/terraform/hcl2json.test.ts +21 -0
  325. package/src/terraform/hcl2json.ts +81 -0
  326. package/src/terraform/parse.ts +11 -74
  327. package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
  328. package/src/workspace/app-kind.test.ts +196 -0
  329. package/src/workspace/box-cli.ts +113 -0
  330. package/src/workspace/box-factory.test.ts +4 -1
  331. package/src/workspace/box-factory.ts +37 -4
  332. package/src/workspace/box-listing-write.schema.json +204 -0
  333. package/src/workspace/box-listing.test.ts +234 -0
  334. package/src/workspace/box-listing.ts +324 -0
  335. package/src/workspace/box-publish.schema.json +498 -0
  336. package/src/workspace/box-publish.test.ts +204 -0
  337. package/src/workspace/box-publish.ts +420 -0
  338. package/src/workspace/box-ship.test.ts +96 -0
  339. package/src/workspace/broker-protocol.schema.json +186 -0
  340. package/src/workspace/broker-protocol.test.ts +122 -0
  341. package/src/workspace/broker-protocol.ts +317 -0
  342. package/src/workspace/builder-tiers.test.ts +146 -0
  343. package/src/workspace/change-set.schema.json +460 -0
  344. package/src/workspace/chant-migrations.e2e.test.ts +17 -5
  345. package/src/workspace/checks/boxes.test.ts +3 -0
  346. package/src/workspace/checks.ts +10 -3
  347. package/src/workspace/component-environments.test.ts +105 -0
  348. package/src/workspace/composites.schema.json +23 -4
  349. package/src/workspace/composites.test.ts +8 -8
  350. package/src/workspace/composites.ts +13 -2
  351. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +2 -2
  352. package/src/workspace/conformance/__fixtures__/reference-broker.ts +207 -0
  353. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +7 -2
  354. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +42 -2
  355. package/src/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
  356. package/src/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
  357. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +96 -0
  358. package/src/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
  359. package/src/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
  360. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +14 -8
  361. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +187 -6
  362. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +25 -0
  363. package/src/workspace/conformance/broker.test.ts +122 -0
  364. package/src/workspace/conformance/broker.ts +792 -0
  365. package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
  366. package/src/workspace/conformance/index.mjs +22 -1
  367. package/src/workspace/conformance/index.ts +5 -1
  368. package/src/workspace/conformance/vitest.ts +33 -1
  369. package/src/workspace/conformance/writer-conformance.e2e.test.ts +49 -3
  370. package/src/workspace/conformance/writer.test.ts +32 -2
  371. package/src/workspace/conformance/writer.ts +339 -28
  372. package/src/workspace/decide.test.ts +208 -4
  373. package/src/workspace/decide.ts +427 -73
  374. package/src/workspace/decision-points.schema.json +11 -4
  375. package/src/workspace/declaration.schema.json +153 -4
  376. package/src/workspace/declaration.ts +141 -0
  377. package/src/workspace/environments.ts +66 -14
  378. package/src/workspace/factory-rules.test.ts +62 -0
  379. package/src/workspace/factory-rules.ts +218 -0
  380. package/src/workspace/graph-cli.ts +2 -2
  381. package/src/workspace/graph.schema.json +67 -0
  382. package/src/workspace/identity.test.ts +23 -0
  383. package/src/workspace/init.ts +82 -2
  384. package/src/workspace/intent-cli.ts +26 -9
  385. package/src/workspace/intent-patch-id.test.ts +162 -0
  386. package/src/workspace/intent-record.schema.json +130 -3
  387. package/src/workspace/intent-record.ts +74 -28
  388. package/src/workspace/intent-reviews.test.ts +76 -0
  389. package/src/workspace/intent-squash.test.ts +213 -0
  390. package/src/workspace/intent-why.test.ts +5 -5
  391. package/src/workspace/intent.schema.json +234 -4
  392. package/src/workspace/intent.ts +260 -66
  393. package/src/workspace/json-edit.test.ts +121 -0
  394. package/src/workspace/json-edit.ts +142 -0
  395. package/src/workspace/jsonc.ts +20 -0
  396. package/src/workspace/kinds.ts +157 -2
  397. package/src/workspace/lexicon-symbols.test.ts +148 -0
  398. package/src/workspace/lexicon-symbols.ts +125 -0
  399. package/src/workspace/lineage-upgrade.test.ts +63 -0
  400. package/src/workspace/lineage-upgrade.ts +146 -5
  401. package/src/workspace/plan-summary.schema.json +155 -0
  402. package/src/workspace/point-answer.schema.json +42 -2
  403. package/src/workspace/points-cli.ts +42 -16
  404. package/src/workspace/points-write.schema.json +162 -4
  405. package/src/workspace/points.schema.json +157 -2
  406. package/src/workspace/points.test.ts +13 -1
  407. package/src/workspace/points.ts +118 -12
  408. package/src/workspace/pr-report.schema.json +95 -0
  409. package/src/workspace/profiles.test.ts +159 -0
  410. package/src/workspace/read-contract.test.ts +2 -2
  411. package/src/workspace/reason-codes.test.ts +13 -1
  412. package/src/workspace/reason-codes.ts +38 -4
  413. package/src/workspace/record-sessions.ts +9 -8
  414. package/src/workspace/records-amend.schema.json +78 -0
  415. package/src/workspace/records-cli.ts +20 -6
  416. package/src/workspace/records-close.schema.json +78 -0
  417. package/src/workspace/records-close.ts +31 -7
  418. package/src/workspace/records-concurrent.e2e.test.ts +135 -0
  419. package/src/workspace/records-concurrent.test.ts +145 -0
  420. package/src/workspace/records-formats.test.ts +46 -10
  421. package/src/workspace/records-new.schema.json +7 -0
  422. package/src/workspace/records-review.schema.json +78 -0
  423. package/src/workspace/records-sessions-write.test.ts +69 -0
  424. package/src/workspace/records-sessions.test.ts +22 -0
  425. package/src/workspace/records-write.ts +192 -25
  426. package/src/workspace/records.schema.json +16 -0
  427. package/src/workspace/records.ts +50 -9
  428. package/src/workspace/reference-kinds/app/package.json +10 -0
  429. package/src/workspace/reference-kinds/app/workspace-kinds.json +45 -0
  430. package/src/workspace/run-statement.schema.json +622 -0
  431. package/src/workspace/runs-cli.ts +343 -14
  432. package/src/workspace/runs-statement.test.ts +239 -0
  433. package/src/workspace/runs-write.schema.json +416 -5
  434. package/src/workspace/runs.schema.json +451 -4
  435. package/src/workspace/runs.test.ts +39 -0
  436. package/src/workspace/runs.ts +296 -35
  437. package/src/workspace/session-kinds.ts +3 -3
  438. package/src/workspace/squash.ts +198 -0
  439. package/src/workspace/status-contract.test.ts +5 -2
  440. package/src/workspace/status-gates.ts +3 -1
  441. package/src/workspace/status.schema.json +126 -3
  442. package/src/workspace/status.ts +90 -1
  443. package/src/workspace/symbols.ts +20 -9
  444. package/src/workspace/trust/run-commits.ts +115 -0
  445. package/src/workspace/trust/run-statement.ts +226 -0
  446. package/src/workspace/trust/seal.ts +8 -2
  447. package/src/workspace/trust/verify-cli.ts +6 -5
  448. package/src/workspace/trust/verify.ts +39 -6
  449. package/src/workspace/wip-cli.ts +97 -0
  450. package/src/workspace/wip-write.schema.json +504 -0
  451. package/src/workspace/wip.schema.json +334 -0
  452. package/src/workspace/wip.test.ts +294 -0
  453. package/src/workspace/wip.ts +753 -0
  454. package/src/workspace/work-cli.ts +3 -0
  455. package/src/workspace/work-evidence.schema.json +3 -0
  456. package/src/workspace/work-size.test.ts +77 -0
  457. package/src/workspace/work-size.ts +93 -0
  458. package/src/workspace/workspace-kinds.schema.json +122 -0
  459. package/src/workspace/write-lock-cli.ts +131 -0
  460. package/src/workspace/write-lock.schema.json +172 -0
  461. package/src/workspace/write-lock.test.ts +189 -0
  462. package/src/workspace/write-lock.ts +474 -0
  463. package/src/workspace/write-scope.test.ts +16 -1
  464. package/src/workspace/write-scope.ts +33 -11
package/src/cli/main.ts CHANGED
@@ -347,6 +347,30 @@ export function parseArgs(args: string[]): ParsedArgs {
347
347
  result.fromAffected = args[++i];
348
348
  } else if (arg === "--gate") {
349
349
  result.gate = args[++i];
350
+ } else if (arg === "--wave-gate") {
351
+ result.waveGate = args[++i];
352
+ if (!result.waveGate || result.waveGate.startsWith("-")) throw new Error("--wave-gate needs a gate name: --wave-gate <name>");
353
+ } else if (arg === "--wave") {
354
+ const raw = args[++i];
355
+ const wave = Number(raw);
356
+ if (!Number.isInteger(wave) || wave < 1) throw new Error(`--wave needs a wave number from 1: got "${raw ?? ""}"`);
357
+ result.wave = wave;
358
+ } else if (arg === "--canary") {
359
+ const raw = args[++i];
360
+ if (!raw || raw.startsWith("-")) throw new Error("--canary needs a component name: --canary <name>[,<name>...]");
361
+ (result.canary ??= []).push(...raw.split(",").map((n) => n.trim()).filter(Boolean));
362
+ } else if (arg === "--pr") {
363
+ const raw = args[++i];
364
+ const pr = Number(raw);
365
+ if (!Number.isInteger(pr) || pr < 1) throw new Error(`--pr needs a pull or merge request number: got "${raw ?? ""}"`);
366
+ result.pr = pr;
367
+ } else if (arg === "--forge") {
368
+ result.forge = args[++i];
369
+ if (!result.forge || result.forge.startsWith("-")) throw new Error("--forge needs a forge: --forge github|gitlab|forgejo");
370
+ } else if (arg === "--require-review") {
371
+ result.requireReview = true;
372
+ } else if (arg === "--pr-loop") {
373
+ result.prLoop = true;
350
374
  } else if (arg === "--resume") {
351
375
  result.resume = args[++i];
352
376
  } else if (arg === "--local") {
@@ -389,6 +413,9 @@ export function parseArgs(args: string[]): ParsedArgs {
389
413
  if (value !== undefined && !value.startsWith("-")) result.intent = args[++i];
390
414
  else if (args.includes("--record")) result.intent = "";
391
415
  else throw new Error("--intent needs a region: --intent <path[:start-end]|path#symbol>, or --intent --record <id>");
416
+ } else if (arg === "--follow-squash") {
417
+ // `chant workspace graph --intent ... --follow-squash` and `chant workspace runs --follow-squash` (#3035)
418
+ result.followSquash = true;
392
419
  } else if (arg === "--record") {
393
420
  // `chant workspace graph --intent --record <id>`
394
421
  result.record = args[++i];
@@ -423,6 +450,29 @@ export function parseArgs(args: string[]): ParsedArgs {
423
450
  // `chant workspace records amend <id> --set <file|->` (#2670)
424
451
  result.set = args[++i];
425
452
  if (!result.set || (result.set.startsWith("-") && result.set !== "-")) throw new Error("--set needs a JSON file, or - for standard input: --set <file|->");
453
+ } else if (arg === "--cover") {
454
+ // `chant workspace box listing set <member> --cover <image>` (#3308)
455
+ result.cover = args[++i];
456
+ if (!result.cover || result.cover.startsWith("-")) throw new Error("--cover needs a PNG, JPEG or WebP file: --cover <image>");
457
+ } else if (arg === "--cover-path") {
458
+ // `chant workspace box listing set <member> --cover <image> --cover-path <path>` (#3308)
459
+ result.coverPath = args[++i];
460
+ if (!result.coverPath || result.coverPath.startsWith("-")) throw new Error("--cover-path needs a path from the workspace root: --cover-path <path>");
461
+ } else if (arg === "--records") {
462
+ // `chant workspace box publish <member> --records` (#3165): the records kept uncommitted, not a work item.
463
+ result.records = true;
464
+ } else if (arg === "--expect") {
465
+ // `chant workspace records amend|review|close <id> --expect <digest>` (#3173): the record's digest the caller last read.
466
+ result.expect = args[++i];
467
+ if (!result.expect || result.expect.startsWith("-")) throw new Error("--expect needs the record's digest, as records --json prints it: --expect <digest>");
468
+ } else if (arg === "--label") {
469
+ // `chant workspace wip save --label <text>` (#3172)
470
+ result.label = args[++i];
471
+ if (!result.label || result.label.startsWith("-")) throw new Error("--label needs the snapshot's name, such as a turn id: --label <text>");
472
+ } else if (arg === "--branch") {
473
+ // `chant workspace wip --branch <branch>` (#3172)
474
+ result.branch = args[++i];
475
+ if (!result.branch || result.branch.startsWith("-")) throw new Error("--branch needs a branch name: --branch <branch>");
426
476
  } else if (arg === "--verdict") {
427
477
  // `chant workspace records review <id> --verdict agree|dissent|abstain` (#2670)
428
478
  result.verdict = args[++i];
@@ -437,9 +487,13 @@ export function parseArgs(args: string[]): ParsedArgs {
437
487
  // `chant workspace points --open` (#2739): only the questions still open.
438
488
  result.open = true;
439
489
  } else if (arg === "--inputs") {
440
- // `chant workspace points ask <point> --inputs <file|->` (#2739)
490
+ // `chant workspace points ask <point> --inputs <file|-|json>` (#2739; JSON itself, #3403)
441
491
  result.inputs = args[++i];
442
- if (!result.inputs || (result.inputs.startsWith("-") && result.inputs !== "-")) throw new Error("--inputs needs a JSON file, or - for standard input: --inputs <file|->");
492
+ if (!result.inputs || (result.inputs.startsWith("-") && result.inputs !== "-")) throw new Error("--inputs needs a JSON file, - for standard input, or the JSON itself: --inputs <file|-|json>");
493
+ } else if (arg === "--candidates") {
494
+ // `chant workspace points ask <point> --candidates <file|-|json>` (#3403): an ad-hoc point's question and candidates.
495
+ result.candidates = args[++i];
496
+ if (!result.candidates || (result.candidates.startsWith("-") && result.candidates !== "-")) throw new Error("--candidates needs a JSON file, - for standard input, or the JSON itself: --candidates <file|-|json>");
443
497
  } else if (arg === "--response") {
444
498
  // `chant workspace points ask <point> --response <file>` (#2739): a POST /v1/systemone response the caller got.
445
499
  result.response = args[++i];
@@ -508,6 +562,11 @@ export function parseArgs(args: string[]): ParsedArgs {
508
562
  result.actor = args[++i];
509
563
  } else if (arg === "--approver") {
510
564
  result.approver = args[++i];
565
+ } else if (arg === "--relayed-by") {
566
+ // `chant approve <op> <gate> --relayed-by <principal>` and `workspace points answer <id> --relayed-by <principal>` (#3402):
567
+ // who carried the approval or answer to chant for the person it names.
568
+ result.relayedBy = args[++i];
569
+ if (!result.relayedBy || result.relayedBy.startsWith("-")) throw new Error("--relayed-by needs the principal that relayed it: --relayed-by <principal>");
511
570
  } else if (arg === "--on") {
512
571
  // `chant run ... --on <lexicon>` (#2121) — which runtime hosts the run.
513
572
  result.on = args[++i];
@@ -577,6 +636,11 @@ export function parseArgs(args: string[]): ParsedArgs {
577
636
  const value = args[++i];
578
637
  if (!value || value.startsWith("-")) throw new Error(`${arg} needs a value: ${arg} <${arg.slice(2)}>`);
579
638
  result[arg.slice(2) as "holder" | "ttl" | "token" | "outcome"] = value;
639
+ } else if (arg === "--signer") {
640
+ // `chant workspace runs statement <id> --signer <principal>` (#3192): the runner principal that will sign.
641
+ const value = args[++i];
642
+ if (!value || value.startsWith("-")) throw new Error("--signer needs a principal: --signer <principal>");
643
+ result.signer = value;
580
644
  } else if (arg === "--unit" || arg === "--decision") {
581
645
  // `chant workspace runs --unit <id> | --decision <id>` (#3033): the runs on one work item, or for one decision.
582
646
  const value = args[++i];
@@ -659,6 +723,7 @@ export function parseArgs(args: string[]): ParsedArgs {
659
723
  // flags" isn't something this loop can name in isolation.
660
724
  throw new Error(`Unknown flag: ${arg}\nRun "chant --help" to see supported flags.`);
661
725
  } else if (!arg.startsWith("-")) {
726
+ if (result.command) (result.positionals ??= []).push(arg);
662
727
  if (!result.command) {
663
728
  result.command = arg;
664
729
  } else if (result.path === ".") {
@@ -777,6 +842,14 @@ Commands:
777
842
  (--json) its target, stage (planned/emitted/bridged/
778
843
  applied) and path. Read-only.
779
844
 
845
+ Change sets:
846
+ change-set summary <file> The grouped plan summary of a change-set
847
+ [--format text|json|markdown] document: members taking
848
+ [--limit <chars>] the same change grouped, every destroy,
849
+ replacement, failure and hole named.
850
+ markdown fits an MR/PR note of --limit
851
+ characters (default 65536). Read-only.
852
+
780
853
  Ops:
781
854
  run <name> Run an Op on the resolved runtime (--on; local by default)
782
855
  [--work <id>] [--holder <name>]: the work item an Op
@@ -838,7 +911,9 @@ Ops:
838
911
  --sign [<key file>] seals the approval with an ssh
839
912
  key; a gate the workspace's identity.gates names
840
913
  counts only an approval sealed by a key the signers
841
- file at base lists for --actor
914
+ file at base lists for --actor. --relayed-by
915
+ <principal> records who carried the approval to chant
916
+ for --actor (a follower, a bot); --sign covers it
842
917
 
843
918
  graph Show Op dependency graph (--stacks for cross-stack order,
844
919
  --format ir|mermaid|dot|layout for the lint-gated graph IR,
@@ -909,7 +984,7 @@ Workspace (level 1, #2524):
909
984
  the fields hold none. --sign seals the record's author
910
985
  (decided_by for decisions) with an ssh key. Prints
911
986
  {path, id} as JSON and never commits
912
- workspace records amend <id> [--kind <kind file>] --set <file|-> [--sign [<key file>]] [--dry-run]
987
+ workspace records amend <id> [--kind <kind file>] --set <file|-> [--expect <digest>] [--sign [<key file>]] [--dry-run]
913
988
  Set top-level fields of one record. A closed record
914
989
  never changes, and an approved one changes only its
915
990
  state (upward), pins and reviews; anything else is
@@ -918,21 +993,30 @@ Workspace (level 1, #2524):
918
993
  decisions) is refused with ratify-quorum-not-met
919
994
  until its quorum is met. --sign seals the author
920
995
  again; without it an amendment removes the author
921
- seal and says so. Prints {path, id, changed}
922
- workspace records review <id> [--kind <kind file>] --verdict agree|dissent|abstain --by <principal> [--note <text>] [--session <id>] [--sign [<key file>]] [--dry-run]
996
+ seal and says so. --expect refuses the write with
997
+ record-conflict when the record's digest has moved
998
+ on. Prints {path, id, changed, digest}
999
+ workspace records review <id> [--kind <kind file>] --verdict agree|dissent|abstain --by <principal> [--note <text>] [--session <id>] [--expect <digest>] [--sign [<key file>]] [--dry-run]
923
1000
  Append a review to one record, dated and bound to
924
1001
  the digest of the record text. A dissent needs
925
1002
  --note. --sign seals it with an ssh key (git's
926
1003
  user.signingkey without a file); under a signers
927
1004
  file at base only a sealed verdict counts. With
928
1005
  --session, the session must be open, and the verdict
929
- is appended to its verdicts too. Prints
930
- {path, id, review}
931
- workspace records close <session id> [--kind <session kind file>] [--dry-run]
1006
+ is appended to its verdicts too. --expect refuses a
1007
+ verdict on a record that moved on. Prints
1008
+ {path, id, review, digest}
1009
+ workspace records close <session id> [--kind <session kind file>] [--expect <digest>] [--dry-run]
932
1010
  Close an open review session: its state, close time,
933
1011
  closing commit and seal, in one write. Without
934
1012
  --kind, the one session kind the declaration names.
935
1013
  Prints {path, id, changed, seal, closedRev}
1014
+ Every working-tree write holds the working tree's
1015
+ write lock (CHANT_WRITE_LOCK_WAIT_MS, default 15s)
1016
+ workspace lock [acquire --holder <name> [--ttl <duration>] | release --token <token>] [--json]
1017
+ Who holds the working tree's write lock; take it for
1018
+ a batch of writes across chant calls, each run with
1019
+ CHANT_WRITE_LOCK=<token>, and give it back
936
1020
  workspace work claim|renew|release <id> --holder <name> [--kind <kind file>] [--ttl <seconds|duration>] [--token <token>] [--outcome <outcome>] [--note <text>] [--json]
937
1021
  Take, heartbeat or give back the lease on a work item:
938
1022
  refs/chant/lease/work/<id>, a compare-and-set ref with a
@@ -950,12 +1034,14 @@ Workspace (level 1, #2524):
950
1034
  a url or a path pinned by hash) is appended to the
951
1035
  item's pins through records amend. Prints the
952
1036
  work-evidence document and never commits
953
- workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--json]
1037
+ workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--follow-squash] [--json]
954
1038
  The agent runs in the run ledger on chant/lifecycle, each
955
1039
  with its model, tokens, cost and the commits it made,
956
1040
  and totals per work item, decision and principal; a run
957
1041
  that reports no cost is listed as unpriced, never zero.
958
- Read-only, never fetches
1042
+ Read-only, never fetches; --follow-squash joins each
1043
+ squash merge to its pull request's runs, fetching a
1044
+ missing refs/pull/<n>/head from origin
959
1045
  workspace runs start --from <file|->
960
1046
  Record that an agent run started: who it worked for,
961
1047
  its harness, model, work item and lease. Prints the run
@@ -966,18 +1052,65 @@ Workspace (level 1, #2524):
966
1052
  and the commits it made
967
1053
  workspace runs record --from <file|->
968
1054
  Record a finished run, its start and end in one write
1055
+ workspace runs sign <run id> (--key <runner.pem> | --envelope <file|->) [--base <rev>]
1056
+ Store a DSSE statement over an ended run (its record's
1057
+ hash, work item, harness, model and commits), signed
1058
+ with a runner or steward key .chant/trust.json lists
1059
+ at base, or signed elsewhere and checked here
1060
+ workspace runs statement <run id> --signer <principal>
1061
+ Print the statement for a signer whose key is
1062
+ elsewhere, such as a lobby, with the payload to sign
1063
+ workspace runs verify [<run id>] [--require signed] [--base <rev>] [--json]
1064
+ Judge each run's stored statements against the runner
1065
+ keys at base. Reports only, unless --require signed
1066
+ workspace box listing set <member> [--from <file|->] [--cover <image> [--cover-path <path>]] [--by <principal>] [--dry-run]
1067
+ Change a box's listing (published, title, line, cover)
1068
+ in the declaration, in place, keeping its formatting
1069
+ and comments; --cover copies a PNG, JPEG or WebP into
1070
+ the repository. Judged by the write scope at base.
1071
+ Prints the box-listing-write document; never commits
1072
+ workspace box publish <member> (<item> | --records) [--by <principal>] [--head <owner/name>] [--dry-run]
1073
+ Publish a built work item, or the records kept
1074
+ uncommitted, through the publisher the box block names:
1075
+ run with a JSON request on stdin, its answer checked
1076
+ and its commit held to the apply record (ws-088).
1077
+ Prints the box-publish document; chant itself never
1078
+ commits, pushes or calls a forge
1079
+ workspace wip [--branch <branch>] [--json]
1080
+ List the work-in-progress snapshots under
1081
+ refs/chant/wip/<branch>, newest first, and how far each
1082
+ ref the box's replicate policy names is from its remote.
1083
+ Reads locally; never fetches (#3172)
1084
+ workspace wip save [--label <text>] [--by <principal>]
1085
+ Snapshot the whole working tree, uncommitted records
1086
+ included, onto refs/chant/wip/<branch> without touching
1087
+ the index or the branch; pushes when the policy says so
1088
+ workspace wip restore [<snapshot>] [--by <principal>]
1089
+ Put the working tree back as the snapshot (the branch's
1090
+ latest by default) holds it, after a snapshot of how it
1091
+ is now; HEAD never moves
1092
+ workspace wip push Push the refs the box's replicate policy names to its
1093
+ remote, never forced, for the host's schedule
1094
+ workspace wip fetch On a replacement box, bring back what was replicated:
1095
+ create or fast-forward the local refs, then wip restore
969
1096
  workspace points [--open] [--kind <kind file>] [--at <rev>] [--json]
970
1097
  List the decision points the declared answer kinds'
971
1098
  points files declare, and the questions asked of them;
972
1099
  --open keeps the escalated and proposed ones (ws-058)
973
- workspace points ask <point> --inputs <file|-> [--response <file>] [--subject <id>] [--kind <kind file>] [--dry-run]
1100
+ workspace points ask <point> --inputs <file|-|json> [--candidates <file|-|json>] [--response <file>] [--subject <id>] [--kind <kind file>] [--dry-run]
974
1101
  Ask a point's table, model and quorum deciders and
975
1102
  record the answer: proposed from a model, escalated to
976
1103
  people below its threshold. --response is a POST
977
- /v1/systemone response the caller got; chant calls no model
978
- workspace points answer <id> --answer <value> --by <name>... [--kind <kind file>] [--dry-run]
1104
+ /v1/systemone response the caller got; chant calls no model.
1105
+ --candidates gives an ad-hoc point's question and
1106
+ candidates, { question, criteria }
1107
+ workspace points answer <id> --answer <value> --by <name>... [--note <text>] [--relayed-by <principal>] [--kind <kind file>] [--dry-run]
979
1108
  Record people's answer to an open question, or confirm
980
- a model's proposal, once the point's quorum is met
1109
+ a model's proposal, once the point's quorum is met,
1110
+ with their note and who relayed it
1111
+ workspace points retract <id> --by <name>... [--note <text>] [--kind <kind file>] [--dry-run]
1112
+ Take an answer back: the question is open for people
1113
+ again, and the answer stays in its retractions
981
1114
  workspace pin <path> [--json]
982
1115
  Print the integrity value that pins the plugin at
983
1116
  <path>, to put in a path pin of chant.workspace.json.
@@ -987,13 +1120,14 @@ Workspace (level 1, #2524):
987
1120
  member it is bound to, the record kinds and verbs its
988
1121
  write scope allows, and the spec records --current
989
1122
  prints. Without a name, the session CHANT_AGENT names
990
- workspace verify [--base <rev>] [--head <rev>] [--require attested]
1123
+ workspace verify [--base <rev>] [--head <rev>] [--require attested | attested-runs]
991
1124
  Check the commits in base..head against the signers
992
1125
  and roles read from base. A change to the signers file
993
1126
  or .chant/trust.json needs a signature by a signer
994
1127
  trusted at base, and a new signer set needs a
995
- threshold of the old one. Does nothing without a
996
- signers file
1128
+ threshold of the old one. A commit an agent run made
1129
+ is attested by the run's signed statement; with
1130
+ attested-runs, only those commits must be
997
1131
  workspace signers [--json] [rotate [--threshold <n>] | sign --key <file>]
998
1132
  Show the signer history at base, or propose the next
999
1133
  signer set and sign it with a current signer's key
@@ -1101,12 +1235,14 @@ Workspace (level 1, #2524):
1101
1235
  Each composite instance the members declare, with the
1102
1236
  components whose contract can deploy it; an instance
1103
1237
  with none lists an empty set
1104
- workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--json]
1238
+ workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]
1105
1239
  The intent graph over one region: the commits that
1106
1240
  touched it, the decisions whose constrains cover it,
1107
1241
  the artifacts they pin, and findings with closed codes.
1108
- Without --kind, every record kind the declaration names
1109
- workspace graph --intent --record <id> [--at <rev>] [--kind <kind file>...] [--json]
1242
+ Without --kind, every record kind the declaration names.
1243
+ --follow-squash follows a squash merge to its pull
1244
+ request's commits, fetching a missing ref from origin
1245
+ workspace graph --intent --record <id> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]
1110
1246
  One decision's intent walk over every path: and member:
1111
1247
  entry it constrains: the commits in its window, each
1112
1248
  own, worked, within-other or unexplained, with counts
@@ -1156,8 +1292,23 @@ Component release ledger + status:
1156
1292
  (--base <ref> [--head <ref>] [--include-dependents],
1157
1293
  or --from-affected <file>; --dry-run prints the
1158
1294
  derivation and dispatches nothing; --gate <name>
1159
- puts one approval over the whole set; --resume
1160
- <file> finishes an attempt that stopped)
1295
+ puts one approval over the whole set;
1296
+ --wave-gate <name> puts one on each wave, planned
1297
+ when the wave is reached, with --canary <name>
1298
+ for wave 1 and --wave <n> for one wave per CI
1299
+ job; --resume <file> finishes an attempt that
1300
+ stopped)
1301
+ components pr-plan Plan the components a pull request reaches and
1302
+ the ones that depend on them, and write the
1303
+ report and note (--base <ref> --pr <n>
1304
+ [--forge github|gitlab|forgejo] posts them);
1305
+ applies nothing
1306
+ components pr-apply On merge: plan the same members again and apply
1307
+ them only if an approval stands for that digest
1308
+ (chant approve pr-<n> pr-apply --plan <digest>);
1309
+ a moved plan refuses with exit 3
1310
+ (--require-review: the approver must have
1311
+ approved the pull request on the forge)
1161
1312
  components release <env> Append one immutable release record
1162
1313
  (--component <name> --digest <sha256:...>
1163
1314
  [--git-sha <sha>] [--run-id <id>] [--actor <name>]);
@@ -1564,6 +1715,8 @@ export const commandRegistry: CommandDef[] = [
1564
1715
  // Status read over a tree of carve manifests (#2038): the contract a
1565
1716
  // renderer replaces its own walk-and-guess discovery with. Read-only.
1566
1717
  { name: "carve status", handler: runCarveStatus },
1718
+ // #3188 — the grouped plan summary of a change-set document. Reads one file; imported on first use.
1719
+ { name: "change-set summary", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/change-set")).runChangeSetSummary(ctx) },
1567
1720
  { name: "init", handler: runInit, runsNoConfig: true },
1568
1721
  { name: "init lexicon", handler: runInitLexicon },
1569
1722
  { name: "update", handler: runUpdate },
@@ -1630,6 +1783,12 @@ export const commandRegistry: CommandDef[] = [
1630
1783
  { name: "workspace graph", handler: async (ctx) => (await import("../workspace/member-commands")).runWorkspaceMembers(ctx, "graph") },
1631
1784
  // Each unit decides for itself whether its config is loaded (runCommandInProcess).
1632
1785
  { name: "workspace member-run", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/member-run")).runWorkspaceMemberRun(ctx, runCommandInProcess) },
1786
+ // #3308 — a box's listing, written through chant so a tool never edits the declaration itself (ws-074).
1787
+ { name: "workspace box", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/box-cli")).runWorkspaceBox(ctx) },
1788
+ // #3173 — the working tree's write lock, held across chant calls for a batch of writes (ws-089).
1789
+ { name: "workspace lock", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/write-lock-cli")).runWorkspaceLock(ctx) },
1790
+ // #3172 — work in progress under refs/chant/wip/<branch>, replicated under the box's policy (ws-085).
1791
+ { name: "workspace wip", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/wip-cli")).runWorkspaceWip(ctx) },
1633
1792
  { name: "workspace pin", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/pin-cli")).runWorkspacePin(ctx) },
1634
1793
  // #2548 — an agent session's member, write scope and spec, for a session that resumes.
1635
1794
  { name: "workspace agent", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/agent-cli")).runWorkspaceAgent(ctx) },
@@ -1655,6 +1814,9 @@ export const commandRegistry: CommandDef[] = [
1655
1814
 
1656
1815
  // Component release ledger + status surface (#568, epic #551)
1657
1816
  { name: "components fan-out", requiresPlugins: true, handler: runComponentsFanOut },
1817
+ // #3183 — the pull-request loop the generated CI runs. Imported on first use.
1818
+ { name: "components pr-plan", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrPlan(ctx) },
1819
+ { name: "components pr-apply", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrApply(ctx) },
1658
1820
  { name: "components status", requiresPlugins: true, handler: runComponentsStatus },
1659
1821
  { name: "components release", handler: runComponentsReleaseRecord },
1660
1822
  { name: "components export", handler: runComponentsExport },
@@ -0,0 +1,101 @@
1
+ import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { withTestDir } from "@intentius/chant-test-utils";
3
+ import { spawnSync } from "node:child_process";
4
+ import { writeFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { appendPendingGate, readGateLedger } from "../../lifecycle/gate-ledger";
7
+ import { resetGateOrigin } from "../../lifecycle/gate-origin";
8
+
9
+ const started = vi.fn();
10
+ const resolveGate = vi.fn();
11
+
12
+ vi.mock("../../op/discover", () => ({
13
+ discoverOps: async () => ({
14
+ errors: [],
15
+ ops: new Map([
16
+ ["tf-apply", { config: { name: "tf-apply", overview: "apply", phases: [], neverOverMcp: true } }],
17
+ ["plain-op", { config: { name: "plain-op", overview: "plain", phases: [] } }],
18
+ ]),
19
+ }),
20
+ }));
21
+
22
+ vi.mock("../../op/runtimes/local", () => ({
23
+ createLocalOpRuntime: () => ({
24
+ name: "local",
25
+ start: async (config: { name: string }) => {
26
+ started(config.name);
27
+ return { result: async () => ({ op: config.name, runId: "r1", state: "succeeded", startedAt: "t" }) };
28
+ },
29
+ status: async () => undefined,
30
+ list: async () => new Map(),
31
+ resolveGate,
32
+ }),
33
+ }));
34
+
35
+ import { createOpApproveTool, createOpRunTool, createOpStatusTool, createOpReportTool } from "./op-tools";
36
+
37
+ function git(args: string[], cwd: string): void {
38
+ spawnSync("git", args, { cwd, encoding: "utf-8" });
39
+ }
40
+
41
+ describe("neverOverMcp (chant#3447)", () => {
42
+ const cwd = process.cwd();
43
+ beforeEach(() => {
44
+ started.mockClear();
45
+ resolveGate.mockClear();
46
+ });
47
+ afterEach(() => {
48
+ process.chdir(cwd);
49
+ resetGateOrigin();
50
+ });
51
+
52
+ test("op-approve refuses a CI-reached gate of a declared Op and the ledger is unchanged", async () => {
53
+ await withTestDir(async (dir) => {
54
+ git(["init", "-q", "-b", "main"], dir);
55
+ git(["config", "user.email", "t@chant.dev"], dir);
56
+ git(["config", "user.name", "T"], dir);
57
+ writeFileSync(join(dir, "README.md"), "x\n");
58
+ git(["add", "."], dir);
59
+ git(["commit", "-q", "-m", "init"], dir);
60
+
61
+ // A gate reached on forge CI, not over MCP: the same-origin rule would let it through.
62
+ await appendPendingGate(
63
+ {
64
+ op: "tf-apply",
65
+ gate: "wave-2",
66
+ timestamp: "2026-01-01T00:00:00.000Z",
67
+ origin: "cli",
68
+ expiresAt: "2999-01-01T00:00:00.000Z",
69
+ planDigest: `sha256:${"a".repeat(64)}`,
70
+ },
71
+ { cwd: dir },
72
+ );
73
+ const before = await readGateLedger("tf-apply", { cwd: dir });
74
+ expect(before.pending).toHaveLength(1);
75
+
76
+ process.chdir(dir);
77
+ await expect(createOpApproveTool().handler({ name: "tf-apply", gate: "wave-2" })).rejects.toThrow(
78
+ /neverOverMcp.*whichever channel/s,
79
+ );
80
+
81
+ expect(await readGateLedger("tf-apply", { cwd: dir })).toEqual(before);
82
+ expect(resolveGate).not.toHaveBeenCalled();
83
+ });
84
+ });
85
+
86
+ test("op-run refuses a declared Op and starts nothing", async () => {
87
+ await expect(createOpRunTool().handler({ name: "tf-apply" })).rejects.toThrow(/neverOverMcp.*chant run tf-apply/s);
88
+ expect(started).not.toHaveBeenCalled();
89
+ });
90
+
91
+ test("an Op that does not declare it still runs", async () => {
92
+ const result = (await createOpRunTool().handler({ name: "plain-op" })) as { state: string };
93
+ expect(result.state).toBe("succeeded");
94
+ expect(started).toHaveBeenCalledWith("plain-op");
95
+ });
96
+
97
+ test("op-status and op-report still answer for a declared Op", async () => {
98
+ await expect(createOpStatusTool().handler({ name: "tf-apply" })).resolves.toMatchObject({ op: "tf-apply" });
99
+ await expect(createOpReportTool().handler({ name: "tf-apply" })).resolves.toContain("# tf-apply");
100
+ });
101
+ });
@@ -53,6 +53,21 @@ async function runtimeFor(name: unknown): Promise<OpRuntimeProvider> {
53
53
  return plugin.opRuntime;
54
54
  }
55
55
 
56
+ /**
57
+ * The refusal for an Op that declares `neverOverMcp`, or `undefined` when MCP
58
+ * may act on it. It names the rule and the way through, which is a person at a
59
+ * shell. An Op that is not discovered is left to the caller's own not-found
60
+ * handling.
61
+ */
62
+ async function neverOverMcpRefusal(name: string, action: "run" | "approve"): Promise<string | undefined> {
63
+ const { ops } = await discoverOps();
64
+ if (!ops.get(name)?.config.neverOverMcp) return undefined;
65
+ return action === "run"
66
+ ? `Op "${name}" declares neverOverMcp, so it is not run over MCP. Run it at a shell with \`chant run ${name}\`; op-status and op-report still answer here.`
67
+ : `Op "${name}" declares neverOverMcp, so its gates are not resolved over MCP, whichever channel reached them. ` +
68
+ `Approvals belong to people: approve at a shell with \`chant approve ${name} <gate>\`.`;
69
+ }
70
+
56
71
  const RUNTIME_PARAM = {
57
72
  type: "string",
58
73
  description: 'Runtime to address: a lexicon name with an opRuntime, or "local" (the default)',
@@ -103,7 +118,8 @@ export function createOpRunTool(): ToolRegistration {
103
118
  definition: {
104
119
  name: "op-run",
105
120
  description:
106
- "Run an Op on a runtime and return its result. Defaults to the built-in local runtime, which executes the Op in this process.",
121
+ "Run an Op on a runtime and return its result. Defaults to the built-in local runtime, which executes the Op in this process. " +
122
+ "Refuses an Op that declares neverOverMcp.",
107
123
  inputSchema: {
108
124
  type: "object",
109
125
  properties: {
@@ -117,6 +133,8 @@ export function createOpRunTool(): ToolRegistration {
117
133
  const name = params.name as string;
118
134
 
119
135
  const { ops } = await discoverOps();
136
+ const refusal = await neverOverMcpRefusal(name, "run");
137
+ if (refusal) throw new Error(refusal);
120
138
  if (!ops.has(name)) {
121
139
  const available = [...ops.keys()];
122
140
  return `Op "${name}" not found. Available: ${available.join(", ") || "none"}`;
@@ -185,7 +203,8 @@ export function createOpApproveTool(): ToolRegistration {
185
203
  "Record a gate's resolution on the gate ledger and wake the runtime hosting the gated run. " +
186
204
  "Refuses a gate this same channel reached: a run started with op-run must be approved from " +
187
205
  "somewhere else, normally `chant approve <op> <gate>` at a shell. The approver is recorded as " +
188
- "unattested, because this channel cannot verify a name.",
206
+ "unattested, because this channel cannot verify a name. Also refuses every gate of an Op that " +
207
+ "declares neverOverMcp, whatever channel reached the gate.",
189
208
  inputSchema: {
190
209
  type: "object",
191
210
  properties: {
@@ -206,6 +225,9 @@ export function createOpApproveTool(): ToolRegistration {
206
225
  const gate = params.gate as string;
207
226
  if (!name || !gate) throw new Error("op-approve needs both an Op name and a gate name");
208
227
 
228
+ const refusal = await neverOverMcpRefusal(name, "approve");
229
+ if (refusal) throw new Error(refusal);
230
+
209
231
  const runtime = await runtimeFor(params.runtime);
210
232
  const outcome = await recordGateApproval(name, gate, {
211
233
  note: params.note as string | undefined,
@@ -85,7 +85,7 @@ describe("which servers have the workspace tools", () => {
85
85
  const { tools } = (await rpc(server(cwd), "tools/list")) as { tools: { name: string }[] };
86
86
  expect(tools.map((t) => t.name)).toEqual(expect.arrayContaining(names));
87
87
  }
88
- expect(names).toEqual(["workspace-ls", "workspace-status", "workspace-graph", "workspace-changes", "workspace-records", "workspace-points", "workspace-agent", "workspace-work-history", "workspace-runs", "records-new", "records-amend", "records-review", "records-close", "points-answer"]);
88
+ expect(names).toEqual(["workspace-ls", "workspace-status", "workspace-graph", "workspace-changes", "workspace-records", "workspace-points", "workspace-agent", "workspace-work-history", "workspace-runs", "records-new", "records-amend", "records-review", "records-close", "points-answer", "points-retract"]);
89
89
  });
90
90
 
91
91
  test("a server outside any workspace, or given none, does not", async () => {