@intentius/chant 0.102.0 → 0.104.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (471) hide show
  1. package/dist/audit/core.d.ts +2 -0
  2. package/dist/audit/core.d.ts.map +1 -1
  3. package/dist/audit/discover.d.ts.map +1 -1
  4. package/dist/change-set.d.ts +232 -0
  5. package/dist/change-set.d.ts.map +1 -0
  6. package/dist/cli/commands/audit.d.ts +7 -0
  7. package/dist/cli/commands/audit.d.ts.map +1 -1
  8. package/dist/cli/commands/build.d.ts.map +1 -1
  9. package/dist/cli/handlers/build.d.ts.map +1 -1
  10. package/dist/cli/handlers/change-set.d.ts +11 -0
  11. package/dist/cli/handlers/change-set.d.ts.map +1 -0
  12. package/dist/cli/handlers/fan-out.d.ts +33 -0
  13. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  14. package/dist/cli/handlers/operator.d.ts +7 -0
  15. package/dist/cli/handlers/operator.d.ts.map +1 -1
  16. package/dist/cli/handlers/pr.d.ts +31 -0
  17. package/dist/cli/handlers/pr.d.ts.map +1 -0
  18. package/dist/cli/handlers/run.d.ts.map +1 -1
  19. package/dist/cli/main.d.ts.map +1 -1
  20. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  21. package/dist/cli/mcp/workspace-tools.d.ts +3 -2
  22. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  23. package/dist/cli/registry.d.ts +63 -1
  24. package/dist/cli/registry.d.ts.map +1 -1
  25. package/dist/components/auto-release.d.ts +16 -0
  26. package/dist/components/auto-release.d.ts.map +1 -1
  27. package/dist/components/capability.d.ts +53 -0
  28. package/dist/components/capability.d.ts.map +1 -1
  29. package/dist/components/changed-files.d.ts +31 -0
  30. package/dist/components/changed-files.d.ts.map +1 -0
  31. package/dist/components/cli-support.d.ts +9 -1
  32. package/dist/components/cli-support.d.ts.map +1 -1
  33. package/dist/components/component.d.ts +24 -0
  34. package/dist/components/component.d.ts.map +1 -1
  35. package/dist/components/deploy-units.d.ts.map +1 -1
  36. package/dist/components/driver.d.ts +13 -0
  37. package/dist/components/driver.d.ts.map +1 -1
  38. package/dist/components/fan-out-output.d.ts +6 -0
  39. package/dist/components/fan-out-output.d.ts.map +1 -1
  40. package/dist/components/fan-out-record.d.ts +41 -0
  41. package/dist/components/fan-out-record.d.ts.map +1 -0
  42. package/dist/components/fan-out-run.d.ts +65 -0
  43. package/dist/components/fan-out-run.d.ts.map +1 -1
  44. package/dist/components/fan-out-support.d.ts +2 -0
  45. package/dist/components/fan-out-support.d.ts.map +1 -1
  46. package/dist/components/fan-out.d.ts +6 -0
  47. package/dist/components/fan-out.d.ts.map +1 -1
  48. package/dist/components/gated-wave-pipeline.d.ts +50 -0
  49. package/dist/components/gated-wave-pipeline.d.ts.map +1 -0
  50. package/dist/components/index.d.ts +1 -1
  51. package/dist/components/index.d.ts.map +1 -1
  52. package/dist/components/layers.d.ts +25 -0
  53. package/dist/components/layers.d.ts.map +1 -0
  54. package/dist/components/pr-pipeline.d.ts +61 -0
  55. package/dist/components/pr-pipeline.d.ts.map +1 -0
  56. package/dist/components/pr-run.d.ts +119 -0
  57. package/dist/components/pr-run.d.ts.map +1 -0
  58. package/dist/components/promote.d.ts.map +1 -1
  59. package/dist/components/verbs/shell.d.ts.map +1 -1
  60. package/dist/components/wave-plan.d.ts +49 -0
  61. package/dist/components/wave-plan.d.ts.map +1 -0
  62. package/dist/gated-waves.d.ts +147 -0
  63. package/dist/gated-waves.d.ts.map +1 -0
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/lexicon.d.ts +48 -0
  67. package/dist/lexicon.d.ts.map +1 -1
  68. package/dist/lifecycle/gate-ledger.d.ts +7 -0
  69. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  70. package/dist/lifecycle/git.d.ts +46 -1
  71. package/dist/lifecycle/git.d.ts.map +1 -1
  72. package/dist/lint/post-synth.d.ts +18 -0
  73. package/dist/lint/post-synth.d.ts.map +1 -1
  74. package/dist/op/activities/activity-contracts.d.ts +126 -0
  75. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  76. package/dist/op/activities/change-set.d.ts +62 -0
  77. package/dist/op/activities/change-set.d.ts.map +1 -0
  78. package/dist/op/activities/decide.d.ts +2 -0
  79. package/dist/op/activities/decide.d.ts.map +1 -1
  80. package/dist/op/activities/factory.d.ts +137 -0
  81. package/dist/op/activities/factory.d.ts.map +1 -0
  82. package/dist/op/activities/index.d.ts +4 -0
  83. package/dist/op/activities/index.d.ts.map +1 -1
  84. package/dist/op/activity-contract.d.ts.map +1 -1
  85. package/dist/op/builders.d.ts +11 -0
  86. package/dist/op/builders.d.ts.map +1 -1
  87. package/dist/op/decide-backend.d.ts +5 -1
  88. package/dist/op/decide-backend.d.ts.map +1 -1
  89. package/dist/op/decide-read-inputs.d.ts +4 -2
  90. package/dist/op/decide-read-inputs.d.ts.map +1 -1
  91. package/dist/op/factory.d.ts +83 -0
  92. package/dist/op/factory.d.ts.map +1 -0
  93. package/dist/op/index.d.ts +4 -1
  94. package/dist/op/index.d.ts.map +1 -1
  95. package/dist/op/op-ir.d.ts +2 -0
  96. package/dist/op/op-ir.d.ts.map +1 -1
  97. package/dist/op/op-verb-class.d.ts.map +1 -1
  98. package/dist/op/types.d.ts +8 -0
  99. package/dist/op/types.d.ts.map +1 -1
  100. package/dist/op/work-lease-run.d.ts.map +1 -1
  101. package/dist/plan-summary.d.ts +207 -0
  102. package/dist/plan-summary.d.ts.map +1 -0
  103. package/dist/pr-forge.d.ts +105 -0
  104. package/dist/pr-forge.d.ts.map +1 -0
  105. package/dist/pr-loop.d.ts +178 -0
  106. package/dist/pr-loop.d.ts.map +1 -0
  107. package/dist/telemetry-attribution.d.ts +31 -0
  108. package/dist/telemetry-attribution.d.ts.map +1 -1
  109. package/dist/terraform/hcl2json.d.ts +45 -0
  110. package/dist/terraform/hcl2json.d.ts.map +1 -0
  111. package/dist/terraform/parse.d.ts +4 -40
  112. package/dist/terraform/parse.d.ts.map +1 -1
  113. package/dist/vendor/js-yaml-load.d.ts +56 -0
  114. package/dist/vendor/js-yaml-load.d.ts.map +1 -0
  115. package/dist/workspace/box-cli.d.ts +12 -0
  116. package/dist/workspace/box-cli.d.ts.map +1 -0
  117. package/dist/workspace/box-factory.d.ts +27 -3
  118. package/dist/workspace/box-factory.d.ts.map +1 -1
  119. package/dist/workspace/box-listing.d.ts +85 -0
  120. package/dist/workspace/box-listing.d.ts.map +1 -0
  121. package/dist/workspace/box-publish.d.ts +147 -0
  122. package/dist/workspace/box-publish.d.ts.map +1 -0
  123. package/dist/workspace/broker-protocol.d.ts +190 -0
  124. package/dist/workspace/broker-protocol.d.ts.map +1 -0
  125. package/dist/workspace/checks.d.ts.map +1 -1
  126. package/dist/workspace/composites.d.ts +2 -2
  127. package/dist/workspace/composites.d.ts.map +1 -1
  128. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +17 -0
  129. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -0
  130. package/dist/workspace/conformance/broker.d.ts +202 -0
  131. package/dist/workspace/conformance/broker.d.ts.map +1 -0
  132. package/dist/workspace/conformance/index.d.ts +2 -1
  133. package/dist/workspace/conformance/index.d.ts.map +1 -1
  134. package/dist/workspace/conformance/vitest.d.ts +9 -2
  135. package/dist/workspace/conformance/vitest.d.ts.map +1 -1
  136. package/dist/workspace/conformance/writer.d.ts +108 -14
  137. package/dist/workspace/conformance/writer.d.ts.map +1 -1
  138. package/dist/workspace/decide.d.ts +126 -12
  139. package/dist/workspace/decide.d.ts.map +1 -1
  140. package/dist/workspace/decision-points.schema.json +11 -4
  141. package/dist/workspace/declaration.d.ts +65 -0
  142. package/dist/workspace/declaration.d.ts.map +1 -1
  143. package/dist/workspace/declaration.schema.json +153 -4
  144. package/dist/workspace/environments.d.ts +36 -10
  145. package/dist/workspace/environments.d.ts.map +1 -1
  146. package/dist/workspace/factory-rules.d.ts +180 -0
  147. package/dist/workspace/factory-rules.d.ts.map +1 -0
  148. package/dist/workspace/init.d.ts +21 -0
  149. package/dist/workspace/init.d.ts.map +1 -1
  150. package/dist/workspace/intent-cli.d.ts +1 -1
  151. package/dist/workspace/intent-cli.d.ts.map +1 -1
  152. package/dist/workspace/intent-record.d.ts +28 -1
  153. package/dist/workspace/intent-record.d.ts.map +1 -1
  154. package/dist/workspace/intent.d.ts +102 -5
  155. package/dist/workspace/intent.d.ts.map +1 -1
  156. package/dist/workspace/json-edit.d.ts +24 -0
  157. package/dist/workspace/json-edit.d.ts.map +1 -0
  158. package/dist/workspace/jsonc.d.ts +11 -0
  159. package/dist/workspace/jsonc.d.ts.map +1 -1
  160. package/dist/workspace/kinds.d.ts +48 -0
  161. package/dist/workspace/kinds.d.ts.map +1 -1
  162. package/dist/workspace/lexicon-symbols.d.ts +29 -0
  163. package/dist/workspace/lexicon-symbols.d.ts.map +1 -0
  164. package/dist/workspace/lineage-upgrade.d.ts +31 -2
  165. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  166. package/dist/workspace/point-answer.schema.json +42 -2
  167. package/dist/workspace/points-cli.d.ts +10 -3
  168. package/dist/workspace/points-cli.d.ts.map +1 -1
  169. package/dist/workspace/points.d.ts +66 -5
  170. package/dist/workspace/points.d.ts.map +1 -1
  171. package/dist/workspace/reason-codes.d.ts +33 -4
  172. package/dist/workspace/reason-codes.d.ts.map +1 -1
  173. package/dist/workspace/record-sessions.d.ts +5 -7
  174. package/dist/workspace/record-sessions.d.ts.map +1 -1
  175. package/dist/workspace/records-cli.d.ts +7 -0
  176. package/dist/workspace/records-cli.d.ts.map +1 -1
  177. package/dist/workspace/records-close.d.ts +7 -3
  178. package/dist/workspace/records-close.d.ts.map +1 -1
  179. package/dist/workspace/records-write.d.ts +66 -6
  180. package/dist/workspace/records-write.d.ts.map +1 -1
  181. package/dist/workspace/records.d.ts +28 -9
  182. package/dist/workspace/records.d.ts.map +1 -1
  183. package/dist/workspace/runs-cli.d.ts +138 -6
  184. package/dist/workspace/runs-cli.d.ts.map +1 -1
  185. package/dist/workspace/runs.d.ts +132 -11
  186. package/dist/workspace/runs.d.ts.map +1 -1
  187. package/dist/workspace/session-kinds.d.ts +1 -1
  188. package/dist/workspace/session-kinds.d.ts.map +1 -1
  189. package/dist/workspace/squash.d.ts +80 -0
  190. package/dist/workspace/squash.d.ts.map +1 -0
  191. package/dist/workspace/status-gates.d.ts +2 -0
  192. package/dist/workspace/status-gates.d.ts.map +1 -1
  193. package/dist/workspace/status.d.ts +54 -0
  194. package/dist/workspace/status.d.ts.map +1 -1
  195. package/dist/workspace/symbols.d.ts +16 -5
  196. package/dist/workspace/symbols.d.ts.map +1 -1
  197. package/dist/workspace/trust/run-commits.d.ts +46 -0
  198. package/dist/workspace/trust/run-commits.d.ts.map +1 -0
  199. package/dist/workspace/trust/run-statement.d.ts +128 -0
  200. package/dist/workspace/trust/run-statement.d.ts.map +1 -0
  201. package/dist/workspace/trust/seal.d.ts +6 -1
  202. package/dist/workspace/trust/seal.d.ts.map +1 -1
  203. package/dist/workspace/trust/verify-cli.d.ts +1 -1
  204. package/dist/workspace/trust/verify.d.ts +16 -2
  205. package/dist/workspace/trust/verify.d.ts.map +1 -1
  206. package/dist/workspace/wip-cli.d.ts +21 -0
  207. package/dist/workspace/wip-cli.d.ts.map +1 -0
  208. package/dist/workspace/wip.d.ts +268 -0
  209. package/dist/workspace/wip.d.ts.map +1 -0
  210. package/dist/workspace/work-cli.d.ts.map +1 -1
  211. package/dist/workspace/work-evidence.d.ts +1 -1
  212. package/dist/workspace/work-evidence.d.ts.map +1 -1
  213. package/dist/workspace/work-size.d.ts +55 -0
  214. package/dist/workspace/work-size.d.ts.map +1 -0
  215. package/dist/workspace/workspace-kinds.schema.json +122 -0
  216. package/dist/workspace/write-lock-cli.d.ts +60 -0
  217. package/dist/workspace/write-lock-cli.d.ts.map +1 -0
  218. package/dist/workspace/write-lock.d.ts +152 -0
  219. package/dist/workspace/write-lock.d.ts.map +1 -0
  220. package/dist/workspace/write-scope.d.ts +6 -2
  221. package/dist/workspace/write-scope.d.ts.map +1 -1
  222. package/dist/yaml.d.ts +1 -1
  223. package/dist/yaml.d.ts.map +1 -1
  224. package/package.json +6 -1
  225. package/src/__fixtures__/change-set/lifecycle-plan.floci.json +59 -0
  226. package/src/__fixtures__/change-set/lifecycle-v1.ts.txt +4 -0
  227. package/src/__fixtures__/change-set/lifecycle-v2.ts.txt +4 -0
  228. package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +95 -0
  229. package/src/__fixtures__/change-set/warden.change-set.golden.json +106 -0
  230. package/src/__fixtures__/change-set/warden.plan.json +44 -0
  231. package/src/__fixtures__/plan-summary/normalization-vectors.json +1593 -0
  232. package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +31 -0
  233. package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +31 -0
  234. package/src/__fixtures__/pr-loop/pr-plan.golden.json +127 -0
  235. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +37 -0
  236. package/src/audit/core.test.ts +15 -0
  237. package/src/audit/core.ts +8 -2
  238. package/src/audit/discover.test.ts +11 -0
  239. package/src/audit/discover.ts +7 -2
  240. package/src/change-set-bundle.test.ts +57 -0
  241. package/src/change-set.test.ts +215 -0
  242. package/src/change-set.ts +445 -0
  243. package/src/cli/commands/audit-opt-in.test.ts +54 -0
  244. package/src/cli/commands/audit.ts +29 -2
  245. package/src/cli/commands/build.test.ts +19 -0
  246. package/src/cli/commands/build.ts +3 -2
  247. package/src/cli/handlers/build.ts +13 -1
  248. package/src/cli/handlers/change-set.test.ts +56 -0
  249. package/src/cli/handlers/change-set.ts +44 -0
  250. package/src/cli/handlers/fan-out.test.ts +221 -2
  251. package/src/cli/handlers/fan-out.ts +165 -75
  252. package/src/cli/handlers/graph.ts +3 -0
  253. package/src/cli/handlers/operator.test.ts +21 -0
  254. package/src/cli/handlers/operator.ts +34 -3
  255. package/src/cli/handlers/pr.ts +373 -0
  256. package/src/cli/handlers/run.ts +1 -0
  257. package/src/cli/main.test.ts +20 -0
  258. package/src/cli/main.ts +185 -23
  259. package/src/cli/mcp/never-over-mcp.test.ts +101 -0
  260. package/src/cli/mcp/op-tools.ts +24 -2
  261. package/src/cli/mcp/workspace-tools.test.ts +1 -1
  262. package/src/cli/mcp/workspace-tools.ts +34 -6
  263. package/src/cli/registry.ts +63 -1
  264. package/src/components/auto-release.ts +1 -1
  265. package/src/components/capability.ts +55 -0
  266. package/src/components/changed-files.ts +66 -0
  267. package/src/components/cli-support.ts +28 -5
  268. package/src/components/component.schema.json +17 -0
  269. package/src/components/component.ts +25 -0
  270. package/src/components/deploy-units.ts +3 -0
  271. package/src/components/driver.test.ts +52 -0
  272. package/src/components/driver.ts +31 -13
  273. package/src/components/fan-out-output.ts +42 -1
  274. package/src/components/fan-out-record.ts +69 -0
  275. package/src/components/fan-out-run.test.ts +17 -0
  276. package/src/components/fan-out-run.ts +304 -1
  277. package/src/components/fan-out-support.ts +3 -0
  278. package/src/components/fan-out-waves.test.ts +375 -0
  279. package/src/components/fan-out.ts +18 -17
  280. package/src/components/gated-wave-pipeline.ts +82 -0
  281. package/src/components/index.ts +1 -0
  282. package/src/components/layers.ts +30 -0
  283. package/src/components/pr-pipeline.ts +91 -0
  284. package/src/components/pr-run.test.ts +205 -0
  285. package/src/components/pr-run.ts +280 -0
  286. package/src/components/promote.ts +5 -0
  287. package/src/components/verbs/shell.test.ts +11 -0
  288. package/src/components/verbs/shell.ts +7 -3
  289. package/src/components/wave-plan.ts +124 -0
  290. package/src/discovery/sandbox/bundle.ts +1 -1
  291. package/src/gated-waves-bundle.test.ts +62 -0
  292. package/src/gated-waves.test.ts +118 -0
  293. package/src/gated-waves.ts +240 -0
  294. package/src/index.ts +1 -0
  295. package/src/lexicon.ts +51 -0
  296. package/src/lifecycle/gate-ledger.ts +7 -0
  297. package/src/lifecycle/git.ts +102 -16
  298. package/src/lint/post-synth.test.ts +19 -0
  299. package/src/lint/post-synth.ts +19 -1
  300. package/src/meta/declared-imports.test.ts +1 -1
  301. package/src/op/activities/activity-contracts.ts +84 -0
  302. package/src/op/activities/change-set.ts +114 -0
  303. package/src/op/activities/decide.ts +3 -0
  304. package/src/op/activities/factory.ts +675 -0
  305. package/src/op/activities/index.ts +6 -0
  306. package/src/op/activity-contract.test.ts +21 -1
  307. package/src/op/activity-contract.ts +19 -9
  308. package/src/op/builders.ts +23 -0
  309. package/src/op/decide-backend.ts +5 -1
  310. package/src/op/decide-read-inputs.ts +33 -5
  311. package/src/op/factory.e2e.test.ts +232 -0
  312. package/src/op/factory.ts +118 -0
  313. package/src/op/index.ts +9 -1
  314. package/src/op/op-ir.test.ts +9 -0
  315. package/src/op/op-ir.ts +4 -0
  316. package/src/op/op-verb-class.ts +3 -0
  317. package/src/op/types.ts +8 -0
  318. package/src/op/work-lease-run.ts +3 -0
  319. package/src/plan-summary.test.ts +263 -0
  320. package/src/plan-summary.ts +845 -0
  321. package/src/pr-forge.test.ts +237 -0
  322. package/src/pr-forge.ts +332 -0
  323. package/src/pr-loop-bundle.test.ts +46 -0
  324. package/src/pr-loop.test.ts +171 -0
  325. package/src/pr-loop.ts +324 -0
  326. package/src/telemetry-attribution.test.ts +23 -1
  327. package/src/telemetry-attribution.ts +51 -0
  328. package/src/terraform/hcl2json.test.ts +21 -0
  329. package/src/terraform/hcl2json.ts +81 -0
  330. package/src/terraform/parse.ts +11 -74
  331. package/src/vendor/js-yaml-load.test.ts +115 -0
  332. package/src/vendor/js-yaml-load.ts +1939 -0
  333. package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
  334. package/src/workspace/app-kind.test.ts +196 -0
  335. package/src/workspace/box-cli.ts +113 -0
  336. package/src/workspace/box-factory.test.ts +4 -1
  337. package/src/workspace/box-factory.ts +37 -4
  338. package/src/workspace/box-listing-write.schema.json +204 -0
  339. package/src/workspace/box-listing.test.ts +234 -0
  340. package/src/workspace/box-listing.ts +324 -0
  341. package/src/workspace/box-publish.schema.json +498 -0
  342. package/src/workspace/box-publish.test.ts +204 -0
  343. package/src/workspace/box-publish.ts +420 -0
  344. package/src/workspace/box-ship.test.ts +96 -0
  345. package/src/workspace/broker-protocol.schema.json +186 -0
  346. package/src/workspace/broker-protocol.test.ts +122 -0
  347. package/src/workspace/broker-protocol.ts +317 -0
  348. package/src/workspace/builder-tiers.test.ts +146 -0
  349. package/src/workspace/change-set.schema.json +460 -0
  350. package/src/workspace/chant-migrations.e2e.test.ts +17 -5
  351. package/src/workspace/checks/boxes.test.ts +3 -0
  352. package/src/workspace/checks.ts +10 -3
  353. package/src/workspace/component-environments.test.ts +105 -0
  354. package/src/workspace/composites.schema.json +23 -4
  355. package/src/workspace/composites.test.ts +8 -8
  356. package/src/workspace/composites.ts +13 -2
  357. package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +2 -2
  358. package/src/workspace/conformance/__fixtures__/reference-broker.ts +207 -0
  359. package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +7 -2
  360. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +42 -2
  361. package/src/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
  362. package/src/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
  363. package/src/workspace/conformance/__writer_fixture__/decisions/points.json +96 -0
  364. package/src/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
  365. package/src/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
  366. package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +14 -8
  367. package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +187 -6
  368. package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +25 -0
  369. package/src/workspace/conformance/broker.test.ts +122 -0
  370. package/src/workspace/conformance/broker.ts +792 -0
  371. package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
  372. package/src/workspace/conformance/index.mjs +22 -1
  373. package/src/workspace/conformance/index.ts +5 -1
  374. package/src/workspace/conformance/vitest.ts +33 -1
  375. package/src/workspace/conformance/writer-conformance.e2e.test.ts +49 -3
  376. package/src/workspace/conformance/writer.test.ts +32 -2
  377. package/src/workspace/conformance/writer.ts +339 -28
  378. package/src/workspace/decide.test.ts +208 -4
  379. package/src/workspace/decide.ts +427 -73
  380. package/src/workspace/decision-points.schema.json +11 -4
  381. package/src/workspace/declaration.schema.json +153 -4
  382. package/src/workspace/declaration.ts +141 -0
  383. package/src/workspace/environments.ts +66 -14
  384. package/src/workspace/factory-rules.test.ts +62 -0
  385. package/src/workspace/factory-rules.ts +218 -0
  386. package/src/workspace/graph-cli.ts +2 -2
  387. package/src/workspace/graph.schema.json +67 -0
  388. package/src/workspace/identity.test.ts +23 -0
  389. package/src/workspace/init.ts +82 -2
  390. package/src/workspace/intent-cli.ts +26 -9
  391. package/src/workspace/intent-patch-id.test.ts +162 -0
  392. package/src/workspace/intent-record.schema.json +130 -3
  393. package/src/workspace/intent-record.ts +74 -28
  394. package/src/workspace/intent-reviews.test.ts +76 -0
  395. package/src/workspace/intent-squash.test.ts +213 -0
  396. package/src/workspace/intent-why.test.ts +5 -5
  397. package/src/workspace/intent.schema.json +234 -4
  398. package/src/workspace/intent.ts +260 -66
  399. package/src/workspace/json-edit.test.ts +121 -0
  400. package/src/workspace/json-edit.ts +142 -0
  401. package/src/workspace/jsonc.ts +20 -0
  402. package/src/workspace/kinds.ts +157 -2
  403. package/src/workspace/lexicon-symbols.test.ts +148 -0
  404. package/src/workspace/lexicon-symbols.ts +125 -0
  405. package/src/workspace/lineage-upgrade.test.ts +63 -0
  406. package/src/workspace/lineage-upgrade.ts +146 -5
  407. package/src/workspace/plan-summary.schema.json +155 -0
  408. package/src/workspace/point-answer.schema.json +42 -2
  409. package/src/workspace/points-cli.ts +42 -16
  410. package/src/workspace/points-write.schema.json +162 -4
  411. package/src/workspace/points.schema.json +157 -2
  412. package/src/workspace/points.test.ts +13 -1
  413. package/src/workspace/points.ts +118 -12
  414. package/src/workspace/pr-report.schema.json +95 -0
  415. package/src/workspace/profiles.test.ts +159 -0
  416. package/src/workspace/read-contract.test.ts +2 -2
  417. package/src/workspace/reason-codes.test.ts +13 -1
  418. package/src/workspace/reason-codes.ts +38 -4
  419. package/src/workspace/record-sessions.ts +9 -8
  420. package/src/workspace/records-amend.schema.json +78 -0
  421. package/src/workspace/records-cli.ts +20 -6
  422. package/src/workspace/records-close.schema.json +78 -0
  423. package/src/workspace/records-close.ts +31 -7
  424. package/src/workspace/records-concurrent.e2e.test.ts +135 -0
  425. package/src/workspace/records-concurrent.test.ts +145 -0
  426. package/src/workspace/records-formats.test.ts +46 -10
  427. package/src/workspace/records-new.schema.json +7 -0
  428. package/src/workspace/records-review.schema.json +78 -0
  429. package/src/workspace/records-sessions-write.test.ts +69 -0
  430. package/src/workspace/records-sessions.test.ts +22 -0
  431. package/src/workspace/records-write.ts +192 -25
  432. package/src/workspace/records.schema.json +16 -0
  433. package/src/workspace/records.ts +50 -9
  434. package/src/workspace/reference-kinds/app/package.json +10 -0
  435. package/src/workspace/reference-kinds/app/workspace-kinds.json +45 -0
  436. package/src/workspace/run-statement.schema.json +622 -0
  437. package/src/workspace/runs-cli.ts +343 -14
  438. package/src/workspace/runs-statement.test.ts +239 -0
  439. package/src/workspace/runs-write.schema.json +416 -5
  440. package/src/workspace/runs.schema.json +451 -4
  441. package/src/workspace/runs.test.ts +39 -0
  442. package/src/workspace/runs.ts +296 -35
  443. package/src/workspace/session-kinds.ts +3 -3
  444. package/src/workspace/squash.ts +198 -0
  445. package/src/workspace/status-contract.test.ts +5 -2
  446. package/src/workspace/status-gates.ts +3 -1
  447. package/src/workspace/status.schema.json +126 -3
  448. package/src/workspace/status.ts +90 -1
  449. package/src/workspace/symbols.ts +20 -9
  450. package/src/workspace/trust/run-commits.ts +115 -0
  451. package/src/workspace/trust/run-statement.ts +226 -0
  452. package/src/workspace/trust/seal.ts +8 -2
  453. package/src/workspace/trust/verify-cli.ts +6 -5
  454. package/src/workspace/trust/verify.ts +39 -6
  455. package/src/workspace/wip-cli.ts +97 -0
  456. package/src/workspace/wip-write.schema.json +504 -0
  457. package/src/workspace/wip.schema.json +334 -0
  458. package/src/workspace/wip.test.ts +294 -0
  459. package/src/workspace/wip.ts +753 -0
  460. package/src/workspace/work-cli.ts +3 -0
  461. package/src/workspace/work-evidence.schema.json +3 -0
  462. package/src/workspace/work-size.test.ts +77 -0
  463. package/src/workspace/work-size.ts +93 -0
  464. package/src/workspace/workspace-kinds.schema.json +122 -0
  465. package/src/workspace/write-lock-cli.ts +131 -0
  466. package/src/workspace/write-lock.schema.json +172 -0
  467. package/src/workspace/write-lock.test.ts +189 -0
  468. package/src/workspace/write-lock.ts +474 -0
  469. package/src/workspace/write-scope.test.ts +16 -1
  470. package/src/workspace/write-scope.ts +33 -11
  471. package/src/yaml.ts +31 -21
@@ -660,44 +660,120 @@ export async function getRemoteLifecycleBranchSha(
660
660
  }
661
661
 
662
662
  /**
663
- * Push the state branch to remote with --force-with-lease.
663
+ * Why a push of the state branch did or did not reach the remote.
664
664
  *
665
- * If the remote chant/lifecycle ref has advanced past the local remote-tracking
666
- * SHA captured at the start of this push, the push is rejected and we throw
667
- * StaleLifecycleBranchError so the caller can surface a recovery hint.
665
+ * - `pushed` — the remote now holds the local tip.
666
+ * - `no-remote` — nothing to push to; local is the whole ledger.
667
+ * - `stale` — the remote moved past the tip this checkout last saw (another
668
+ * writer pushed); fetch, re-apply and retry.
669
+ * - `failed` — git refused for another reason (no network, no credentials,
670
+ * a protected branch); `stderr` is git's first words on it.
671
+ */
672
+ export type LifecyclePushStatus =
673
+ | { status: "pushed"; remote: string; commit: string }
674
+ | { status: "no-remote" }
675
+ | { status: "stale"; remote: string; expected: string | null; stderr: string }
676
+ | { status: "failed"; remote: string; stderr: string };
677
+
678
+ /**
679
+ * Point `refs/remotes/<remote>/chant/lifecycle` at `sha`, the remote tip this
680
+ * checkout has just seen (#3391).
668
681
  *
669
- * Returns false (without throwing) only when no remote is configured at all.
682
+ * The push's lease is that ref, and git only keeps it up to date when the
683
+ * remote's configured fetch refspec covers `chant/lifecycle`. A single-branch
684
+ * clone (`git clone --single-branch`, a CI checkout of one ref, a shallow
685
+ * clone) has a refspec for its one branch only, so neither the explicit
686
+ * `chant/lifecycle:chant/lifecycle` fetch nor a successful push touches the
687
+ * tracking ref. The first push of a new branch then succeeds against an empty
688
+ * lease, and every later one is refused as stale against a remote that holds
689
+ * exactly what this checkout last pushed. Recording the tip ourselves, only at
690
+ * the two moments it is known (a fetch that fast-forwarded or found the local
691
+ * branch ahead, and a push that landed), keeps the lease honest without
692
+ * widening the clone's refspec. It is never set from a diverged fetch: that is
693
+ * the case the lease exists to refuse.
670
694
  */
671
- export async function pushLifecycle(opts?: { cwd?: string }): Promise<boolean> {
695
+ async function trackRemoteLifecycle(remote: string, sha: string, cwd?: string): Promise<void> {
672
696
  const rt = getRuntime();
673
- const remoteResult = await rt.spawn(["git", "remote"], { cwd: opts?.cwd });
674
- if (remoteResult.exitCode !== 0 || !remoteResult.stdout.trim()) return false;
697
+ await rt.spawn(["git", "update-ref", `refs/remotes/${remote}/${STATE_BRANCH}`, sha], { cwd, env: C_LOCALE_ENV });
698
+ }
675
699
 
676
- const remote = remoteResult.stdout.trim().split("\n")[0];
700
+ /**
701
+ * Push the state branch to the ledger remote with --force-with-lease and say
702
+ * what happened (#3391). Never throws for a git refusal; {@link pushLifecycle}
703
+ * is the throwing form most callers use.
704
+ */
705
+ export async function pushLifecycleStatus(opts?: { cwd?: string }): Promise<LifecyclePushStatus> {
706
+ const rt = getRuntime();
707
+ const cwd = opts?.cwd;
708
+ const found = await ledgerRemote(cwd);
709
+ if (found === null) return { status: "no-remote" };
710
+ if ("failed" in found) return { status: "failed", remote: "(unknown)", stderr: found.stderr };
711
+ const { remote } = found;
677
712
 
678
713
  // Capture the lease SHA — if null, the remote ref doesn't exist yet
679
714
  // (first-time push) and we send `--force-with-lease=ref:` (empty SHA),
680
715
  // which git interprets as "ref does not exist on remote".
681
716
  const expected = await getRemoteLifecycleBranchSha(remote, opts);
682
717
  const lease = `refs/heads/${STATE_BRANCH}:${expected ?? ""}`;
718
+ const tip = await getStateBranchTip(cwd);
683
719
 
684
720
  const pushResult = await rt.spawn(
685
721
  ["git", "push", `--force-with-lease=${lease}`, remote, `${STATE_BRANCH}:${STATE_BRANCH}`],
686
- { cwd: opts?.cwd },
722
+ { cwd, env: C_LOCALE_ENV },
687
723
  );
688
724
 
689
725
  if (pushResult.exitCode !== 0) {
690
- const stderr = pushResult.stderr ?? "";
726
+ const stderr = (pushResult.stderr ?? "").trim();
691
727
  if (
692
728
  stderr.includes("stale info") ||
693
729
  stderr.includes("rejected") ||
694
730
  stderr.includes("non-fast-forward")
695
731
  ) {
696
- throw new StaleLifecycleBranchError(expected, stderr);
732
+ return { status: "stale", remote, expected, stderr };
697
733
  }
698
- return false;
734
+ return { status: "failed", remote, stderr };
735
+ }
736
+ if (tip) await trackRemoteLifecycle(remote, tip, cwd);
737
+ return { status: "pushed", remote, commit: tip ?? "" };
738
+ }
739
+
740
+ /**
741
+ * Push the state branch to remote with --force-with-lease.
742
+ *
743
+ * If the remote chant/lifecycle ref has advanced past the local remote-tracking
744
+ * SHA captured at the start of this push, the push is rejected and we throw
745
+ * StaleLifecycleBranchError so the caller can surface a recovery hint.
746
+ *
747
+ * Returns false (without throwing) when no remote is configured, or when git
748
+ * refused the push for a reason other than a stale lease.
749
+ *
750
+ * The remote is the ledger's ({@link ledgerRemote}: `origin` when there is one),
751
+ * the same one {@link fetchLifecycleStatus} reads, and a push that lands
752
+ * records the tip it pushed as the remote-tracking ref, so the next push's
753
+ * lease is right in a single-branch clone too (#3391).
754
+ */
755
+ export async function pushLifecycle(opts?: { cwd?: string }): Promise<boolean> {
756
+ const result = await pushLifecycleStatus(opts);
757
+ if (result.status === "stale") throw new StaleLifecycleBranchError(result.expected, result.stderr);
758
+ return result.status === "pushed";
759
+ }
760
+
761
+ /**
762
+ * One line on why a push did not land, for a write document's `notPushed`
763
+ * (#3391), or null when it did.
764
+ */
765
+ export function describeLifecyclePush(result: LifecyclePushStatus): string | null {
766
+ const said = (stderr: string) => (stderr ? `; git said: ${stderr.split("\n").find((l) => l.trim()) ?? stderr}` : "");
767
+ switch (result.status) {
768
+ case "pushed":
769
+ return null;
770
+ case "no-remote":
771
+ return `this repository has no remote, so ${STATE_BRANCH} stays local`;
772
+ case "stale":
773
+ return `"${result.remote}" holds ${STATE_BRANCH} commits this checkout has not fetched, so the push was refused; fetch ${STATE_BRANCH} and write again${said(result.stderr)}`;
774
+ case "failed":
775
+ return `the push of ${STATE_BRANCH} to "${result.remote}" failed${said(result.stderr)}`;
699
776
  }
700
- return true;
701
777
  }
702
778
 
703
779
  /**
@@ -814,7 +890,12 @@ export async function fetchLifecycleStatus(opts?: { cwd?: string }): Promise<Lif
814
890
  ["git", "fetch", remote, `${STATE_BRANCH}:${STATE_BRANCH}`],
815
891
  { cwd, env: C_LOCALE_ENV },
816
892
  );
817
- if (fetchResult.exitCode === 0) return { status: "fetched" };
893
+ if (fetchResult.exitCode === 0) {
894
+ // The local branch is now the remote's tip; record it for the push's lease (#3391).
895
+ const tip = await getStateBranchTip(cwd);
896
+ if (tip) await trackRemoteLifecycle(remote, tip, cwd);
897
+ return { status: "fetched" };
898
+ }
818
899
 
819
900
  const stderr = (fetchResult.stderr ?? "").trim();
820
901
  if (NO_SUCH_REMOTE_REF_RE.test(stderr)) return { status: "absent", remote };
@@ -834,8 +915,13 @@ export async function fetchLifecycleStatus(opts?: { cwd?: string }): Promise<Lif
834
915
  ["git", "merge-base", "--is-ancestor", PROBE_REF, `refs/heads/${STATE_BRANCH}`],
835
916
  { cwd, env: C_LOCALE_ENV },
836
917
  );
918
+ const remoteTip = contains.exitCode === 0 ? (await rt.spawn(["git", "rev-parse", "--verify", PROBE_REF], { cwd, env: C_LOCALE_ENV })).stdout.trim() : "";
837
919
  await rt.spawn(["git", "update-ref", "-d", PROBE_REF], { cwd, env: C_LOCALE_ENV });
838
- if (contains.exitCode === 0) return { status: "ahead", remote };
920
+ if (contains.exitCode === 0) {
921
+ // Ahead of the remote: its tip is in our history, so it is the right lease (#3391).
922
+ if (remoteTip) await trackRemoteLifecycle(remote, remoteTip, cwd);
923
+ return { status: "ahead", remote };
924
+ }
839
925
  return { status: "diverged", remote, stderr };
840
926
  }
841
927
 
@@ -162,6 +162,25 @@ describe("cross-lexicon activity contracts (#2101)", () => {
162
162
  });
163
163
  });
164
164
 
165
+ describe("telemetry attribution (#3375)", () => {
166
+ const readsTelemetry: PostSynthCheck = {
167
+ id: "TELEMETRY",
168
+ description: "reports the attribution the context carries",
169
+ check(ctx) {
170
+ return [{ checkId: "TELEMETRY", severity: "warning", message: JSON.stringify(ctx.telemetry ?? null) }];
171
+ },
172
+ };
173
+
174
+ test("the build's attribution is threaded into the context", () => {
175
+ const diags = runPostSynthChecks([readsTelemetry], createBuildResult(), undefined, { telemetry: { workspace: "acme" } });
176
+ expect(diags[0].message).toBe('{"workspace":"acme"}');
177
+ });
178
+
179
+ test("a level-0 run leaves the field undefined", () => {
180
+ expect(runPostSynthChecks([readsTelemetry], createBuildResult())[0].message).toBe("null");
181
+ });
182
+ });
183
+
165
184
  describe("isPostSynthCheck", () => {
166
185
  test("accepts a well-formed check and rejects others", async () => {
167
186
  const { isPostSynthCheck } = await import("./post-synth");
@@ -1,6 +1,7 @@
1
1
  import type { Declarable } from "../declarable";
2
2
  import type { ActivityContract } from "../op/activity-contract";
3
3
  import type { SerializerResult } from "../serializer";
4
+ import type { TelemetryAttribution } from "../telemetry-attribution";
4
5
  import type { Severity } from "./rule";
5
6
  import { parseOutputDocs, type OutputDoc } from "./output-docs";
6
7
 
@@ -55,6 +56,13 @@ export interface PostSynthContext {
55
56
  * when it is `undefined`.
56
57
  */
57
58
  readonly activityContracts?: ReadonlyMap<string, ActivityContract>;
59
+ /**
60
+ * The telemetry attribution the build stamped (#2558), from
61
+ * `resolveTelemetryAttribution`. Set when the project is inside a workspace
62
+ * or sets `telemetry.attribution: true`, and absent at level 0 and from a
63
+ * hand-built context. OTEL118 runs only when it is set.
64
+ */
65
+ readonly telemetry?: TelemetryAttribution;
58
66
  /** Raw build result object */
59
67
  buildResult: {
60
68
  outputs: Map<string, string | SerializerResult>;
@@ -183,6 +191,14 @@ export interface PostSynthCheck {
183
191
  description: string;
184
192
  /** Execute the check and return diagnostics */
185
193
  check(ctx: PostSynthContext): PostSynthDiagnostic[];
194
+ /**
195
+ * `chant audit` skips this check unless the project enables it with a
196
+ * `lint.rules` entry for its id (chant #3190). Builds are not affected: they
197
+ * already filter by the lexicon's `lintPresets()`. It exists because the
198
+ * audit runs every check a lexicon ships, report-only ones included, so a
199
+ * new check would otherwise change the audit output of every project.
200
+ */
201
+ auditOptIn?: boolean;
186
202
  }
187
203
 
188
204
  /** Structural type guard — used to collect project-authored policy checks. */
@@ -201,12 +217,13 @@ export function isPostSynthCheck(value: unknown): value is PostSynthCheck {
201
217
  * into the context so a check can branch on the current environment/stack, and
202
218
  * `opts.activityContracts` (chant #2101) carries the build's cross-lexicon
203
219
  * activity-contract map to the checks that validate Op steps against it.
220
+ * `opts.telemetry` is the build's resolved telemetry attribution.
204
221
  */
205
222
  export function runPostSynthChecks(
206
223
  checks: PostSynthCheck[],
207
224
  buildResult: PostSynthContext["buildResult"],
208
225
  env?: string,
209
- opts?: { activityContracts?: ReadonlyMap<string, ActivityContract> },
226
+ opts?: { activityContracts?: ReadonlyMap<string, ActivityContract>; telemetry?: TelemetryAttribution },
210
227
  ): PostSynthDiagnostic[] {
211
228
  const getDocs = createDocsAccessor(buildResult.outputs);
212
229
  const ctx: PostSynthContext = {
@@ -215,6 +232,7 @@ export function runPostSynthChecks(
215
232
  env,
216
233
  buildResult,
217
234
  ...(opts?.activityContracts ? { activityContracts: opts.activityContracts } : {}),
235
+ ...(opts?.telemetry ? { telemetry: opts.telemetry } : {}),
218
236
  get docs(): OutputDoc[] {
219
237
  return getDocs();
220
238
  },
@@ -43,7 +43,7 @@ const DELIBERATELY_UNDECLARED = new Map<string, string>([
43
43
  [
44
44
  "@cdktn/hcl2json",
45
45
  "carries a ~1.8 MB wasm blob and is only needed by `chant carve`; " +
46
- "`terraform/parse.ts` catches the failed import and prints the install line",
46
+ "`terraform/hcl2json.ts` catches the failed import and prints the install line",
47
47
  ],
48
48
  [
49
49
  "@intentius/chant-lexicon-gitlab",
@@ -252,6 +252,29 @@ export const sourceArchiveContract = activityContract(
252
252
  z.object({ digest: z.string(), archive: z.string(), commit: z.string(), dir: z.string(), files: z.number(), bytes: z.number() }),
253
253
  );
254
254
 
255
+ const changeSetPart = z.object({
256
+ member: z.object({ member: z.string(), planDigest: z.string().nullable() }).passthrough(),
257
+ entries: z.array(z.unknown()),
258
+ });
259
+
260
+ export const composeChangeSetContract = activityContract(
261
+ "composeChangeSet",
262
+ z.strictObject({ parts: z.array(changeSetPart) }),
263
+ z.object({ document: z.record(z.string(), z.unknown()), digest: z.string(), summary: z.record(z.string(), z.unknown()) }),
264
+ );
265
+
266
+ export const lifecyclePlanChangeSetContract = activityContract(
267
+ "lifecyclePlanChangeSet",
268
+ z.strictObject({ member: z.string(), env: z.string(), cwd: z.string().optional(), owned: z.boolean().optional() }),
269
+ z.object({ part: changeSetPart }),
270
+ );
271
+
272
+ export const readChangeSetPartContract = activityContract(
273
+ "readChangeSetPart",
274
+ z.strictObject({ member: z.string(), planner: z.enum(["chant", "warden"]), file: z.string(), lexicon: z.string().optional() }),
275
+ z.object({ part: changeSetPart }),
276
+ );
277
+
255
278
  export const releasePlanContract = activityContract(
256
279
  "releasePlan",
257
280
  z.strictObject({
@@ -356,3 +379,64 @@ export const releaseRollbackRecordContract = activityContract(
356
379
  }),
357
380
  z.object({ recorded: z.boolean(), digest: z.string(), env: z.string(), component: z.string(), approver: z.string().nullable() }),
358
381
  );
382
+
383
+ /**
384
+ * The factory reference Op's activities (#3406, ws-087, ./factory.ts).
385
+ * OPS013 checks a later step's reference into each result, so `factoryOp`'s
386
+ * wiring (ask into build, build into check, all three into record) is
387
+ * validated at `chant build`.
388
+ */
389
+ const factoryLease = z.unknown();
390
+ const factoryBackends = z.record(z.string(), backendSchema).optional();
391
+
392
+ export const factoryPickContract = activityContract(
393
+ "factoryPick",
394
+ z.strictObject({ cwd: z.string().optional(), kind: z.string().optional() }),
395
+ z.strictObject({
396
+ candidates: z.array(z.string()),
397
+ held: z.array(z.object({ item: z.string(), hold: z.string(), message: z.string() })),
398
+ keys: z.array(z.string()),
399
+ }),
400
+ );
401
+
402
+ export const factoryReadyContract = activityContract(
403
+ "factoryReady",
404
+ z.strictObject({ cwd: z.string().optional(), kind: z.string().optional(), also: z.string().optional() }),
405
+ z.array(z.string()),
406
+ );
407
+
408
+ export const factoryAskContract = activityContract(
409
+ "factoryAsk",
410
+ z.strictObject({ lease: factoryLease, cwd: z.string().optional(), kind: z.string().optional(), backends: factoryBackends }),
411
+ z.strictObject({
412
+ item: z.string(),
413
+ ask: z.boolean(),
414
+ tier: z.string(),
415
+ understand: z.string().nullable(),
416
+ outcome: z.enum(["dropped", "redraft", "ask"]).nullable(),
417
+ }),
418
+ );
419
+
420
+ export const factoryBuildContract = activityContract(
421
+ "factoryBuild",
422
+ z.strictObject({ lease: factoryLease, ask: z.unknown(), builder: z.string().min(1), context: z.string().optional(), cwd: z.string().optional() }),
423
+ z.strictObject({ ran: z.boolean(), finished: z.boolean(), exitCode: z.number().nullable(), reverted: z.array(z.string()), note: z.string(), report: z.record(z.string(), z.unknown()).nullable() }),
424
+ );
425
+
426
+ export const factoryCheckContract = activityContract(
427
+ "factoryCheck",
428
+ z.strictObject({ lease: factoryLease, build: z.unknown(), check: z.string().optional(), cwd: z.string().optional() }),
429
+ z.strictObject({ ran: z.boolean(), ok: z.boolean(), command: z.string().nullable(), evidence: z.array(z.string()), log: z.string().nullable(), report: z.record(z.string(), z.unknown()).nullable() }),
430
+ );
431
+
432
+ export const factoryRecordContract = activityContract(
433
+ "factoryRecord",
434
+ z.strictObject({ lease: factoryLease, ask: z.unknown(), build: z.unknown(), check: z.unknown(), after: z.string().optional(), cwd: z.string().optional() }),
435
+ z.strictObject({
436
+ outcome: z.enum(["done", "not_done", "dropped", "redraft", "ask"]),
437
+ reason: z.string().nullable(),
438
+ commit: z.string().nullable(),
439
+ implementsProposed: z.array(z.object({ decision: z.string(), paths: z.array(z.string()) })),
440
+ }),
441
+ );
442
+
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Op activities for the change-set document (#3181).
3
+ *
4
+ * A combined run over several members plans each one in its own step, joins
5
+ * the parts with {@link composeChangeSet}, and binds its gate to the
6
+ * document's digest:
7
+ *
8
+ * ```ts
9
+ * phase("Plan", [
10
+ * terraformPlan({ root: "estate", id: "estate" }), // a choudoufu member
11
+ * lifecyclePlanChangeSet({ member: "delivery", env: "prod", cwd: "delivery", id: "delivery" }),
12
+ * readChangeSetPart({ member: "warden", planner: "warden", file: "warden.plan.json", id: "warden" }),
13
+ * composeChangeSet({ parts: [estate.out.changeSet, delivery.out.part, warden.out.part], id: "change-set" }),
14
+ * ]),
15
+ * phase("Approve", [gate("approve-run", { plan: stepOutput("change-set", "digest") })]),
16
+ * ```
17
+ *
18
+ * Each activity here only reads: a plan, a file, or the parts it is handed.
19
+ */
20
+
21
+ import { exec } from "node:child_process";
22
+ import { readFileSync } from "node:fs";
23
+ import { resolve } from "node:path";
24
+ import { promisify } from "node:util";
25
+ import {
26
+ composeChangeSet as compose,
27
+ lifecyclePlanPart,
28
+ reconcilePlanPart,
29
+ type ChangeSetDocument,
30
+ type ChangeSetPart,
31
+ type ChangeSetSummary,
32
+ } from "../../change-set";
33
+ import type { ChangeSet as LifecycleChangeSet } from "../../lifecycle/change-set";
34
+ import type { ChangeSet as ReconcileChangeSet } from "../../reconcile";
35
+ import { CHANT_VERSION } from "../../cli/version";
36
+
37
+ const execAsync = promisify(exec);
38
+
39
+ export interface ComposeChangeSetArgs {
40
+ /** One part per member, usually step outputs: `terraformPlan`'s `changeSet`, the other activities' `part`. */
41
+ parts: ChangeSetPart[];
42
+ }
43
+
44
+ export interface ComposeChangeSetResult {
45
+ document: ChangeSetDocument;
46
+ /** The document's digest. A gate's `plan` binds it. */
47
+ digest: string;
48
+ summary: ChangeSetSummary;
49
+ }
50
+
51
+ /** Join members' parts into one change-set document. Fails when two parts name the same member. */
52
+ export async function composeChangeSet(args: ComposeChangeSetArgs): Promise<ComposeChangeSetResult> {
53
+ const parts = args.parts ?? [];
54
+ const missing = parts.findIndex((p) => !p || typeof p !== "object" || !p.member);
55
+ if (missing !== -1) throw new Error(`composeChangeSet: parts[${missing}] is not a change-set part; did the step that plans it run?`);
56
+ const document = compose(parts, { chant: CHANT_VERSION });
57
+ return { document, digest: document.digest, summary: document.summary };
58
+ }
59
+
60
+ export interface LifecyclePlanChangeSetArgs {
61
+ /** The workspace member the plan is of. */
62
+ member: string;
63
+ env: string;
64
+ /** The member's directory. Default: the current directory. */
65
+ cwd?: string;
66
+ /** Pass `--owned`, so owned resources missing from source plan as deletes. */
67
+ owned?: boolean;
68
+ }
69
+
70
+ export interface ChangeSetPartResult {
71
+ part: ChangeSetPart;
72
+ }
73
+
74
+ function shellQuote(s: string): string {
75
+ return `'${s.replace(/'/g, `'\\''`)}'`;
76
+ }
77
+
78
+ /** `chant lifecycle plan <env> --json` in a chant member's directory, as that member's change-set part. */
79
+ export async function lifecyclePlanChangeSet(args: LifecyclePlanChangeSetArgs, signal?: AbortSignal): Promise<ChangeSetPartResult> {
80
+ const { stdout } = await execAsync(`chant lifecycle plan ${shellQuote(args.env)}${args.owned ? " --owned" : ""} --json`, {
81
+ cwd: args.cwd ? resolve(args.cwd) : process.cwd(),
82
+ signal,
83
+ maxBuffer: 64 * 1024 * 1024,
84
+ });
85
+ return { part: lifecyclePlanPart({ member: args.member, plan: JSON.parse(stdout) as LifecycleChangeSet }) };
86
+ }
87
+
88
+ export interface ReadChangeSetPartArgs {
89
+ member: string;
90
+ /** Which planner wrote the file: `chant` (`lifecycle plan --json`) or `warden` (its reconcile change sets). */
91
+ planner: "chant" | "warden";
92
+ /** The plan file, relative to the current directory. */
93
+ file: string;
94
+ /** The lexicon the entries belong to, when the plan does not say. */
95
+ lexicon?: string;
96
+ }
97
+
98
+ /**
99
+ * A plan another step or job wrote to a file, as a member's part. A warden
100
+ * plan is the reconcile `ChangeSet` (`@intentius/chant/reconcile`), or an
101
+ * array of them, one per cycle and org.
102
+ */
103
+ export async function readChangeSetPart(args: ReadChangeSetPartArgs): Promise<ChangeSetPartResult> {
104
+ const plan: unknown = JSON.parse(readFileSync(resolve(args.file), "utf-8"));
105
+ const lexicon = args.lexicon !== undefined ? { lexicon: args.lexicon } : {};
106
+ switch (args.planner) {
107
+ case "chant":
108
+ return { part: lifecyclePlanPart({ member: args.member, plan: plan as LifecycleChangeSet, ...lexicon }) };
109
+ case "warden":
110
+ return { part: reconcilePlanPart({ member: args.member, plan: plan as ReconcileChangeSet | ReconcileChangeSet[], ...lexicon }) };
111
+ default:
112
+ throw new Error(`readChangeSetPart: no adapter for planner ${String(args.planner)}; terraform-family plans come from terraformPlan's changeSet`);
113
+ }
114
+ }
@@ -84,6 +84,8 @@ export interface DecideResult {
84
84
  backend: string | null;
85
85
  confidence: number | null;
86
86
  threshold: number | null;
87
+ /** For a model's answer: the model's own explanation of it, when the backend gave one (#3345). */
88
+ reason: string | null;
87
89
  /** The people who answered, when people did. */
88
90
  answeredBy: string[];
89
91
  /** Each decider that was asked and did not answer, and why. */
@@ -159,6 +161,7 @@ function result(q: QuestionView, missing: string[]): DecideResult {
159
161
  backend: byModel ? (q.model?.backend ?? null) : null,
160
162
  confidence: byModel ? q.confidence : null,
161
163
  threshold: byModel ? q.threshold : null,
164
+ reason: byModel ? q.reason : null,
162
165
  answeredBy: q.answeredBy,
163
166
  escalations: q.escalations,
164
167
  missing,