@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
@@ -15,6 +15,12 @@
15
15
  * proposal a person confirms; below it, the next decider is asked.
16
16
  * quorum people. Always last: the question escalates to them.
17
17
  *
18
+ * A point declared `adhoc` (#3403) has no candidates of its own: its
19
+ * question's text and candidates come with each ask, as an agent's question
20
+ * to the room does, and the answer record keeps them as `asked`, so people's
21
+ * answer is checked against the candidates the question was asked with. Only
22
+ * people decide it: its chain is one quorum.
23
+ *
18
24
  * chant never calls a model (ws-052). {@link runChain} takes the model call
19
25
  * as a function, {@link ModelAsk}, which the decide Op activity (#2740), a
20
26
  * runtime's decider or a test's stub supplies. Without one, a model decider is
@@ -51,6 +57,8 @@ export const POINT_INPUT_OUTPUTS = {
51
57
  finding: { schema: "intent", def: "finding", description: "a finding of graph --intent, or of check --changes (#2794)" },
52
58
  region: { schema: "intent", def: "region", description: "the region an intent graph covers" },
53
59
  commit: { schema: "intent", def: "commit", description: "a commit in an intent graph's window" },
60
+ node: { schema: "intent", def: "node", description: "any node of an intent graph, by its id and kind: a commit, decision, unit, artifact or the region itself, as an intent walk asks of it (#3351)" },
61
+ ask: { schema: "points", def: "question", description: "an ad-hoc question as its asker knows it (#3403): the asker's own id for the ask, such as hud's request id for an agent's question, and who asked" },
54
62
  member: { schema: "ls", def: "member", description: "a member, as ls lists it" },
55
63
  gate: { schema: "status", def: "gate", description: "a gate, as status lists it" },
56
64
  release: { schema: "status", def: "release", description: "a release, as status lists it; a release plan's fields until the lifecycle ledger lists plans (#2717)" },
@@ -73,7 +81,8 @@ export type QuestionType = "noul" | "choice" | "score";
73
81
  export interface Question {
74
82
  type: QuestionType;
75
83
  instructions: string;
76
- criteria: Record<string, string> | string[];
84
+ /** The candidates and what each means. Absent only from an ad-hoc point's declaration, whose candidates come with each ask (#3403). */
85
+ criteria?: Record<string, string> | string[];
77
86
  }
78
87
 
79
88
  export type Scalar = string | number | boolean | null;
@@ -92,6 +101,8 @@ export type Decider =
92
101
 
93
102
  export interface Point {
94
103
  title: string;
104
+ /** The question's text and candidates come with each ask, not the declaration (#3403). */
105
+ adhoc?: boolean;
95
106
  question: Question;
96
107
  inputs: Record<string, string>;
97
108
  deciders: Decider[];
@@ -116,8 +127,8 @@ export class PointsError extends Error {
116
127
  /** A question's candidate answers: true and false, a choice's options, or a score's levels. */
117
128
  export function candidates(question: Question): (string | boolean)[] {
118
129
  if (question.type === "noul") return [true, false];
119
- if (question.type === "choice") return Object.keys(question.criteria);
120
- return [...(question.criteria as string[])];
130
+ if (question.type === "choice") return Object.keys(question.criteria ?? {});
131
+ return [...((question.criteria as string[] | undefined) ?? [])];
121
132
  }
122
133
 
123
134
  const ONLY: Record<Decider["kind"], string[]> = { table: ["rows"], model: ["backend", "model", "threshold", "unreachable"], quorum: ["count", "roles"] };
@@ -140,6 +151,12 @@ function pointProblems(points: Record<string, Point>): PointProblem[] {
140
151
  });
141
152
  }
142
153
  }
154
+ if (point.adhoc && (point.deciders.length !== 1 || point.deciders[0].kind !== "quorum")) {
155
+ problems.push({
156
+ field: at("deciders"),
157
+ message: "is an ad-hoc point's chain, and an ad-hoc point is decided by people alone: its chain is one quorum, since no table row or model can know candidates that come with the ask",
158
+ });
159
+ }
143
160
  const allowed = candidates(point.question);
144
161
  const last = point.deciders.length - 1;
145
162
  point.deciders.forEach((d, i) => {
@@ -210,7 +227,9 @@ export function schemaProblems(data: unknown): PointProblem[] {
210
227
  ? `is missing ${JSON.stringify(e.params.missingProperty)}`
211
228
  : e.keyword === "propertyNames"
212
229
  ? `has a name that is not allowed: ${JSON.stringify(e.params.propertyName)}`
213
- : (e.message ?? "is invalid");
230
+ : e.keyword === "not"
231
+ ? "has criteria, and an ad-hoc point's candidates come with each ask (#3403): leave criteria out"
232
+ : (e.message ?? "is invalid");
214
233
  const key = `${field}\0${message}`;
215
234
  if (seen.has(key)) continue;
216
235
  seen.add(key);
@@ -262,8 +281,71 @@ const sha256 = (text: string): string => createHash("sha256").update(text, "utf8
262
281
  /** The version of a point: the sha256 of its declaration. Editing the question or any decider changes it. */
263
282
  export const pointVersion = (point: Point): string => sha256(canonical(point));
264
283
 
265
- /** What is answered once: the point, its version and the inputs. */
266
- export const inputsHash = (name: string, version: string, inputs: Record<string, unknown>): string => sha256(canonical({ point: name, version, inputs }));
284
+ /**
285
+ * What is answered once: the point, its version and the inputs, and for an
286
+ * ad-hoc point the question and candidates it was asked with (#3403).
287
+ */
288
+ export const inputsHash = (name: string, version: string, inputs: Record<string, unknown>, asked?: Asked): string => sha256(canonical({ point: name, version, inputs, asked }));
289
+
290
+ // ── Ad-hoc questions (#3403) ─────────────────────────────────────────────────
291
+
292
+ /** An ad-hoc point's question as one ask gave it: its text and its candidates, in the point's question type's shape. */
293
+ export interface Asked {
294
+ question: string;
295
+ criteria: Record<string, string> | string[];
296
+ }
297
+
298
+ /** The question an answer asks: the point's own, or for an ad-hoc point the one the ask gave. */
299
+ export function askedQuestion(point: Point, asked: Asked | undefined): Question {
300
+ return asked ? { type: point.question.type, instructions: asked.question, criteria: asked.criteria } : point.question;
301
+ }
302
+
303
+ const nonEmpty = (v: unknown): v is string => typeof v === "string" && v.trim() !== "";
304
+
305
+ /**
306
+ * An ad-hoc ask's question and candidates (`points ask --candidates`), checked
307
+ * against the point's question type as a declaration's criteria are: noul
308
+ * describes true and false, choice gives 2 to 255 options, each with what it
309
+ * means, and score 2 to 10 distinct ordered levels. Returns the problems, or
310
+ * the question.
311
+ */
312
+ export function parseAsked(point: Point, value: unknown): { asked: Asked } | { problems: string[] } {
313
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return { problems: ["the candidates must be a JSON object with question and criteria"] };
314
+ const v = value as Record<string, unknown>;
315
+ const problems: string[] = [];
316
+ const extra = Object.keys(v).filter((k) => k !== "question" && k !== "criteria");
317
+ if (extra.length > 0) problems.push(`the candidates take question and criteria, not ${extra.join(", ")}`);
318
+ if (!nonEmpty(v.question)) problems.push("question must be the question's text");
319
+ const c = v.criteria;
320
+ const type = point.question.type;
321
+ if (type === "score") {
322
+ if (!Array.isArray(c) || c.length < 2 || c.length > 10 || !c.every(nonEmpty) || new Set(c).size !== c.length) problems.push("criteria must be 2 to 10 distinct levels, in order, for a score question");
323
+ } else if (c === null || typeof c !== "object" || Array.isArray(c)) {
324
+ problems.push(`criteria must be an object of ${type === "noul" ? "true and false" : "options"}, each with what it means`);
325
+ } else {
326
+ const entries = Object.entries(c as Record<string, unknown>);
327
+ if (!entries.every(([k, d]) => k !== "" && typeof d === "string")) problems.push("each criterion must be a non-empty name with what it means as a string");
328
+ if (type === "noul") {
329
+ const keys = entries.map(([k]) => k).sort();
330
+ if (keys.length !== 2 || keys[0] !== "false" || keys[1] !== "true") problems.push("criteria must describe true and false, and nothing else, for a noul question");
331
+ } else if (entries.length < 2 || entries.length > 255) {
332
+ problems.push("criteria must give 2 to 255 options for a choice question");
333
+ }
334
+ }
335
+ if (problems.length > 0) return { problems };
336
+ const criteria = Array.isArray(c) ? [...(c as string[])] : { ...(c as Record<string, string>) };
337
+ return { asked: { question: (v.question as string).trim(), criteria } };
338
+ }
339
+
340
+ /** An answer record's `asked`, when it has one. */
341
+ export function askedOf(data: Record<string, unknown>): Asked | undefined {
342
+ const a = data.asked as Record<string, unknown> | undefined;
343
+ if (a === null || typeof a !== "object" || Array.isArray(a) || typeof a.question !== "string") return undefined;
344
+ const c = a.criteria;
345
+ if (Array.isArray(c)) return { question: a.question, criteria: c.filter((x): x is string => typeof x === "string") };
346
+ if (c !== null && typeof c === "object") return { question: a.question, criteria: Object.fromEntries(Object.entries(c as Record<string, unknown>).filter(([, d]) => typeof d === "string")) as Record<string, string> };
347
+ return undefined;
348
+ }
267
349
 
268
350
  /** The id of the answer to `name` for inputs hashing to `hash`: the name and the hash's first 12 hex digits. */
269
351
  export const answerId = (name: string, hash: string): string => `${name}-${hash.slice(0, 12)}`;
@@ -311,15 +393,28 @@ export interface WireQuestion {
311
393
  criteria: Record<string, string> | string[];
312
394
  }
313
395
 
314
- /** One answer in the wire format's shape. */
396
+ /**
397
+ * One answer in the wire format's shape. `reason` is the model's own
398
+ * explanation of its answer, when the backend gives one (#3345): kept in the
399
+ * answer record beside a proposal, or in the escalation entry of an answer
400
+ * below the threshold, so a person sees why the model chose.
401
+ */
315
402
  export type WireAnswer =
316
- | { type: "noul"; noul: number }
317
- | { type: "choice"; choice: string; probabilities?: Record<string, number>; confidence?: number }
318
- | { type: "score"; score?: number; legend?: unknown; probabilities?: Record<string, number>; confidence?: number }
403
+ | { type: "noul"; noul: number; reason?: string }
404
+ | { type: "choice"; choice: string; probabilities?: Record<string, number>; confidence?: number; reason?: string }
405
+ | { type: "score"; score?: number; legend?: unknown; probabilities?: Record<string, number>; confidence?: number; reason?: string }
319
406
  | { type: "unsupported" };
320
407
 
408
+ /** A wire answer's reason, trimmed, or undefined when it gives none (or not a string). */
409
+ export function answerReason(answer: WireAnswer | undefined): string | undefined {
410
+ const raw = (answer as { reason?: unknown } | undefined)?.reason;
411
+ if (typeof raw !== "string") return undefined;
412
+ const reason = raw.trim();
413
+ return reason === "" ? undefined : reason;
414
+ }
415
+
321
416
  export function wireQuestion(question: Question): WireQuestion {
322
- return { type: question.type, instructions: question.instructions, criteria: question.criteria };
417
+ return { type: question.type, instructions: question.instructions, criteria: question.criteria ?? {} };
323
418
  }
324
419
 
325
420
  /** What a model decider is asked. */
@@ -388,7 +483,12 @@ export function observe(question: Question, answer: WireAnswer | undefined, thre
388
483
  return { observed: false, answer: choice, probabilities, confidence, reason: `confidence ${confidence} is below the threshold ${threshold}` };
389
484
  }
390
485
 
391
- /** A decider that was asked and did not answer, and why. */
486
+ /**
487
+ * A decider that was asked and did not answer, and why: `reason` is chant's
488
+ * account of why it did not answer. `model_reason` is the model's own
489
+ * explanation of the answer it gave (#3345), when it gave one; it is not named
490
+ * `reason` because that field already says why the decider escalated.
491
+ */
392
492
  export interface Escalation {
393
493
  kind: "table" | "model";
394
494
  reason: string;
@@ -398,6 +498,7 @@ export interface Escalation {
398
498
  probabilities?: Record<string, number>;
399
499
  confidence?: number;
400
500
  threshold?: number;
501
+ model_reason?: string;
401
502
  }
402
503
 
403
504
  export type ChainResult =
@@ -409,6 +510,8 @@ export type ChainResult =
409
510
  probabilities: Record<string, number>;
410
511
  confidence: number;
411
512
  threshold: number;
513
+ /** The model's explanation of its answer, when the backend gave one (#3345). */
514
+ reason?: string;
412
515
  escalations: Escalation[];
413
516
  }
414
517
  | { status: "escalated"; decider: { kind: "quorum"; count: number; roles?: string[] }; escalations: Escalation[] };
@@ -455,6 +558,7 @@ export async function runChain(name: string, point: Point, inputs: Record<string
455
558
  continue;
456
559
  }
457
560
  const seen = observe(point.question, reply.answer, d.threshold);
561
+ const why = answerReason(reply.answer);
458
562
  if (seen.observed) {
459
563
  return {
460
564
  status: "proposed",
@@ -463,6 +567,7 @@ export async function runChain(name: string, point: Point, inputs: Record<string
463
567
  probabilities: seen.probabilities,
464
568
  confidence: seen.confidence,
465
569
  threshold: d.threshold,
570
+ ...(why !== undefined ? { reason: why } : {}),
466
571
  escalations,
467
572
  };
468
573
  }
@@ -475,6 +580,7 @@ export async function runChain(name: string, point: Point, inputs: Record<string
475
580
  ...(seen.confidence !== undefined ? { confidence: seen.confidence } : {}),
476
581
  threshold: d.threshold,
477
582
  reason: `not observed: ${seen.reason}`,
583
+ ...(why !== undefined ? { model_reason: why } : {}),
478
584
  });
479
585
  } else {
480
586
  return { status: "escalated", decider: { kind: "quorum", count: d.count, ...(d.roles ? { roles: d.roles } : {}) }, escalations };
@@ -0,0 +1,95 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://intentius.io/chant/schemas/workspace/pr-report/v1/pr-report.schema.json",
4
+ "title": "pull-request report",
5
+ "description": "What one stage of the pull-request loop (#3183) planned or applied: the members a change reaches, the digest the gate binds, where the approval stands and how the stage ended. Written by `chant components pr-plan` (pr-plan.json) and `chant components pr-apply` (pr-apply.json), and built by prReport in @intentius/chant/components/pr-run. The pull-request note is rendered from it by renderPrNote in @intentius/chant/pr-loop. Version 1 of the workspace read contract. Readers ignore fields they do not know; a field is only ever added within a version.",
6
+ "type": "object",
7
+ "required": ["$schema", "contract", "stage", "pr", "env", "base", "head", "op", "gate", "digest", "status", "approval", "selection", "members", "changeSet"],
8
+ "properties": {
9
+ "$schema": { "const": "https://intentius.io/chant/schemas/workspace/pr-report/v1/pr-report.schema.json" },
10
+ "contract": { "const": 1 },
11
+ "stage": { "enum": ["plan", "apply"] },
12
+ "pr": {
13
+ "type": ["integer", "null"],
14
+ "minimum": 1,
15
+ "description": "The pull or merge request number. null when an apply found no pull request that merged its commit."
16
+ },
17
+ "env": { "type": "string" },
18
+ "base": { "type": "string", "pattern": "^[0-9a-f]{40,64}$", "description": "The commit the change is measured from: the merge base of the given base and head." },
19
+ "head": { "type": "string", "pattern": "^[0-9a-f]{40,64}$" },
20
+ "op": {
21
+ "type": "string",
22
+ "description": "The gate's op, pr-<number>. Its ledger is _gates/<op>.jsonl on chant/lifecycle. pr-unknown when no pull request was found."
23
+ },
24
+ "gate": { "type": "string", "description": "The gate the apply waits on, pr-apply unless the pipeline names another." },
25
+ "digest": {
26
+ "type": "string",
27
+ "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$",
28
+ "description": "The change-set digest of what this stage planned: computePlanDigest(\"change-set\", ...) over every member's { member, planDigest }. What the gate binds."
29
+ },
30
+ "status": {
31
+ "enum": ["planned", "plan-failed", "nothing", "applied", "failed", "refused"],
32
+ "description": "planned: every member planned (plan stage). plan-failed: a member failed to plan, so nothing can apply. nothing: no component deploys what the change touches. applied: every member applied. failed: a member failed to apply and its dependents did not run. refused: nothing applied, see refusal."
33
+ },
34
+ "refusal": {
35
+ "enum": ["not-approved", "plan-changed", "not-a-reviewer", "no-pull-request"],
36
+ "description": "Why an apply refused. plan-changed: an approval stands for another digest, so the plan changed after review."
37
+ },
38
+ "message": { "type": "string", "description": "The refusal or failure as printed." },
39
+ "approval": {
40
+ "type": "object",
41
+ "required": ["status"],
42
+ "properties": {
43
+ "status": { "enum": ["approved", "changed", "pending"], "description": "approved: an approval stands for digest. changed: one stands for another digest. pending: none stands." },
44
+ "approvedBy": { "type": "array", "items": { "type": "string" }, "description": "Who approved digest, as the approval names them (github:<login>, gitlab:<login>, <forge>@<host>:<login>, or a signer)." },
45
+ "approved": { "type": "string", "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$", "description": "With changed: the digest that was approved." }
46
+ }
47
+ },
48
+ "selection": {
49
+ "type": "object",
50
+ "required": ["changed", "dependents", "unclaimed", "indeterminate", "waves"],
51
+ "description": "Component names. The fan-out derivation the change gives (#2417).",
52
+ "properties": {
53
+ "changed": { "type": "array", "items": { "type": "string" }, "description": "Components deploying a unit the change touched, in dependency order." },
54
+ "dependents": { "type": "array", "items": { "type": "string" }, "description": "Components selected because they depend on a changed one, in dependency order." },
55
+ "unclaimed": { "type": "array", "items": { "type": "string" }, "description": "Units the change touched that no component deploys." },
56
+ "indeterminate": { "type": "array", "items": { "type": "string" }, "description": "Components a source diff cannot judge that the walk did not reach." },
57
+ "waves": { "type": "array", "items": { "type": "array", "items": { "type": "string" } }, "description": "The selected components in parallel-safe waves, in apply order." }
58
+ }
59
+ },
60
+ "members": {
61
+ "type": "array",
62
+ "description": "One entry per change-set member, sorted by member.",
63
+ "items": {
64
+ "type": "object",
65
+ "required": ["member", "component", "planDigest", "status", "counts"],
66
+ "properties": {
67
+ "member": { "type": "string", "description": "The member's name in the change set: the root, for a terraform-family step." },
68
+ "component": { "type": "string" },
69
+ "planDigest": { "type": ["string", "null"], "description": "What a gate on this member alone binds. null when it failed to plan." },
70
+ "status": { "enum": ["planned", "plan-failed", "applied", "failed", "blocked"] },
71
+ "counts": {
72
+ "type": "object",
73
+ "required": ["create", "update", "replace", "delete"],
74
+ "properties": {
75
+ "create": { "type": "integer", "minimum": 0 },
76
+ "update": { "type": "integer", "minimum": 0 },
77
+ "replace": { "type": "integer", "minimum": 0 },
78
+ "delete": { "type": "integer", "minimum": 0 }
79
+ }
80
+ },
81
+ "error": { "type": "string" },
82
+ "inputsMoved": {
83
+ "type": "array",
84
+ "items": { "type": "string" },
85
+ "description": "Apply stage: components this one depends on whose outputs changed when they applied. It applied the plan it was approved with; the next run plans it against the new values."
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "changeSet": {
91
+ "type": "object",
92
+ "description": "The change-set document over every member (change-set.schema.json). Its digest equals digest."
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * #3174, ws-096: factory fields are opt-in, and the reference workspace ships
3
+ * three profiles (ideation, app, infra) that `chant workspace init --profile`
4
+ * copies. A records-only workspace, an infra workspace with no factory and an
5
+ * infra workspace whose factory has no app member and no box services all
6
+ * pass `chant workspace check`, and so does a copy of each profile.
7
+ */
8
+
9
+ import { execFileSync, spawnSync } from "node:child_process";
10
+ import { mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, statSync } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { join, relative } from "node:path";
13
+ import { afterAll, describe, expect, test } from "vitest";
14
+ import { REPO, repo } from "./__fixtures__/contract-repo";
15
+ import { plantability } from "./box-factory";
16
+ import { runDeclarationChecks } from "./checks";
17
+ import { readDeclaration } from "./declaration";
18
+ import { PROFILES, PROFILES_DIR, profileSource } from "./init";
19
+ import { initFromCommand } from "./lineage-init";
20
+ import { workingTree } from "./tree";
21
+
22
+ const scratch: string[] = [];
23
+ afterAll(() => {
24
+ for (const d of scratch) rmSync(d, { recursive: true, force: true });
25
+ });
26
+
27
+ const REFERENCE = join(REPO, "reference-workspace");
28
+ const CHANT = join(REPO, "packages", "core", "bin", "chant");
29
+
30
+ /** Every file under `dir`, relative to it, sorted. */
31
+ function files(dir: string): string[] {
32
+ const out: string[] = [];
33
+ const walk = (at: string) => {
34
+ for (const name of readdirSync(at)) {
35
+ const p = join(at, name);
36
+ if (statSync(p).isDirectory()) walk(p);
37
+ else out.push(relative(dir, p));
38
+ }
39
+ };
40
+ walk(dir);
41
+ return out.sort();
42
+ }
43
+
44
+ /** The errors `chant workspace check` reports, as `<id>:<entity>: <message>`. */
45
+ async function errors(root: string): Promise<string[]> {
46
+ const report = await runDeclarationChecks(root);
47
+ return report.diagnostics.filter((d) => d.severity === "error").map((d) => `${d.ruleId}:${d.entity ?? ""}: ${d.message}`);
48
+ }
49
+
50
+ /** A copy of a profile, made the way `chant workspace init --profile` makes it, from this checkout. */
51
+ async function copyOf(profile: string): Promise<string> {
52
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), `chant-3174-${profile}-`)));
53
+ scratch.push(dir);
54
+ const made = await initFromCommand({ from: `${REPO}#${PROFILES_DIR}/${profile}`, path: dir, params: profile === "infra" ? {} : { name: "Fern" } });
55
+ expect(made.success, made.error).toBe(true);
56
+ execFileSync("git", ["init", "-q"], { cwd: dir });
57
+ return dir;
58
+ }
59
+
60
+ describe("factory fields are opt-in (#3174)", () => {
61
+ test("a records-only workspace passes check, with no member, box or factory", async () => {
62
+ const root = repo({
63
+ "chant.workspace.json": JSON.stringify({ name: "notes", schema: 1, members: [], records: [{ kind: "decisions/decision.kind.mjs" }] }, null, 2),
64
+ "decisions/decision.kind.mjs": readFileSync(join(REFERENCE, "decisions", "decision.kind.mjs"), "utf-8"),
65
+ "decisions/decision.schema.json": readFileSync(join(REFERENCE, "decisions", "decision.schema.json"), "utf-8"),
66
+ });
67
+ expect(await errors(root)).toEqual([]);
68
+ });
69
+
70
+ test("an infra workspace with no factory passes check", async () => {
71
+ const root = repo({
72
+ "chant.workspace.json": JSON.stringify({ name: "estate", schema: 1, members: [{ name: "network", dir: "network", kind: "chant" }] }, null, 2),
73
+ "network/chant.config.json": '{ "lexicons": ["terraform"] }\n',
74
+ });
75
+ expect(await errors(root)).toEqual([]);
76
+ });
77
+
78
+ test("an infra workspace whose factory builds an estate member, with no app member and no box services, passes check and isn't plantable", async () => {
79
+ const declaration = {
80
+ name: "estate",
81
+ schema: 1,
82
+ members: [{ name: "network", dir: "network", kind: "chant", box: { factory: { builds: ["network"], check: { run: "chant lint", kind: "lint" } } } }],
83
+ };
84
+ const root = repo({ "chant.workspace.json": JSON.stringify(declaration, null, 2), "network/chant.config.json": '{ "lexicons": ["terraform"] }\n' });
85
+ expect(await errors(root)).toEqual([]);
86
+ expect(plantability(readDeclaration(workingTree(root), "")).reason?.code).toBe("box-none");
87
+ });
88
+ });
89
+
90
+ describe("the reference workspace's profiles (#3174)", () => {
91
+ test("are ideation, app and infra, each a template directory with a declaration", () => {
92
+ expect(readdirSync(join(REPO, PROFILES_DIR)).sort()).toEqual([...PROFILES].sort());
93
+ });
94
+
95
+ test("carry the reference workspace's own record kinds and app kind, byte for byte", () => {
96
+ for (const profile of PROFILES) {
97
+ const dir = join(REPO, PROFILES_DIR, profile);
98
+ for (const f of files(dir).filter((p) => /^(decisions|work|answers|kinds)\//.test(p))) {
99
+ expect(readFileSync(join(dir, f), "utf-8"), `${profile}/${f}`).toBe(readFileSync(join(REFERENCE, f), "utf-8"));
100
+ }
101
+ }
102
+ });
103
+
104
+ test("ideation: records and a stub app of the app kind, no box and no factory, and check passes", async () => {
105
+ const root = await copyOf("ideation");
106
+ expect(await errors(root)).toEqual([]);
107
+ const d = readDeclaration(workingTree(root), "");
108
+ expect(d.members.map((m) => `${m.name}:${m.kind}`)).toEqual(["app:app"]);
109
+ expect(d.members.every((m) => m.box === null)).toBe(true);
110
+ expect(d.records.map((r) => r.kind)).toEqual(["decisions/decision.kind.mjs", "work/work.kind.mjs", "answers/answer.kind.mjs"]);
111
+ expect(readFileSync(join(root, "app", "server.mjs"), "utf-8")).toContain('NAME = "Fern"');
112
+ });
113
+
114
+ test("app: the app member runs as the box's service and the factory builds it, and check passes", async () => {
115
+ const root = await copyOf("app");
116
+ expect(await errors(root)).toEqual([]);
117
+ const d = readDeclaration(workingTree(root), "");
118
+ expect(plantability(d)).toMatchObject({ plantable: true, box: "app" });
119
+ expect(d.members[0].box?.factory?.builds).toEqual(["app"]);
120
+ });
121
+
122
+ test("infra: an estate member and a factory, no app member and no box services, and check passes", async () => {
123
+ const root = await copyOf("infra");
124
+ expect(await errors(root)).toEqual([]);
125
+ const d = readDeclaration(workingTree(root), "");
126
+ expect(d.members.map((m) => `${m.name}:${m.kind}`)).toEqual(["network:chant"]);
127
+ expect(d.members[0].box?.factory?.check).toEqual({ run: "cd network && npx chant lint && npx chant build", kind: "lint" });
128
+ expect(d.members[0].box?.services).toEqual([]);
129
+ expect(d.pins).toEqual([]);
130
+ });
131
+ });
132
+
133
+ describe("chant workspace init --profile (#3174)", () => {
134
+ test("reads the profile from the chant repository at this chant's tag, or from --from", () => {
135
+ expect(profileSource("app", undefined, "0.103.0")).toBe("https://github.com/INTENTIUS/chant@chant-v0.103.0#reference-workspace/profiles/app");
136
+ expect(profileSource("infra", "/src/chant", "0.103.0")).toBe("/src/chant#reference-workspace/profiles/infra");
137
+ });
138
+
139
+ test("copies the profile with a lineage lock and names the workspace", () => {
140
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "chant-3174-cli-")));
141
+ scratch.push(dir);
142
+ const run = spawnSync(CHANT, ["workspace", "init", dir, "--profile", "ideation", "--from", REPO, "--name", "fern", "--param", "name=Fern", "--yes"], { encoding: "utf-8" });
143
+ expect(run.status, run.stderr).toBe(0);
144
+ expect(JSON.parse(readFileSync(join(dir, "chant.workspace.json"), "utf-8")).name).toBe("fern");
145
+ expect(readdirSync(join(dir, ".chant"))).toContain("workspace.lock.json");
146
+ });
147
+
148
+ test("names the profiles when given another, and writes nothing without --yes", () => {
149
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "chant-3174-cli-")));
150
+ scratch.push(dir);
151
+ const bad = spawnSync(CHANT, ["workspace", "init", dir, "--profile", "web"], { encoding: "utf-8" });
152
+ expect(bad.status).toBe(1);
153
+ expect(bad.stderr).toMatch(/the profiles are ideation, app, infra/);
154
+ const dry = spawnSync(CHANT, ["workspace", "init", dir, "--profile", "infra", "--from", REPO], { encoding: "utf-8", input: "" });
155
+ expect(dry.status).toBe(0);
156
+ expect(dry.stdout).toMatch(/Nothing written/);
157
+ expect(readdirSync(dir)).toEqual([]);
158
+ });
159
+ });
@@ -215,7 +215,7 @@ describe("every schema against the reference workspace (#2543)", () => {
215
215
  expect(doc.components[0].runtimes).toEqual([{ name: "local", lexicon: null, default: true, command: "chant run --components app" }]);
216
216
  expect(doc.members.find((m) => m.name === "delivery")!.runtimeReasons).toEqual([]);
217
217
  // #2695: the reference config declares no environments, so the app deploys to local only.
218
- expect(doc.components[0].environments).toEqual([{ name: "local", default: true, source: "builtin", command: "chant run --components app" }]);
218
+ expect(doc.components[0].environments).toEqual([{ name: "local", default: true, source: "builtin", site: null, command: "chant run --components app" }]);
219
219
  expect(doc.members.find((m) => m.name === "delivery")!.environmentReasons.map((r) => r.code)).toEqual(["environments-none-declared"]);
220
220
  expect(doc.reasons).toEqual([]);
221
221
  }
@@ -244,7 +244,7 @@ describe("every schema against the reference workspace (#2543)", () => {
244
244
  const doc = await workspacePoints({ cwd: FIXTURE, at });
245
245
  expectValid(doc);
246
246
  if ("error" in doc) throw new Error(doc.error.message);
247
- expect(doc.points.map((p) => p.name)).toEqual(["slice-tier", "ship-skip", "finding-triage", "needs-a-decision"]);
247
+ expect(doc.points.map((p) => p.name)).toEqual(["slice-tier", "understand", "ship-skip", "finding-triage", "needs-a-decision", "intent-origin", "intent-judgment", "intent-disposition", "agent-question"]);
248
248
  const sliceTier = doc.points.find((p) => p.name === "slice-tier")!;
249
249
  expect(sliceTier.criteria).toEqual({
250
250
  small: "A haiku-class builder. The work item fits the small limits.",
@@ -29,7 +29,8 @@ import { WORK_WARNING_CODES } from "./work";
29
29
  import { BOX_FINDING_CODES } from "./checks/boxes";
30
30
  import { DIAGRAM_FINDING_CODES } from "./checks/diagrams";
31
31
  import { WORK_ERROR_CODES, WORK_HISTORY_ERROR_CODES, WORK_LEASE_REFUSALS } from "./work-cli";
32
- import { RUNS_ERROR_CODES, RUNS_REASON_CODES, RUNS_WRITE_ERROR_CODES } from "./runs-cli";
32
+ import { RUN_STATEMENT_ERROR_CODES, RUNS_ERROR_CODES, RUNS_REASON_CODES, RUNS_WRITE_ERROR_CODES } from "./runs-cli";
33
+ import { RUN_STATEMENT_FAILURE_CODES } from "./trust/run-statement";
33
34
  import { WORK_EVIDENCE_ERROR_CODES } from "./work-evidence";
34
35
  import { RECORD_FINDING_CODES } from "./checks/records";
35
36
  import { ANSWER_WARNING_CODES } from "./points";
@@ -39,6 +40,10 @@ import { PATCH_ERROR_CODES } from "./patch";
39
40
  import { AGENT_ERROR_CODES } from "./agent-cli";
40
41
  import { SCOPE_FINDING_CODES, WRITE_SCOPE_CODES } from "./write-scope";
41
42
  import { PLANTABLE_REASON_CODES } from "./box-factory";
43
+ import { BOX_LISTING_ERROR_CODES } from "./box-listing";
44
+ import { BOX_PUBLISH_ERROR_CODES } from "./box-publish";
45
+ import { WRITE_LOCK_ERROR_CODES } from "./write-lock-cli";
46
+ import { WIP_ERROR_CODES, WIP_WRITE_ERROR_CODES } from "./wip";
42
47
  import { ROTATION_REFUSAL_CODES } from "./trust/rotation";
43
48
  import { SIGNERS_ERROR_CODES } from "./trust/signers-cli";
44
49
  import { EVIDENCE_ERROR_CODES } from "./trust/evidence";
@@ -88,6 +93,8 @@ const PER_COMMAND: Record<string, readonly string[]> = {
88
93
  RUNS_ERROR_CODES,
89
94
  RUNS_REASON_CODES,
90
95
  RUNS_WRITE_ERROR_CODES,
96
+ RUN_STATEMENT_ERROR_CODES,
97
+ RUN_STATEMENT_FAILURE_CODES,
91
98
  WORK_EVIDENCE_ERROR_CODES,
92
99
  RECORD_FINDING_CODES,
93
100
  ANSWER_WARNING_CODES,
@@ -99,6 +106,11 @@ const PER_COMMAND: Record<string, readonly string[]> = {
99
106
  WRITE_SCOPE_CODES,
100
107
  SCOPE_FINDING_CODES,
101
108
  PLANTABLE_REASON_CODES,
109
+ BOX_LISTING_ERROR_CODES,
110
+ BOX_PUBLISH_ERROR_CODES,
111
+ WRITE_LOCK_ERROR_CODES,
112
+ WIP_ERROR_CODES,
113
+ WIP_WRITE_ERROR_CODES,
102
114
  ROTATION_REFUSAL_CODES,
103
115
  SIGNERS_ERROR_CODES,
104
116
  EVIDENCE_ERROR_CODES,