@intentius/chant 0.124.28 → 0.124.30

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 (449) hide show
  1. package/dist/build-params.d.ts +34 -1
  2. package/dist/build-params.d.ts.map +1 -1
  3. package/dist/build-params.js +31 -1
  4. package/dist/change-set-graph.d.ts +8 -1
  5. package/dist/change-set-graph.d.ts.map +1 -1
  6. package/dist/change-set-graph.js +4 -2
  7. package/dist/change-set.d.ts +10 -0
  8. package/dist/change-set.d.ts.map +1 -1
  9. package/dist/change-set.js +4 -3
  10. package/dist/cli/build-params-cli.d.ts +7 -1
  11. package/dist/cli/build-params-cli.d.ts.map +1 -1
  12. package/dist/cli/build-params-cli.js +17 -8
  13. package/dist/cli/commands/build.d.ts.map +1 -1
  14. package/dist/cli/commands/build.js +6 -3
  15. package/dist/cli/handlers/approve.d.ts.map +1 -1
  16. package/dist/cli/handlers/approve.js +45 -0
  17. package/dist/cli/handlers/build.d.ts.map +1 -1
  18. package/dist/cli/handlers/build.js +18 -4
  19. package/dist/cli/handlers/fan-out.d.ts +9 -0
  20. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  21. package/dist/cli/handlers/fan-out.js +67 -8
  22. package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
  23. package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
  24. package/dist/cli/handlers/lifecycle-gc.js +143 -0
  25. package/dist/cli/handlers/pr.d.ts +11 -3
  26. package/dist/cli/handlers/pr.d.ts.map +1 -1
  27. package/dist/cli/handlers/pr.js +134 -26
  28. package/dist/cli/handlers/promote.d.ts +7 -0
  29. package/dist/cli/handlers/promote.d.ts.map +1 -1
  30. package/dist/cli/handlers/promote.js +210 -33
  31. package/dist/cli/handlers/release.d.ts +62 -5
  32. package/dist/cli/handlers/release.d.ts.map +1 -1
  33. package/dist/cli/handlers/release.js +414 -28
  34. package/dist/cli/handlers/run.d.ts +11 -1
  35. package/dist/cli/handlers/run.d.ts.map +1 -1
  36. package/dist/cli/handlers/run.js +67 -6
  37. package/dist/cli/main.d.ts.map +1 -1
  38. package/dist/cli/main.js +91 -10
  39. package/dist/cli/parse-args.d.ts.map +1 -1
  40. package/dist/cli/parse-args.js +73 -2
  41. package/dist/cli/registry.d.ts +32 -4
  42. package/dist/cli/registry.d.ts.map +1 -1
  43. package/dist/cli/run-events.d.ts +3 -2
  44. package/dist/cli/run-events.d.ts.map +1 -1
  45. package/dist/cli/run-events.js +5 -3
  46. package/dist/components/auto-release.d.ts +2 -0
  47. package/dist/components/auto-release.d.ts.map +1 -1
  48. package/dist/components/auto-release.js +1 -0
  49. package/dist/components/capability.d.ts +9 -0
  50. package/dist/components/capability.d.ts.map +1 -1
  51. package/dist/components/driver.d.ts +8 -0
  52. package/dist/components/driver.d.ts.map +1 -1
  53. package/dist/components/driver.js +35 -0
  54. package/dist/components/fan-out-inputs.d.ts +69 -0
  55. package/dist/components/fan-out-inputs.d.ts.map +1 -0
  56. package/dist/components/fan-out-inputs.js +169 -0
  57. package/dist/components/fan-out-record.d.ts +13 -0
  58. package/dist/components/fan-out-record.d.ts.map +1 -1
  59. package/dist/components/fan-out-record.js +16 -0
  60. package/dist/components/fan-out-run.d.ts.map +1 -1
  61. package/dist/components/fan-out-run.js +50 -0
  62. package/dist/components/fan-out.d.ts +4 -0
  63. package/dist/components/fan-out.d.ts.map +1 -1
  64. package/dist/components/pr-pipeline.d.ts +49 -0
  65. package/dist/components/pr-pipeline.d.ts.map +1 -1
  66. package/dist/components/pr-pipeline.js +38 -4
  67. package/dist/components/pr-run.d.ts +20 -2
  68. package/dist/components/pr-run.d.ts.map +1 -1
  69. package/dist/components/pr-run.js +26 -13
  70. package/dist/components/promote.d.ts +23 -0
  71. package/dist/components/promote.d.ts.map +1 -1
  72. package/dist/components/promote.js +60 -2
  73. package/dist/components/release-compile.d.ts +9 -0
  74. package/dist/components/release-compile.d.ts.map +1 -1
  75. package/dist/components/release-compile.js +9 -3
  76. package/dist/components/release-gate.d.ts +11 -1
  77. package/dist/components/release-gate.d.ts.map +1 -1
  78. package/dist/components/release-gate.js +10 -4
  79. package/dist/components/wave-plan.d.ts +16 -0
  80. package/dist/components/wave-plan.d.ts.map +1 -1
  81. package/dist/components/wave-plan.js +36 -2
  82. package/dist/config.d.ts +38 -0
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js +2 -0
  85. package/dist/flag-params.d.ts +54 -0
  86. package/dist/flag-params.d.ts.map +1 -0
  87. package/dist/flag-params.js +130 -0
  88. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  89. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  90. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  91. package/dist/lifecycle/change-set-store.d.ts +2 -1
  92. package/dist/lifecycle/change-set-store.d.ts.map +1 -1
  93. package/dist/lifecycle/change-set-store.js +2 -1
  94. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  95. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  96. package/dist/lifecycle/delivery-metrics.js +145 -0
  97. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  98. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  99. package/dist/lifecycle/deploy-outcome.js +101 -0
  100. package/dist/lifecycle/gate-change-set.d.ts +10 -1
  101. package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
  102. package/dist/lifecycle/gate-change-set.js +35 -3
  103. package/dist/lifecycle/gate-ledger.d.ts +2 -3
  104. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  105. package/dist/lifecycle/git.d.ts +30 -1
  106. package/dist/lifecycle/git.d.ts.map +1 -1
  107. package/dist/lifecycle/git.js +31 -5
  108. package/dist/lifecycle/index.d.ts +4 -0
  109. package/dist/lifecycle/index.d.ts.map +1 -1
  110. package/dist/lifecycle/index.js +4 -0
  111. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  112. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  113. package/dist/lifecycle/mixed-environment.js +154 -0
  114. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  115. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  116. package/dist/lifecycle/publish-attachments.js +179 -0
  117. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  118. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  119. package/dist/lifecycle/publish-credentials.js +69 -0
  120. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  121. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  122. package/dist/lifecycle/publish-declaration.js +83 -0
  123. package/dist/lifecycle/publish-forge.d.ts +53 -0
  124. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  125. package/dist/lifecycle/publish-forge.js +298 -0
  126. package/dist/lifecycle/publish-npm.d.ts +61 -0
  127. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  128. package/dist/lifecycle/publish-npm.js +242 -0
  129. package/dist/lifecycle/publish-oci.d.ts +64 -0
  130. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  131. package/dist/lifecycle/publish-oci.js +383 -0
  132. package/dist/lifecycle/publish-record.d.ts +66 -0
  133. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  134. package/dist/lifecycle/publish-record.js +84 -0
  135. package/dist/lifecycle/publish-targets.d.ts +45 -0
  136. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  137. package/dist/lifecycle/publish-targets.js +85 -0
  138. package/dist/lifecycle/release-cut.d.ts +48 -1
  139. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  140. package/dist/lifecycle/release-cut.js +116 -23
  141. package/dist/lifecycle/release-join.d.ts +95 -0
  142. package/dist/lifecycle/release-join.d.ts.map +1 -0
  143. package/dist/lifecycle/release-join.js +139 -0
  144. package/dist/lifecycle/release-ledger.d.ts +8 -3
  145. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  146. package/dist/lifecycle/release-ledger.js +2 -0
  147. package/dist/lifecycle/release-publish.d.ts +312 -0
  148. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  149. package/dist/lifecycle/release-publish.js +335 -0
  150. package/dist/lifecycle/release-revocation.d.ts +80 -0
  151. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  152. package/dist/lifecycle/release-revocation.js +123 -0
  153. package/dist/lifecycle/release-store.d.ts +12 -2
  154. package/dist/lifecycle/release-store.d.ts.map +1 -1
  155. package/dist/lifecycle/release-store.js +4 -2
  156. package/dist/lifecycle/release-verify.d.ts +61 -0
  157. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  158. package/dist/lifecycle/release-verify.js +92 -0
  159. package/dist/lifecycle/release-workstation.d.ts +91 -0
  160. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  161. package/dist/lifecycle/release-workstation.js +180 -0
  162. package/dist/lifecycle/retention-policy.d.ts +45 -0
  163. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  164. package/dist/lifecycle/retention-policy.js +43 -0
  165. package/dist/lifecycle/retention.d.ts +185 -0
  166. package/dist/lifecycle/retention.d.ts.map +1 -0
  167. package/dist/lifecycle/retention.js +400 -0
  168. package/dist/lifecycle/run-ledger.d.ts +1 -1
  169. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  170. package/dist/lifecycle/run-ledger.js +9 -0
  171. package/dist/op/activities/activity-contracts.d.ts +1 -0
  172. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  173. package/dist/op/activities/activity-contracts.js +1 -0
  174. package/dist/op/activities/shell.d.ts +6 -0
  175. package/dist/op/activities/shell.d.ts.map +1 -1
  176. package/dist/op/activities/shell.js +1 -1
  177. package/dist/op/local-executor.d.ts +23 -1
  178. package/dist/op/local-executor.d.ts.map +1 -1
  179. package/dist/op/local-executor.js +9 -3
  180. package/dist/op/runtime.d.ts +27 -0
  181. package/dist/op/runtime.d.ts.map +1 -1
  182. package/dist/op/runtime.js +20 -0
  183. package/dist/plan-summary.d.ts.map +1 -1
  184. package/dist/plan-summary.js +8 -2
  185. package/dist/pr-forge.d.ts +23 -0
  186. package/dist/pr-forge.d.ts.map +1 -1
  187. package/dist/pr-forge.js +44 -0
  188. package/dist/pr-loop.d.ts +17 -0
  189. package/dist/pr-loop.d.ts.map +1 -1
  190. package/dist/pr-loop.js +30 -4
  191. package/dist/pr-report-url.d.ts +82 -0
  192. package/dist/pr-report-url.d.ts.map +1 -0
  193. package/dist/pr-report-url.js +174 -0
  194. package/dist/provenance.d.ts +2 -2
  195. package/dist/provenance.d.ts.map +1 -1
  196. package/dist/run-event-store.d.ts +29 -2
  197. package/dist/run-event-store.d.ts.map +1 -1
  198. package/dist/run-event-store.js +50 -3
  199. package/dist/run-event-stream.d.ts +4 -3
  200. package/dist/run-event-stream.d.ts.map +1 -1
  201. package/dist/run-event-stream.js +7 -3
  202. package/dist/run-event.d.ts +34 -1
  203. package/dist/run-event.d.ts.map +1 -1
  204. package/dist/run-event.js +4 -0
  205. package/dist/wave-runtime.mjs +260 -229
  206. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  207. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  208. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  209. package/dist/workspace/change-set.schema.json +5 -1
  210. package/dist/workspace/check.schema.json +4 -2
  211. package/dist/workspace/checks/records.d.ts +9 -1
  212. package/dist/workspace/checks/records.d.ts.map +1 -1
  213. package/dist/workspace/checks/records.js +35 -1
  214. package/dist/workspace/checks.d.ts +1 -0
  215. package/dist/workspace/checks.d.ts.map +1 -1
  216. package/dist/workspace/checks.js +2 -1
  217. package/dist/workspace/ci-green-forge.d.ts +2 -0
  218. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  219. package/dist/workspace/ci-green.d.ts +10 -0
  220. package/dist/workspace/ci-green.d.ts.map +1 -1
  221. package/dist/workspace/ci-green.js +14 -5
  222. package/dist/workspace/ci-run.d.ts +222 -0
  223. package/dist/workspace/ci-run.d.ts.map +1 -0
  224. package/dist/workspace/ci-run.js +464 -0
  225. package/dist/workspace/ci-serve.d.ts +214 -0
  226. package/dist/workspace/ci-serve.d.ts.map +1 -0
  227. package/dist/workspace/ci-serve.js +412 -0
  228. package/dist/workspace/declaration.d.ts +16 -0
  229. package/dist/workspace/declaration.d.ts.map +1 -1
  230. package/dist/workspace/declaration.js +26 -0
  231. package/dist/workspace/declaration.schema.json +64 -0
  232. package/dist/workspace/declared-kinds.d.ts +16 -0
  233. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  234. package/dist/workspace/declared-kinds.js +25 -0
  235. package/dist/workspace/delivery-metrics.d.ts +91 -0
  236. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  237. package/dist/workspace/delivery-metrics.js +217 -0
  238. package/dist/workspace/delivery-metrics.schema.json +519 -0
  239. package/dist/workspace/flags-read.d.ts +37 -0
  240. package/dist/workspace/flags-read.d.ts.map +1 -0
  241. package/dist/workspace/flags-read.js +103 -0
  242. package/dist/workspace/flags.d.ts +100 -0
  243. package/dist/workspace/flags.d.ts.map +1 -0
  244. package/dist/workspace/flags.js +98 -0
  245. package/dist/workspace/graph-cli.js +1 -1
  246. package/dist/workspace/intent-cli.d.ts +11 -2
  247. package/dist/workspace/intent-cli.d.ts.map +1 -1
  248. package/dist/workspace/intent-cli.js +54 -5
  249. package/dist/workspace/intent-trace.d.ts +219 -0
  250. package/dist/workspace/intent-trace.d.ts.map +1 -0
  251. package/dist/workspace/intent-trace.js +314 -0
  252. package/dist/workspace/intent.d.ts +30 -6
  253. package/dist/workspace/intent.d.ts.map +1 -1
  254. package/dist/workspace/intent.js +141 -21
  255. package/dist/workspace/intent.schema.json +702 -6
  256. package/dist/workspace/overlap.d.ts +146 -0
  257. package/dist/workspace/overlap.d.ts.map +1 -0
  258. package/dist/workspace/overlap.js +276 -0
  259. package/dist/workspace/pr-report.schema.json +5 -0
  260. package/dist/workspace/reason-codes.d.ts +2 -0
  261. package/dist/workspace/reason-codes.d.ts.map +1 -1
  262. package/dist/workspace/reason-codes.js +3 -0
  263. package/dist/workspace/records.d.ts +15 -2
  264. package/dist/workspace/records.d.ts.map +1 -1
  265. package/dist/workspace/records.js +32 -0
  266. package/dist/workspace/records.schema.json +21 -2
  267. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  268. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  269. package/dist/workspace/releases.d.ts +9 -0
  270. package/dist/workspace/releases.d.ts.map +1 -1
  271. package/dist/workspace/releases.js +9 -0
  272. package/dist/workspace/releases.schema.json +123 -1
  273. package/dist/workspace/run-event.schema.json +24 -6
  274. package/dist/workspace/status-gates.d.ts +8 -0
  275. package/dist/workspace/status-gates.d.ts.map +1 -1
  276. package/dist/workspace/status-stewards.d.ts +2 -0
  277. package/dist/workspace/status-stewards.d.ts.map +1 -1
  278. package/dist/workspace/status-stewards.js +1 -0
  279. package/dist/workspace/status.d.ts +78 -0
  280. package/dist/workspace/status.d.ts.map +1 -1
  281. package/dist/workspace/status.js +170 -3
  282. package/dist/workspace/status.schema.json +210 -2
  283. package/dist/workspace/wip.d.ts.map +1 -1
  284. package/dist/workspace/wip.js +11 -3
  285. package/package.json +1 -1
  286. package/src/build-params.test.ts +46 -0
  287. package/src/build-params.ts +55 -1
  288. package/src/change-set-graph.ts +10 -2
  289. package/src/change-set.ts +14 -3
  290. package/src/cli/build-params-cli.ts +22 -9
  291. package/src/cli/commands/build.ts +6 -3
  292. package/src/cli/handlers/approve-release.test.ts +5 -1
  293. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  294. package/src/cli/handlers/approve.ts +47 -0
  295. package/src/cli/handlers/build.test.ts +30 -0
  296. package/src/cli/handlers/build.ts +20 -4
  297. package/src/cli/handlers/fan-out.test.ts +262 -1
  298. package/src/cli/handlers/fan-out.ts +72 -8
  299. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  300. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  301. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  302. package/src/cli/handlers/pr.test.ts +148 -1
  303. package/src/cli/handlers/pr.ts +144 -28
  304. package/src/cli/handlers/promote.test.ts +280 -2
  305. package/src/cli/handlers/promote.ts +210 -34
  306. package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
  307. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  308. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  309. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  310. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  311. package/src/cli/handlers/release.e2e.test.ts +146 -32
  312. package/src/cli/handlers/release.ts +415 -28
  313. package/src/cli/handlers/run-auto-release.test.ts +9 -0
  314. package/src/cli/handlers/run-release.test.ts +55 -0
  315. package/src/cli/handlers/run.ts +69 -5
  316. package/src/cli/main.test.ts +1 -0
  317. package/src/cli/main.ts +91 -10
  318. package/src/cli/parse-args.ts +54 -2
  319. package/src/cli/registry.ts +32 -4
  320. package/src/cli/run-events.ts +5 -2
  321. package/src/components/auto-release.test.ts +9 -0
  322. package/src/components/auto-release.ts +3 -0
  323. package/src/components/capability.ts +9 -0
  324. package/src/components/driver.test.ts +24 -0
  325. package/src/components/driver.ts +36 -0
  326. package/src/components/fan-out-inputs.test.ts +85 -0
  327. package/src/components/fan-out-inputs.ts +192 -0
  328. package/src/components/fan-out-record.test.ts +10 -0
  329. package/src/components/fan-out-record.ts +19 -0
  330. package/src/components/fan-out-run.ts +48 -0
  331. package/src/components/fan-out.ts +4 -0
  332. package/src/components/pr-pipeline.ts +67 -4
  333. package/src/components/pr-run.test.ts +127 -0
  334. package/src/components/pr-run.ts +32 -12
  335. package/src/components/promote.ts +68 -2
  336. package/src/components/release-compile.ts +17 -3
  337. package/src/components/release-gate.ts +18 -4
  338. package/src/components/wave-plan.ts +45 -2
  339. package/src/config.ts +8 -0
  340. package/src/flag-params.test.ts +144 -0
  341. package/src/flag-params.ts +134 -0
  342. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  343. package/src/lifecycle/change-set-store.ts +2 -1
  344. package/src/lifecycle/delivery-metrics.ts +263 -0
  345. package/src/lifecycle/deploy-outcome.ts +147 -0
  346. package/src/lifecycle/gate-change-set.ts +37 -6
  347. package/src/lifecycle/gate-ledger.ts +2 -2
  348. package/src/lifecycle/git.ts +61 -8
  349. package/src/lifecycle/index.ts +4 -0
  350. package/src/lifecycle/mixed-environment.test.ts +97 -0
  351. package/src/lifecycle/mixed-environment.ts +204 -0
  352. package/src/lifecycle/publish-attachments.ts +196 -0
  353. package/src/lifecycle/publish-credentials.ts +84 -0
  354. package/src/lifecycle/publish-declaration.ts +95 -0
  355. package/src/lifecycle/publish-forge.ts +325 -0
  356. package/src/lifecycle/publish-npm.ts +259 -0
  357. package/src/lifecycle/publish-oci.ts +396 -0
  358. package/src/lifecycle/publish-record.ts +113 -0
  359. package/src/lifecycle/publish-resume.test.ts +168 -0
  360. package/src/lifecycle/publish-targets.test.ts +494 -0
  361. package/src/lifecycle/publish-targets.ts +100 -0
  362. package/src/lifecycle/release-cut.test.ts +74 -2
  363. package/src/lifecycle/release-cut.ts +158 -22
  364. package/src/lifecycle/release-flag.test.ts +112 -0
  365. package/src/lifecycle/release-join.test.ts +68 -0
  366. package/src/lifecycle/release-join.ts +182 -0
  367. package/src/lifecycle/release-ledger.ts +9 -3
  368. package/src/lifecycle/release-publish.test.ts +225 -0
  369. package/src/lifecycle/release-publish.ts +568 -0
  370. package/src/lifecycle/release-revocation.ts +147 -0
  371. package/src/lifecycle/release-store.ts +15 -4
  372. package/src/lifecycle/release-verify.test.ts +110 -0
  373. package/src/lifecycle/release-verify.ts +120 -0
  374. package/src/lifecycle/release-workstation.test.ts +204 -0
  375. package/src/lifecycle/release-workstation.ts +216 -0
  376. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  377. package/src/lifecycle/retention-policy.ts +63 -0
  378. package/src/lifecycle/retention.test.ts +363 -0
  379. package/src/lifecycle/retention.ts +508 -0
  380. package/src/lifecycle/run-ledger.ts +7 -1
  381. package/src/op/activities/activity-contracts.ts +1 -0
  382. package/src/op/activities/shell.ts +7 -1
  383. package/src/op/local-executor.ts +27 -4
  384. package/src/op/runtime.ts +42 -0
  385. package/src/plan-summary.test.ts +20 -0
  386. package/src/plan-summary.ts +8 -1
  387. package/src/pr-forge.test.ts +25 -0
  388. package/src/pr-forge.ts +62 -0
  389. package/src/pr-loop.test.ts +13 -1
  390. package/src/pr-loop.ts +41 -4
  391. package/src/pr-report-url.test.ts +131 -0
  392. package/src/pr-report-url.ts +209 -0
  393. package/src/provenance.ts +2 -2
  394. package/src/run-event-store.ts +63 -3
  395. package/src/run-event-stream.ts +8 -4
  396. package/src/run-event.ts +38 -1
  397. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  398. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  399. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  400. package/src/workspace/change-set.schema.json +5 -1
  401. package/src/workspace/check.schema.json +4 -2
  402. package/src/workspace/checks/records.ts +35 -1
  403. package/src/workspace/checks.ts +2 -1
  404. package/src/workspace/ci-green-forge.ts +2 -0
  405. package/src/workspace/ci-green.ts +20 -5
  406. package/src/workspace/ci-run.test.ts +416 -0
  407. package/src/workspace/ci-run.ts +571 -0
  408. package/src/workspace/ci-serve.test.ts +268 -0
  409. package/src/workspace/ci-serve.ts +521 -0
  410. package/src/workspace/declaration.schema.json +64 -0
  411. package/src/workspace/declaration.test.ts +28 -0
  412. package/src/workspace/declaration.ts +38 -0
  413. package/src/workspace/declared-kinds.ts +40 -1
  414. package/src/workspace/delivery-metrics.schema.json +519 -0
  415. package/src/workspace/delivery-metrics.test.ts +236 -0
  416. package/src/workspace/delivery-metrics.ts +286 -0
  417. package/src/workspace/flags-read.ts +121 -0
  418. package/src/workspace/flags.test.ts +141 -0
  419. package/src/workspace/flags.ts +157 -0
  420. package/src/workspace/graph-cli.ts +1 -1
  421. package/src/workspace/intent-cli.ts +46 -5
  422. package/src/workspace/intent-trace.test.ts +173 -0
  423. package/src/workspace/intent-trace.ts +484 -0
  424. package/src/workspace/intent.schema.json +702 -6
  425. package/src/workspace/intent.ts +148 -25
  426. package/src/workspace/older-records.test.ts +412 -0
  427. package/src/workspace/overlap.test.ts +165 -0
  428. package/src/workspace/overlap.ts +340 -0
  429. package/src/workspace/pr-report.schema.json +5 -0
  430. package/src/workspace/read-contract.e2e.test.ts +2 -1
  431. package/src/workspace/reason-codes.test.ts +5 -0
  432. package/src/workspace/reason-codes.ts +3 -0
  433. package/src/workspace/record-assets.test.ts +2 -1
  434. package/src/workspace/records-contract.e2e.test.ts +2 -1
  435. package/src/workspace/records.schema.json +21 -2
  436. package/src/workspace/records.ts +36 -2
  437. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  438. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  439. package/src/workspace/releases.schema.json +123 -1
  440. package/src/workspace/releases.ts +11 -0
  441. package/src/workspace/run-event.schema.json +24 -6
  442. package/src/workspace/status-contract.test.ts +29 -0
  443. package/src/workspace/status-gates.ts +8 -0
  444. package/src/workspace/status-mixed.test.ts +152 -0
  445. package/src/workspace/status-revoked.test.ts +153 -0
  446. package/src/workspace/status-stewards.ts +3 -0
  447. package/src/workspace/status.schema.json +210 -2
  448. package/src/workspace/status.ts +254 -3
  449. package/src/workspace/wip.ts +11 -2
@@ -0,0 +1,209 @@
1
+ /**
2
+ * `pr-plan --report-url` and `pr-apply --report-url` (#4007): copy a stage's
3
+ * report, its change set and its run events where a reader outside CI can
4
+ * fetch them, keyed by pull request and commit.
5
+ *
6
+ * ```
7
+ * <url>/<pr>/<commit>/pr-<stage>.json
8
+ * <url>/<pr>/<commit>/change-set.json
9
+ * <url>/<pr>/<commit>/pr-<stage>.events.jsonl (with --events-json)
10
+ * ```
11
+ *
12
+ * `<pr>` is the pull or merge request number, or `unknown` when pr-apply
13
+ * found none, and `<commit>` is the full sha the stage ran on: the pull
14
+ * request's head for pr-plan, the pushed commit for pr-apply. A workspace
15
+ * member's loop (`--member`) adds `/<member>` after the commit. There is no
16
+ * index: a reader that knows the pull request and the commit knows the key.
17
+ * The events file is named for its stage because pr-plan and pr-apply run on
18
+ * the same commit when a pull request merges by fast-forward.
19
+ *
20
+ * The events file is uploaded again, whole, each time a wave, a component or
21
+ * the run settles, so a reader polling the URL sees each wave as it applies.
22
+ * One upload of it is in flight at a time: a settle while one is in flight
23
+ * asks for one more after it, with everything written by then. A failed
24
+ * upload is a warning and the run goes on. The last upload, after the run
25
+ * settles, carries the whole stream, and the report and the change set go
26
+ * with it.
27
+ *
28
+ * Two schemes. `file://<dir>` writes under a directory, each file through a
29
+ * temporary name and a rename, so a reader never sees half a file. `s3://
30
+ * <bucket>/<prefix>` runs the AWS CLI (`aws s3 cp - <s3 url>`, the body on
31
+ * its stdin), which reads its credentials, region and endpoint from the
32
+ * environment the job gives it, the way the aws lexicon's `s3-sync` does.
33
+ * chant passes it no credential and prints none: a failed upload prints the
34
+ * CLI's own error line.
35
+ */
36
+
37
+ import { spawn } from "node:child_process";
38
+ import { mkdirSync, renameSync, writeFileSync } from "node:fs";
39
+ import { dirname, isAbsolute, join } from "node:path";
40
+ import { fileURLToPath } from "node:url";
41
+
42
+ /** Where `--report-url` points. */
43
+ export type ReportTarget =
44
+ | { scheme: "file"; url: string; dir: string }
45
+ | { scheme: "s3"; url: string; bucket: string; prefix: string };
46
+
47
+ /** `--report-url` read, or why it can't be. */
48
+ export function parseReportUrl(raw: string): ReportTarget | { error: string } {
49
+ const url = raw.trim();
50
+ if (url.startsWith("file://")) {
51
+ let dir: string;
52
+ try {
53
+ dir = fileURLToPath(url);
54
+ } catch (err) {
55
+ return { error: `--report-url ${url}: ${err instanceof Error ? err.message : String(err)}` };
56
+ }
57
+ if (!isAbsolute(dir)) return { error: `--report-url ${url}: a file URL names an absolute directory` };
58
+ return { scheme: "file", url: url.replace(/\/+$/, ""), dir };
59
+ }
60
+ const s3 = /^s3:\/\/([^/]+)\/?(.*)$/.exec(url);
61
+ if (s3) {
62
+ const prefix = s3[2]!.replace(/\/+$/, "");
63
+ return { scheme: "s3", url: `s3://${s3[1]}${prefix ? `/${prefix}` : ""}`, bucket: s3[1]!, prefix };
64
+ }
65
+ return { error: `--report-url ${url}: takes an s3://<bucket>/<prefix> or file://<directory> URL` };
66
+ }
67
+
68
+ /** The key a stage's files are kept under, below the URL: `<pr>/<commit>[/<member>]`. */
69
+ export function reportKey(pr: number | null, commit: string, member?: string): string {
70
+ return [pr === null ? "unknown" : String(pr), commit, ...(member ? [member] : [])].join("/");
71
+ }
72
+
73
+ /** The events file of a stage. */
74
+ export const reportEventsFile = (stage: "plan" | "apply"): string => `pr-${stage}.events.jsonl`;
75
+
76
+ /** Puts one object: `key` below the URL. Rejects with a line a person can read. */
77
+ export type ReportPut = (key: string, body: string, contentType: string) => Promise<void>;
78
+
79
+ /** Runs the AWS CLI with `args` and `stdin`, rejecting with its error line on a non-zero exit. */
80
+ export type AwsCli = (args: string[], stdin: string) => Promise<void>;
81
+
82
+ const AWS_BINARY = "aws";
83
+
84
+ /** The AWS CLI on PATH. */
85
+ export const realAwsCli: AwsCli = (args, stdin) =>
86
+ new Promise((resolvePut, reject) => {
87
+ const child = spawn(AWS_BINARY, args, { stdio: ["pipe", "ignore", "pipe"] });
88
+ let stderr = "";
89
+ child.stderr.on("data", (chunk: Buffer) => {
90
+ if (stderr.length < 4096) stderr += chunk.toString("utf8");
91
+ });
92
+ child.on("error", (err: NodeJS.ErrnoException) =>
93
+ reject(new Error(err.code === "ENOENT" ? "the AWS CLI (aws) is not on PATH; an s3 report URL is uploaded with it" : err.message)),
94
+ );
95
+ child.on("close", (code) => {
96
+ if (code === 0) return resolvePut();
97
+ const line = stderr.trim().split("\n").filter(Boolean).at(-1) ?? "";
98
+ reject(new Error(`aws s3 cp exited ${code}${line ? `: ${line.slice(0, 300)}` : ""}`));
99
+ });
100
+ child.stdin.on("error", () => {
101
+ // The CLI exited before reading its input; its exit says why.
102
+ });
103
+ child.stdin.end(stdin);
104
+ });
105
+
106
+ /** A put for `target`. `aws` replaces the AWS CLI in a test. */
107
+ export function reportPut(target: ReportTarget, aws: AwsCli = realAwsCli): ReportPut {
108
+ if (target.scheme === "file") {
109
+ return async (key, body) => {
110
+ const path = join(target.dir, ...key.split("/"));
111
+ mkdirSync(dirname(path), { recursive: true });
112
+ const temp = `${path}.${process.pid}.tmp`;
113
+ writeFileSync(temp, body);
114
+ renameSync(temp, path);
115
+ };
116
+ }
117
+ return (key, body, contentType) => {
118
+ const object = `s3://${target.bucket}/${target.prefix ? `${target.prefix}/` : ""}${key}`;
119
+ return aws(["s3", "cp", "-", object, "--content-type", contentType, "--only-show-errors"], body);
120
+ };
121
+ }
122
+
123
+ /** A stage's upload: its events as they are written, and its files at the end. */
124
+ export interface PrReportUpload {
125
+ /** The folder the stage's files go to: `<url>/<key>`. */
126
+ readonly where: string;
127
+ /** One line of the stage's run-event stream. A wave, component or run settle uploads the events. */
128
+ line(line: string): void;
129
+ /** A file to upload when the stage closes, replacing one of the same name. */
130
+ file(name: string, body: string): void;
131
+ /** Wait for the upload in flight, then upload the whole stream and the files. Never throws. */
132
+ close(): Promise<void>;
133
+ }
134
+
135
+ const UPLOAD_ON = new Set(["wave-settle", "component-settle", "run-settle"]);
136
+ const NDJSON = "application/x-ndjson";
137
+ const JSON_TYPE = "application/json";
138
+
139
+ const messageOf = (err: unknown): string => (err instanceof Error ? err.message : String(err));
140
+
141
+ /** Open a stage's upload to `target` under `key`. `warn` prints a warning line. */
142
+ export function openPrReportUpload(input: {
143
+ target: ReportTarget;
144
+ key: string;
145
+ stage: "plan" | "apply";
146
+ warn: (message: string) => void;
147
+ put?: ReportPut;
148
+ }): PrReportUpload {
149
+ const put = input.put ?? reportPut(input.target);
150
+ const where = `${input.target.url}/${input.key}`;
151
+ const eventsKey = `${input.key}/${reportEventsFile(input.stage)}`;
152
+ const files = new Map<string, string>();
153
+ let events = "";
154
+ let inFlight: Promise<void> | undefined;
155
+ let again = false;
156
+ let warned = false;
157
+
158
+ const pushEvents = (): void => {
159
+ if (inFlight) {
160
+ again = true;
161
+ return;
162
+ }
163
+ inFlight = put(eventsKey, events, NDJSON)
164
+ .catch((err) => {
165
+ if (warned) return;
166
+ warned = true;
167
+ input.warn(`could not upload the run's events to ${where} as the run went: ${messageOf(err)}. The run goes on, and its end tries again.`);
168
+ })
169
+ .finally(() => {
170
+ inFlight = undefined;
171
+ if (again) {
172
+ again = false;
173
+ pushEvents();
174
+ }
175
+ });
176
+ };
177
+
178
+ return {
179
+ where,
180
+ line(line) {
181
+ events += line;
182
+ let type: unknown;
183
+ try {
184
+ type = (JSON.parse(line) as { type?: unknown }).type;
185
+ } catch {
186
+ return;
187
+ }
188
+ if (typeof type === "string" && UPLOAD_ON.has(type)) pushEvents();
189
+ },
190
+ file(name, body) {
191
+ files.set(name, body);
192
+ },
193
+ async close() {
194
+ again = false;
195
+ while (inFlight) await inFlight;
196
+ const uploads: Array<[string, string, string]> = [
197
+ ...(events ? [[eventsKey, events, NDJSON] as [string, string, string]] : []),
198
+ ...[...files].map(([name, body]): [string, string, string] => [`${input.key}/${name}`, body, JSON_TYPE]),
199
+ ];
200
+ for (const [key, body, type] of uploads) {
201
+ try {
202
+ await put(key, body, type);
203
+ } catch (err) {
204
+ input.warn(`could not upload ${key.slice(input.key.length + 1)} to ${where}: ${messageOf(err)}`);
205
+ }
206
+ }
207
+ },
208
+ };
209
+ }
package/src/provenance.ts CHANGED
@@ -297,6 +297,6 @@ export interface BuildParamProvenance {
297
297
  name: string;
298
298
  /** The resolved, type-coerced value actually bound to `params.<name>` for this build. */
299
299
  value: string | number | boolean;
300
- /** Which input supplied the value. */
301
- source: "cli" | "params-file" | "env" | "default";
300
+ /** Which input supplied the value: `flag:<name>` names the workspace flag record it came from (#4024). */
301
+ source: "cli" | "params-file" | "env" | "default" | `flag:${string}`;
302
302
  }
@@ -8,13 +8,14 @@
8
8
  * as `--events-json` prints them. It sits beside the in-flight records
9
9
  * (`./op/run-live.ts`), outside the working tree and off `chant/lifecycle`:
10
10
  * the run ledger's growth is #4036's concern and these files add none to it.
11
- * Unlike the in-flight record they are kept after the run settles. They are
12
- * never pruned here; retention is #4036's.
11
+ * Unlike the in-flight record they are kept after the run settles, until
12
+ * `chant lifecycle gc --yes` deletes the ones its retention policy lets go
13
+ * (#4036, {@link planRunEventPrune}).
13
14
  *
14
15
  * Every write is best-effort. A run whose events cannot be kept still runs.
15
16
  */
16
17
 
17
- import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
18
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
18
19
  import { join } from "node:path";
19
20
  import { gitCommonDir } from "./op/run-live";
20
21
  import type { RunEvent } from "./run-event";
@@ -211,3 +212,62 @@ export async function readRunEvents(
211
212
  }
212
213
  }
213
214
  }
215
+
216
+ /** One run's event file, as retention sees it (#4036). */
217
+ export interface RunEventFile {
218
+ file: string;
219
+ bytes: number;
220
+ /** The file's last write, ISO-8601. */
221
+ modified: string;
222
+ }
223
+
224
+ /** What {@link planRunEventPrune} would delete and keep. */
225
+ export interface RunEventPrune {
226
+ /** The directory read, or null outside a checkout. */
227
+ dir: string | null;
228
+ remove: RunEventFile[];
229
+ keep: RunEventFile[];
230
+ }
231
+
232
+ /**
233
+ * Which run event files the policy lets go (#4036): every file older than
234
+ * `days` by its last write, apart from the newest `keep`. A run still going
235
+ * writes its file as it goes, so it is never older than the window. Reads
236
+ * only; {@link pruneRunEvents} deletes.
237
+ */
238
+ export async function planRunEventPrune(cwd: string, policy: { days: number; keep: number }, now: Date): Promise<RunEventPrune> {
239
+ const dir = await runEventsDir(cwd);
240
+ if (!dir || !existsSync(dir)) return { dir, remove: [], keep: [] };
241
+ const files: Array<RunEventFile & { ms: number }> = [];
242
+ for (const entry of readdirSync(dir)) {
243
+ if (!entry.endsWith(".jsonl")) continue;
244
+ const file = join(dir, entry);
245
+ try {
246
+ const st = statSync(file);
247
+ if (!st.isFile()) continue;
248
+ files.push({ file, bytes: st.size, modified: new Date(st.mtimeMs).toISOString(), ms: st.mtimeMs });
249
+ } catch {
250
+ // Gone between the listing and the stat.
251
+ }
252
+ }
253
+ files.sort((a, b) => b.ms - a.ms || (a.file < b.file ? -1 : 1));
254
+ const cutoff = now.getTime() - policy.days * 86_400_000;
255
+ const remove: RunEventFile[] = [];
256
+ const keep: RunEventFile[] = [];
257
+ files.forEach(({ ms, ...f }, i) => (i >= policy.keep && ms < cutoff ? remove : keep).push(f));
258
+ return { dir, remove, keep };
259
+ }
260
+
261
+ /** Delete the files a plan lets go. Returns how many were deleted; a file already gone is not counted. */
262
+ export function pruneRunEvents(plan: RunEventPrune): number {
263
+ let n = 0;
264
+ for (const f of plan.remove) {
265
+ try {
266
+ rmSync(f.file);
267
+ n++;
268
+ } catch {
269
+ // Already gone.
270
+ }
271
+ }
272
+ return n;
273
+ }
@@ -26,6 +26,7 @@ import {
26
26
  type RunEventRun,
27
27
  type RunEventRuntime,
28
28
  type RunEventSettleStatus,
29
+ type RunEventFailure,
29
30
  } from "./run-event";
30
31
 
31
32
  /** Where an emitter hands a run's events, before the writer stamps them. */
@@ -46,7 +47,8 @@ const GATE_PREFIX = "gate:";
46
47
  /**
47
48
  * A run-event body as the older `RunProgressEvent`, or undefined when that
48
49
  * stream had no such event. It never printed a gate step, a skipped step,
49
- * a phase outside a component or a gate's decision, so those map to nothing
50
+ * a phase outside a component, a gate's decision or a component the run did
51
+ * not start (`blocked`, `already-applied`, #4007), so those map to nothing
50
52
  * and `--progress-json` prints what it printed before #4041.
51
53
  */
52
54
  export function progressOfRunEvent(body: RunEventBody): RunProgressEvent | undefined {
@@ -79,6 +81,8 @@ export function progressOfRunEvent(body: RunEventBody): RunProgressEvent | undef
79
81
  ? undefined
80
82
  : { type: "phase-done", component: body.component, phase: body.phase, status: progressStatus(body.status) };
81
83
  case "component-settle":
84
+ // A component the run did not start (#4007) was never in the older stream.
85
+ if (body.status === "blocked" || body.status === "already-applied") return undefined;
82
86
  return { type: "component-done", wave: body.wave, component: body.component, status: progressStatus(body.status) };
83
87
  case "wave-settle":
84
88
  return { type: "wave-done", wave: body.wave, status: progressStatus(body.status) };
@@ -171,7 +175,7 @@ export interface RunEventStream {
171
175
  */
172
176
  nested: RunEventSink;
173
177
  /** End the stream with `run-settle`, unless it has ended. */
174
- settle(status: RunEventSettleStatus, error?: string): void;
178
+ settle(status: RunEventSettleStatus, error?: string, failure?: RunEventFailure): void;
175
179
  /** Whether `run-settle` has been written. */
176
180
  readonly settled: boolean;
177
181
  }
@@ -216,9 +220,9 @@ export function openRunEventStream(
216
220
  nested: (body) => {
217
221
  if (body.type !== "run-start" && body.type !== "run-settle") emit(body);
218
222
  },
219
- settle(status, error) {
223
+ settle(status, error, failure) {
220
224
  if (settled) return;
221
- emit({ type: "run-settle", status, durationMs: null, ...(error !== undefined ? { error } : {}) });
225
+ emit({ type: "run-settle", status, durationMs: null, ...(error !== undefined ? { error } : {}), ...(failure ? { failure } : {}) });
222
226
  },
223
227
  get settled() {
224
228
  return settled;
package/src/run-event.ts CHANGED
@@ -105,8 +105,25 @@ export interface RunEventRunSettle {
105
105
  status: RunEventSettleStatus;
106
106
  durationMs: number | null;
107
107
  error?: string;
108
+ /**
109
+ * Why a failed run failed, as its run record's `failure` says (#4094):
110
+ * `chant ci run` writes it on a failed job. Optional, added within
111
+ * contract 1 by chant {@link RUN_EVENT_FAILURE_FLOOR}.
112
+ */
113
+ failure?: RunEventFailure;
114
+ }
115
+
116
+ /** Why a run failed: a short code, a line for a person, the phase and the exit code when known (#4094). */
117
+ export interface RunEventFailure {
118
+ code: string;
119
+ reason: string;
120
+ phase?: string;
121
+ exitCode?: number;
108
122
  }
109
123
 
124
+ /** The first chant that writes `failure` on `run-settle` (#4094). */
125
+ export const RUN_EVENT_FAILURE_FLOOR = "0.124.30";
126
+
110
127
  export interface RunEventWaveStart {
111
128
  type: "wave-start";
112
129
  /** 1-based, into `run-start`'s `waves`. */
@@ -126,11 +143,31 @@ export interface RunEventComponentStart {
126
143
  component: string;
127
144
  }
128
145
 
146
+ /**
147
+ * How a component settled: a {@link RunEventSettleStatus}, or one of two
148
+ * statuses for a component the run did not start (#4007), added within
149
+ * contract 1 by chant 0.124.29. `blocked`: a dependency failed or stopped at
150
+ * a gate, so it never ran. `already-applied`: an earlier attempt that this
151
+ * run resumed applied it. Neither has a `component-start` before it. A reader
152
+ * that knows only the four settle statuses reads either as a component that
153
+ * did not run in this run.
154
+ */
155
+ export type RunEventComponentSettleStatus = RunEventSettleStatus | "blocked" | "already-applied";
156
+
157
+ /** The first chant that writes `blocked`, `already-applied` and `blockedBy` on `component-settle` (#4007). */
158
+ export const RUN_EVENT_COMPONENT_SKIP_FLOOR = "0.124.29";
159
+
129
160
  export interface RunEventComponentSettle {
130
161
  type: "component-settle";
162
+ /**
163
+ * 1-based, into `run-start`'s `waves`. On an `already-applied` settle it is
164
+ * the wave the component had in the derivation the earlier attempt ran.
165
+ */
131
166
  wave: number;
132
167
  component: string;
133
- status: RunEventSettleStatus;
168
+ status: RunEventComponentSettleStatus;
169
+ /** With `blocked`: the failed or gated component the run reached this one from. */
170
+ blockedBy?: string;
134
171
  }
135
172
 
136
173
  /** An Op's phase, a component composition's phase, or a pipeline's stage. `component` is null outside a component run. */
@@ -0,0 +1,132 @@
1
+ /**
2
+ * A workspace with feature flags (#4024), for the flag tests: a throwaway git
3
+ * repository with a decision kind, the reference workspace's work kind, and
4
+ * the canonical flag kind chant ships at `reference-kinds/flag`.
5
+ *
6
+ * - The decision kind closes `confirmed` and `superseded`. d-001 is
7
+ * confirmed, d-002 is only decided.
8
+ * - W-001 implements d-001, so its work is settled. W-002 implements d-002.
9
+ * - The flag `old-banner` hides W-001, so it is due for removal (stale).
10
+ * - The flag `new-checkout` hides W-002 and backs the build parameter
11
+ * `newCheckout` of member `shop`: on in dev, off in prod, and no value in
12
+ * staging.
13
+ * - Member `shop` has a component whose deploy declares a gate, so every
14
+ * environment it builds for has one. Member `blog` has a component with
15
+ * no gate.
16
+ */
17
+
18
+ import { readFileSync } from "node:fs";
19
+ import { join } from "node:path";
20
+ import { git, repo } from "./contract-repo";
21
+
22
+ const HERE = import.meta.dirname;
23
+ const REPO_ROOT = join(HERE, "..", "..", "..", "..", "..");
24
+ const REF = join(REPO_ROOT, "reference-workspace");
25
+ const ref = (path: string) => readFileSync(join(REF, path), "utf-8");
26
+ const canonical = (file: string) => readFileSync(join(HERE, "..", "reference-kinds", "flag", file), "utf-8");
27
+
28
+ const DECISION_KIND = `export const recordKind = {
29
+ name: "decision",
30
+ location: { dir: ".", match: "^d-[0-9]{3}-.+\\\\.md$" },
31
+ format: "markdown-front-matter",
32
+ schema: { id: "urn:test:decision:1", path: "decision.schema.json" },
33
+ idField: "id",
34
+ stateField: "state",
35
+ states: ["proposed", "decided", "confirmed", "superseded"],
36
+ closedStates: ["confirmed", "superseded"],
37
+ approval: { proposed: 0, decided: 1, confirmed: 2, superseded: 2 },
38
+ supersedes: { field: "supersedes" },
39
+ };
40
+ `;
41
+
42
+ const DECISION_SCHEMA = JSON.stringify(
43
+ {
44
+ $schema: "http://json-schema.org/draft-07/schema#",
45
+ $id: "urn:test:decision:1",
46
+ type: "object",
47
+ required: ["id", "state"],
48
+ properties: { id: { type: "string" }, state: { type: "string" }, supersedes: { type: "array", items: { type: "string" } } },
49
+ },
50
+ null,
51
+ 2,
52
+ );
53
+
54
+ const decision = (id: string, state: string) => `---\nid: "${id}"\nstate: "${state}"\nsupersedes: []\n---\n\n# ${id}\n`;
55
+
56
+ export function workItem(id: string, implementsIds: string[], state = "in-progress"): string {
57
+ const data = {
58
+ schema: 1,
59
+ id,
60
+ title: `Work ${id}`,
61
+ state,
62
+ implements: implementsIds,
63
+ needs: [],
64
+ constrains: ["member:shop"],
65
+ evidence: [],
66
+ owner: "alice",
67
+ opened_on: "2026-10-01",
68
+ ...(state === "done" ? { closed_on: "2026-10-10" } : {}),
69
+ source: { kind: "workspace", member: "shop" },
70
+ supersedes: [],
71
+ };
72
+ return `---\n${JSON.stringify(data, null, 2)}\n---\n\n# ${id}\n`;
73
+ }
74
+
75
+ export function flagRecord(id: string, fields: { work: string; member: string; param: string; values: Record<string, string | number | boolean>; owner?: string }): string {
76
+ const data = { schema: 1, id, owner: fields.owner ?? "alice", work: fields.work, member: fields.member, param: fields.param, values: fields.values };
77
+ return `---\n${JSON.stringify(data, null, 2)}\n---\n\n# ${id}\n`;
78
+ }
79
+
80
+ /** A component file whose deploy declares a gate when `gated` is set. */
81
+ export function componentFile(name: string, gated: boolean): string {
82
+ const steps = gated ? `[{ kind: "gate", gate: "prod-release" }, { kind: "apply", template: "dist/${name}.json" }]` : `[{ kind: "apply", template: "dist/${name}.json" }]`;
83
+ return `export const ${name} = { name: "${name}", dependsOn: [], deploy: [{ phase: "Apply", steps: ${steps} }] };\n`;
84
+ }
85
+
86
+ /** The flag workspace's files, with `overrides` laid over them (a null value removes a file). */
87
+ export function flagWorkspaceFiles(overrides: Record<string, string | null> = {}): Record<string, string> {
88
+ const files: Record<string, string | null> = {
89
+ "chant.workspace.json": JSON.stringify(
90
+ {
91
+ name: "flags",
92
+ schema: 1,
93
+ members: [
94
+ { name: "shop", dir: "shop", kind: "chant" },
95
+ { name: "blog", dir: "blog", kind: "chant" },
96
+ ],
97
+ records: [{ kind: "decisions/decision.kind.mjs" }, { kind: "work/work.kind.mjs" }, { kind: "flags/flag.kind.mjs" }],
98
+ },
99
+ null,
100
+ 2,
101
+ ),
102
+ "shop/chant.config.ts": `export default { environments: ["dev", "staging", "prod"] };\n`,
103
+ "shop/shop.component.ts": componentFile("shop", true),
104
+ "blog/chant.config.ts": `export default { environments: ["dev", "prod"] };\n`,
105
+ "blog/blog.component.ts": componentFile("blog", false),
106
+ "decisions/decision.kind.mjs": DECISION_KIND,
107
+ "decisions/decision.schema.json": DECISION_SCHEMA,
108
+ "decisions/d-001-the-banner.md": decision("d-001", "confirmed"),
109
+ "decisions/d-002-the-checkout.md": decision("d-002", "decided"),
110
+ "work/work.kind.mjs": ref("work/work.kind.mjs").replace(' answers: "../answers/answer.kind.mjs",\n', ""),
111
+ "work/work.schema.json": ref("work/work.schema.json"),
112
+ "work/W-001-the-banner.md": workItem("W-001", ["d-001"], "done"),
113
+ "work/W-002-the-checkout.md": workItem("W-002", ["d-002"]),
114
+ "flags/flag.kind.mjs": canonical("flag.kind.mjs"),
115
+ "flags/flag.schema.json": canonical("flag.schema.json"),
116
+ "flags/old-banner.flag.md": flagRecord("old-banner", { work: "W-001", member: "blog", param: "banner", values: { dev: true, prod: true } }),
117
+ "flags/new-checkout.flag.md": flagRecord("new-checkout", { work: "W-002", member: "shop", param: "newCheckout", values: { dev: true, prod: false } }),
118
+ ...overrides,
119
+ };
120
+ return Object.fromEntries(Object.entries(files).filter((e): e is [string, string] => e[1] !== null));
121
+ }
122
+
123
+ /** The flag workspace in a new git repository, committed. */
124
+ export function flagWorkspace(overrides: Record<string, string | null> = {}): string {
125
+ const root = repo(flagWorkspaceFiles(overrides));
126
+ git(root, "config", "user.name", "t");
127
+ git(root, "config", "user.email", "t@example.com");
128
+ git(root, "config", "commit.gpgsign", "false");
129
+ git(root, "add", "-A");
130
+ git(root, "commit", "-q", "-m", "flags");
131
+ return root;
132
+ }
@@ -0,0 +1,100 @@
1
+ {
2
+ "$schema": "https://intentius.io/chant/schemas/workspace/change-set/v1/change-set.schema.json",
3
+ "contract": 1,
4
+ "digest": "jcs1-sha256:38ee3f4bfab616f181ff10ea6b2c75284e3b93a0f976907fc84954e6c0356f07",
5
+ "members": [
6
+ {
7
+ "member": "queues",
8
+ "lexicon": "aws",
9
+ "planner": "chant",
10
+ "scope": "dev",
11
+ "status": "planned",
12
+ "planDigest": "jcs1-sha256:e526af57de8232c2d567d96e24eae57daf4aceb664e4ab3f00f3da1061361cb9",
13
+ "holes": []
14
+ }
15
+ ],
16
+ "entries": [
17
+ {
18
+ "member": "queues",
19
+ "lexicon": "aws",
20
+ "planner": "chant",
21
+ "address": "alerts",
22
+ "type": "AWS::SNS::Topic",
23
+ "id": "arn:aws:sns:us-east-1:000000000000:cs-alerts",
24
+ "action": "no-op",
25
+ "scope": "dev",
26
+ "attributes": [],
27
+ "node": null
28
+ },
29
+ {
30
+ "member": "queues",
31
+ "lexicon": "aws",
32
+ "planner": "chant",
33
+ "address": "dlq",
34
+ "type": "unknown",
35
+ "action": "create",
36
+ "scope": "dev",
37
+ "attributes": [],
38
+ "node": null
39
+ },
40
+ {
41
+ "member": "queues",
42
+ "lexicon": "aws",
43
+ "planner": "chant",
44
+ "address": "events",
45
+ "type": "AWS::SQS::Queue",
46
+ "id": "http://localhost:4566/000000000000/cs-events",
47
+ "action": "no-op",
48
+ "scope": "dev",
49
+ "attributes": [],
50
+ "node": null
51
+ },
52
+ {
53
+ "member": "queues",
54
+ "lexicon": "aws",
55
+ "planner": "chant",
56
+ "address": "jobs",
57
+ "type": "AWS::SQS::Queue",
58
+ "id": "http://localhost:4566/000000000000/cs-jobs",
59
+ "action": "no-op",
60
+ "scope": "dev",
61
+ "attributes": [],
62
+ "node": null
63
+ }
64
+ ],
65
+ "summary": {
66
+ "members": 1,
67
+ "entries": 4,
68
+ "actions": {
69
+ "create": 1,
70
+ "update": 0,
71
+ "replace": 0,
72
+ "delete": 0,
73
+ "read": 0,
74
+ "no-op": 3,
75
+ "forget": 0
76
+ },
77
+ "types": {
78
+ "AWS::SNS::Topic": {
79
+ "no-op": 1
80
+ },
81
+ "AWS::SQS::Queue": {
82
+ "no-op": 2
83
+ },
84
+ "unknown": {
85
+ "create": 1
86
+ }
87
+ },
88
+ "byMember": {
89
+ "queues": {
90
+ "no-op": 3,
91
+ "create": 1
92
+ }
93
+ },
94
+ "deletes": [],
95
+ "replacements": [],
96
+ "failed": [],
97
+ "holes": 0
98
+ },
99
+ "graph": null
100
+ }