@intentius/chant 0.124.31 → 0.124.33

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 (509) hide show
  1. package/bin/chant +7 -2
  2. package/dist/ci-jobs.d.ts +4 -0
  3. package/dist/ci-jobs.d.ts.map +1 -1
  4. package/dist/ci-jobs.js +9 -2
  5. package/dist/cli/commands/check-lexicon-plugin.d.ts +7 -4
  6. package/dist/cli/commands/check-lexicon-plugin.d.ts.map +1 -1
  7. package/dist/cli/commands/check-lexicon-plugin.js +10 -7
  8. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  9. package/dist/cli/commands/check-lexicon.js +18 -12
  10. package/dist/cli/commands/import.d.ts +10 -0
  11. package/dist/cli/commands/import.d.ts.map +1 -1
  12. package/dist/cli/commands/import.js +37 -0
  13. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
  14. package/dist/cli/commands/init-lexicon/templates/project.js +4 -3
  15. package/dist/cli/commands/onboard.d.ts.map +1 -1
  16. package/dist/cli/commands/onboard.js +8 -4
  17. package/dist/cli/commands/update.d.ts.map +1 -1
  18. package/dist/cli/commands/update.js +2 -1
  19. package/dist/cli/dependencies.d.ts +18 -0
  20. package/dist/cli/dependencies.d.ts.map +1 -0
  21. package/dist/cli/dependencies.js +120 -0
  22. package/dist/cli/handlers/build.d.ts.map +1 -1
  23. package/dist/cli/handlers/build.js +38 -4
  24. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  25. package/dist/cli/handlers/fan-out.js +12 -4
  26. package/dist/cli/handlers/graph.d.ts.map +1 -1
  27. package/dist/cli/handlers/graph.js +16 -6
  28. package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
  29. package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
  30. package/dist/cli/handlers/lifecycle-health.js +150 -0
  31. package/dist/cli/handlers/lifecycle.d.ts +26 -0
  32. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  33. package/dist/cli/handlers/lifecycle.js +1 -1
  34. package/dist/cli/handlers/lint.d.ts.map +1 -1
  35. package/dist/cli/handlers/lint.js +6 -0
  36. package/dist/cli/handlers/operator.d.ts +2 -2
  37. package/dist/cli/handlers/operator.js +3 -3
  38. package/dist/cli/handlers/pr.d.ts +16 -0
  39. package/dist/cli/handlers/pr.d.ts.map +1 -1
  40. package/dist/cli/handlers/pr.js +610 -32
  41. package/dist/cli/handlers/promote.d.ts.map +1 -1
  42. package/dist/cli/handlers/promote.js +19 -2
  43. package/dist/cli/handlers/release.d.ts +16 -1
  44. package/dist/cli/handlers/release.d.ts.map +1 -1
  45. package/dist/cli/handlers/release.js +83 -11
  46. package/dist/cli/handlers/run.d.ts +13 -1
  47. package/dist/cli/handlers/run.d.ts.map +1 -1
  48. package/dist/cli/handlers/run.js +38 -15
  49. package/dist/cli/main.d.ts.map +1 -1
  50. package/dist/cli/main.js +121 -33
  51. package/dist/cli/parse-args.d.ts.map +1 -1
  52. package/dist/cli/parse-args.js +37 -0
  53. package/dist/cli/registry.d.ts +29 -6
  54. package/dist/cli/registry.d.ts.map +1 -1
  55. package/dist/cli/registry.js +6 -2
  56. package/dist/codegen/generate-post-synth-barrel.js +2 -2
  57. package/dist/codegen/lexicon-regen.d.ts +4 -0
  58. package/dist/codegen/lexicon-regen.d.ts.map +1 -1
  59. package/dist/codegen/lexicon-regen.js +1 -1
  60. package/dist/codegen/pinned-upgrade.js +2 -2
  61. package/dist/components/capability.d.ts +44 -3
  62. package/dist/components/capability.d.ts.map +1 -1
  63. package/dist/components/capability.js +21 -0
  64. package/dist/components/changed-files.d.ts +7 -1
  65. package/dist/components/changed-files.d.ts.map +1 -1
  66. package/dist/components/changed-files.js +9 -1
  67. package/dist/components/deploy-inputs.d.ts +91 -0
  68. package/dist/components/deploy-inputs.d.ts.map +1 -0
  69. package/dist/components/deploy-inputs.js +184 -0
  70. package/dist/components/driver.d.ts +38 -0
  71. package/dist/components/driver.d.ts.map +1 -1
  72. package/dist/components/driver.js +56 -6
  73. package/dist/components/fan-out-record.d.ts +6 -1
  74. package/dist/components/fan-out-record.d.ts.map +1 -1
  75. package/dist/components/fan-out-record.js +24 -0
  76. package/dist/components/fan-out-run.d.ts +20 -0
  77. package/dist/components/fan-out-run.d.ts.map +1 -1
  78. package/dist/components/fan-out-run.js +21 -1
  79. package/dist/components/gate-plan.d.ts +8 -0
  80. package/dist/components/gate-plan.d.ts.map +1 -1
  81. package/dist/components/gate-plan.js +6 -0
  82. package/dist/components/pr-pipeline.d.ts +98 -12
  83. package/dist/components/pr-pipeline.d.ts.map +1 -1
  84. package/dist/components/pr-pipeline.js +146 -23
  85. package/dist/components/pr-run.d.ts +128 -30
  86. package/dist/components/pr-run.d.ts.map +1 -1
  87. package/dist/components/pr-run.js +290 -70
  88. package/dist/components/release-compile.d.ts +37 -2
  89. package/dist/components/release-compile.d.ts.map +1 -1
  90. package/dist/components/release-compile.js +64 -8
  91. package/dist/components/release-promote.d.ts +17 -3
  92. package/dist/components/release-promote.d.ts.map +1 -1
  93. package/dist/components/release-promote.js +79 -18
  94. package/dist/components/verbs/bom-writer.d.ts +1 -1
  95. package/dist/components/verbs/bom-writer.d.ts.map +1 -1
  96. package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
  97. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
  98. package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
  99. package/dist/components/wave-plan.d.ts +15 -2
  100. package/dist/components/wave-plan.d.ts.map +1 -1
  101. package/dist/components/wave-plan.js +44 -5
  102. package/dist/discovery/fold-import.d.ts.map +1 -1
  103. package/dist/discovery/fold-import.js +16 -6
  104. package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
  105. package/dist/discovery/sandbox/bundle.js +6 -0
  106. package/dist/discovery/specifier-resolve.d.ts.map +1 -1
  107. package/dist/discovery/specifier-resolve.js +8 -2
  108. package/dist/fold/fold.d.ts.map +1 -1
  109. package/dist/fold/fold.js +68 -1
  110. package/dist/fold/subset.d.ts +37 -0
  111. package/dist/fold/subset.d.ts.map +1 -1
  112. package/dist/fold/subset.js +88 -0
  113. package/dist/graph-detail.d.ts.map +1 -1
  114. package/dist/graph-detail.js +32 -7
  115. package/dist/health.d.ts +110 -0
  116. package/dist/health.d.ts.map +1 -0
  117. package/dist/health.js +105 -0
  118. package/dist/health.schema.json +191 -0
  119. package/dist/lexicon-module.d.ts +10 -0
  120. package/dist/lexicon-module.d.ts.map +1 -1
  121. package/dist/lexicon-module.js +53 -8
  122. package/dist/lexicon-plugin-helpers.d.ts.map +1 -1
  123. package/dist/lexicon-plugin-helpers.js +6 -4
  124. package/dist/lexicon.d.ts +29 -0
  125. package/dist/lexicon.d.ts.map +1 -1
  126. package/dist/lifecycle/affected.d.ts.map +1 -1
  127. package/dist/lifecycle/affected.js +7 -1
  128. package/dist/lifecycle/merge-record.d.ts +104 -0
  129. package/dist/lifecycle/merge-record.d.ts.map +1 -0
  130. package/dist/lifecycle/merge-record.js +102 -0
  131. package/dist/lifecycle/publish-attachments.d.ts +8 -5
  132. package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
  133. package/dist/lifecycle/publish-attachments.js +27 -13
  134. package/dist/lifecycle/publish-npm.d.ts +3 -2
  135. package/dist/lifecycle/publish-npm.d.ts.map +1 -1
  136. package/dist/lifecycle/publish-npm.js +4 -3
  137. package/dist/lifecycle/release-at.d.ts +40 -0
  138. package/dist/lifecycle/release-at.d.ts.map +1 -0
  139. package/dist/lifecycle/release-at.js +83 -0
  140. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  141. package/dist/lifecycle/release-cut.js +23 -12
  142. package/dist/lifecycle/release-ledger.d.ts +21 -0
  143. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  144. package/dist/lifecycle/release-ledger.js +24 -0
  145. package/dist/lifecycle/release-publish.d.ts +28 -12
  146. package/dist/lifecycle/release-publish.d.ts.map +1 -1
  147. package/dist/lifecycle/release-publish.js +29 -17
  148. package/dist/lifecycle/release-store.d.ts +25 -2
  149. package/dist/lifecycle/release-store.d.ts.map +1 -1
  150. package/dist/lifecycle/release-store.js +40 -13
  151. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  152. package/dist/lifecycle/run-ledger.js +1 -0
  153. package/dist/lifecycle/work-dir.d.ts +45 -0
  154. package/dist/lifecycle/work-dir.d.ts.map +1 -0
  155. package/dist/lifecycle/work-dir.js +78 -0
  156. package/dist/lint/discover.d.ts +17 -9
  157. package/dist/lint/discover.d.ts.map +1 -1
  158. package/dist/lint/discover.js +62 -54
  159. package/dist/lint/rules/op/index.d.ts +2 -1
  160. package/dist/lint/rules/op/index.d.ts.map +1 -1
  161. package/dist/lint/rules/op/index.js +4 -2
  162. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
  163. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
  164. package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
  165. package/dist/module-source.d.ts +57 -0
  166. package/dist/module-source.d.ts.map +1 -0
  167. package/dist/module-source.js +104 -0
  168. package/dist/op/activities/activity-contracts.d.ts +5 -0
  169. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  170. package/dist/op/activities/activity-contracts.js +3 -0
  171. package/dist/op/activities/factory.js +1 -1
  172. package/dist/op/activities/shell.d.ts +38 -0
  173. package/dist/op/activities/shell.d.ts.map +1 -1
  174. package/dist/op/activities/shell.js +89 -3
  175. package/dist/op/builders.d.ts +7 -0
  176. package/dist/op/builders.d.ts.map +1 -1
  177. package/dist/op/builders.js +7 -0
  178. package/dist/op/discover.d.ts.map +1 -1
  179. package/dist/op/discover.js +13 -3
  180. package/dist/op/gate.d.ts.map +1 -1
  181. package/dist/op/gate.js +3 -0
  182. package/dist/op/op-waves-run.d.ts.map +1 -1
  183. package/dist/op/op-waves-run.js +20 -5
  184. package/dist/op/operator.d.ts +2 -2
  185. package/dist/op/operator.js +2 -2
  186. package/dist/op/runtime.d.ts +2 -0
  187. package/dist/op/runtime.d.ts.map +1 -1
  188. package/dist/op/steward.d.ts +6 -4
  189. package/dist/op/steward.d.ts.map +1 -1
  190. package/dist/op/steward.js +5 -3
  191. package/dist/op/work-lease-run.d.ts +20 -7
  192. package/dist/op/work-lease-run.d.ts.map +1 -1
  193. package/dist/op/work-lease-run.js +56 -12
  194. package/dist/pr-forge.d.ts +10 -0
  195. package/dist/pr-forge.d.ts.map +1 -1
  196. package/dist/pr-forge.js +39 -0
  197. package/dist/pr-loop.d.ts +155 -7
  198. package/dist/pr-loop.d.ts.map +1 -1
  199. package/dist/pr-loop.js +137 -11
  200. package/dist/pr-report-url.d.ts +4 -4
  201. package/dist/pr-report-url.d.ts.map +1 -1
  202. package/dist/pr-report-url.js +5 -5
  203. package/dist/runner-start.d.ts +179 -0
  204. package/dist/runner-start.d.ts.map +1 -0
  205. package/dist/runner-start.js +277 -0
  206. package/dist/ts-loader.d.ts +38 -4
  207. package/dist/ts-loader.d.ts.map +1 -1
  208. package/dist/ts-loader.js +190 -16
  209. package/dist/wave-runtime.mjs +238 -231
  210. package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts +34 -0
  211. package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts.map +1 -0
  212. package/dist/workspace/chant-migrations/fountain-retired-kinds.js +176 -0
  213. package/dist/workspace/chant-migrations.d.ts.map +1 -1
  214. package/dist/workspace/chant-migrations.js +2 -0
  215. package/dist/workspace/check.schema.json +70 -2
  216. package/dist/workspace/checks/boxes.d.ts +6 -6
  217. package/dist/workspace/checks/boxes.js +9 -9
  218. package/dist/workspace/checks/security.d.ts +35 -0
  219. package/dist/workspace/checks/security.d.ts.map +1 -0
  220. package/dist/workspace/checks/security.js +305 -0
  221. package/dist/workspace/checks.d.ts +2 -0
  222. package/dist/workspace/checks.d.ts.map +1 -1
  223. package/dist/workspace/checks.js +5 -0
  224. package/dist/workspace/ci-builds.d.ts +163 -0
  225. package/dist/workspace/ci-builds.d.ts.map +1 -0
  226. package/dist/workspace/ci-builds.js +315 -0
  227. package/dist/workspace/ci-green-cli.d.ts +2 -1
  228. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  229. package/dist/workspace/ci-green-cli.js +25 -5
  230. package/dist/workspace/ci-green-forge.d.ts +18 -4
  231. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  232. package/dist/workspace/ci-green-forge.js +12 -0
  233. package/dist/workspace/ci-green-workflow.d.ts +55 -1
  234. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  235. package/dist/workspace/ci-green-workflow.js +128 -1
  236. package/dist/workspace/ci-green.d.ts +117 -16
  237. package/dist/workspace/ci-green.d.ts.map +1 -1
  238. package/dist/workspace/ci-green.js +358 -43
  239. package/dist/workspace/ci-last-green.schema.json +49 -0
  240. package/dist/workspace/ci-list.schema.json +47 -7
  241. package/dist/workspace/ci-report-url.d.ts +18 -0
  242. package/dist/workspace/ci-report-url.d.ts.map +1 -0
  243. package/dist/workspace/ci-report-url.js +33 -0
  244. package/dist/workspace/ci-run-index.d.ts +13 -1
  245. package/dist/workspace/ci-run-index.d.ts.map +1 -1
  246. package/dist/workspace/ci-run-index.js +6 -4
  247. package/dist/workspace/ci-run-index.schema.json +6 -4
  248. package/dist/workspace/ci-run.d.ts +85 -6
  249. package/dist/workspace/ci-run.d.ts.map +1 -1
  250. package/dist/workspace/ci-run.js +262 -64
  251. package/dist/workspace/ci-runner-key.d.ts +16 -3
  252. package/dist/workspace/ci-runner-key.d.ts.map +1 -1
  253. package/dist/workspace/ci-runner-key.js +30 -10
  254. package/dist/workspace/ci-runner-start.d.ts +77 -0
  255. package/dist/workspace/ci-runner-start.d.ts.map +1 -0
  256. package/dist/workspace/ci-runner-start.js +185 -0
  257. package/dist/workspace/ci-serve.d.ts +130 -11
  258. package/dist/workspace/ci-serve.d.ts.map +1 -1
  259. package/dist/workspace/ci-serve.js +323 -40
  260. package/dist/workspace/declaration.d.ts +25 -0
  261. package/dist/workspace/declaration.d.ts.map +1 -1
  262. package/dist/workspace/declaration.js +14 -2
  263. package/dist/workspace/declaration.schema.json +30 -2
  264. package/dist/workspace/export.d.ts +1 -1
  265. package/dist/workspace/export.d.ts.map +1 -1
  266. package/dist/workspace/export.js +7 -3
  267. package/dist/workspace/ls.d.ts +8 -1
  268. package/dist/workspace/ls.d.ts.map +1 -1
  269. package/dist/workspace/ls.js +2 -1
  270. package/dist/workspace/ls.schema.json +9 -2
  271. package/dist/workspace/member-pipeline.d.ts +1 -0
  272. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  273. package/dist/workspace/member-pipeline.js +2 -0
  274. package/dist/workspace/merge-record.schema.json +49 -0
  275. package/dist/workspace/pin-integrity.d.ts.map +1 -1
  276. package/dist/workspace/pin-integrity.js +3 -2
  277. package/dist/workspace/pr-report.schema.json +75 -8
  278. package/dist/workspace/reason-codes.d.ts +18 -1
  279. package/dist/workspace/reason-codes.d.ts.map +1 -1
  280. package/dist/workspace/reason-codes.js +20 -2
  281. package/dist/workspace/release-candidates.d.ts +129 -0
  282. package/dist/workspace/release-candidates.d.ts.map +1 -0
  283. package/dist/workspace/release-candidates.js +233 -0
  284. package/dist/workspace/release-candidates.schema.json +353 -0
  285. package/dist/workspace/run-event.schema.json +3 -1
  286. package/dist/workspace/security.d.ts +40 -0
  287. package/dist/workspace/security.d.ts.map +1 -0
  288. package/dist/workspace/security.js +99 -0
  289. package/dist/workspace/status.schema.json +3 -1
  290. package/dist/workspace/write-scope.d.ts +9 -0
  291. package/dist/workspace/write-scope.d.ts.map +1 -1
  292. package/dist/workspace/write-scope.js +11 -1
  293. package/package.json +8 -1
  294. package/src/__fixtures__/pr-loop/pr-apply-wave-waiting.note.golden.md +48 -0
  295. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
  296. package/src/ci-jobs.ts +11 -2
  297. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +3 -3
  298. package/src/cli/commands/audit.test.ts +2 -2
  299. package/src/cli/commands/check-lexicon-plugin.test.ts +9 -0
  300. package/src/cli/commands/check-lexicon-plugin.ts +11 -8
  301. package/src/cli/commands/check-lexicon.test.ts +1 -1
  302. package/src/cli/commands/check-lexicon.ts +16 -11
  303. package/src/cli/commands/import.ts +40 -0
  304. package/src/cli/commands/init-lexicon/templates/project.ts +4 -3
  305. package/src/cli/commands/init-lexicon.test.ts +8 -5
  306. package/src/cli/commands/onboard.test.ts +45 -0
  307. package/src/cli/commands/onboard.ts +8 -4
  308. package/src/cli/commands/update.test.ts +6 -0
  309. package/src/cli/commands/update.ts +4 -1
  310. package/src/cli/compiled-cli.e2e.test.ts +76 -11
  311. package/src/cli/dependencies.e2e.test.ts +101 -0
  312. package/src/cli/dependencies.test.ts +93 -0
  313. package/src/cli/dependencies.ts +122 -0
  314. package/src/cli/handlers/build.test.ts +105 -0
  315. package/src/cli/handlers/build.ts +35 -4
  316. package/src/cli/handlers/fan-out.test.ts +73 -0
  317. package/src/cli/handlers/fan-out.ts +11 -4
  318. package/src/cli/handlers/graph.test.ts +76 -0
  319. package/src/cli/handlers/graph.ts +19 -12
  320. package/src/cli/handlers/lifecycle-health.test.ts +123 -0
  321. package/src/cli/handlers/lifecycle-health.ts +157 -0
  322. package/src/cli/handlers/lifecycle.ts +1 -1
  323. package/src/cli/handlers/lint.test.ts +42 -0
  324. package/src/cli/handlers/lint.ts +6 -0
  325. package/src/cli/handlers/operator.ts +3 -3
  326. package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
  327. package/src/cli/handlers/pr-record.test.ts +201 -0
  328. package/src/cli/handlers/pr-waves.test.ts +449 -0
  329. package/src/cli/handlers/pr.test.ts +156 -0
  330. package/src/cli/handlers/pr.ts +648 -33
  331. package/src/cli/handlers/promote.test.ts +43 -1
  332. package/src/cli/handlers/promote.ts +19 -3
  333. package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
  334. package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
  335. package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
  336. package/src/cli/handlers/release.e2e.test.ts +19 -28
  337. package/src/cli/handlers/release.ts +81 -11
  338. package/src/cli/handlers/run-components-exit.test.ts +99 -0
  339. package/src/cli/handlers/run-signer.test.ts +79 -0
  340. package/src/cli/handlers/run.ts +34 -16
  341. package/src/cli/main.test.ts +18 -0
  342. package/src/cli/main.ts +120 -33
  343. package/src/cli/parse-args.ts +28 -0
  344. package/src/cli/registry.ts +35 -8
  345. package/src/codegen/generate-post-synth-barrel.ts +2 -2
  346. package/src/codegen/lexicon-regen.ts +1 -1
  347. package/src/codegen/pinned-upgrade.ts +2 -2
  348. package/src/codegen/post-synth-barrel-guards.test.ts +6 -11
  349. package/src/components/capability.ts +51 -3
  350. package/src/components/changed-files.test.ts +16 -1
  351. package/src/components/changed-files.ts +9 -1
  352. package/src/components/deploy-inputs.test.ts +108 -0
  353. package/src/components/deploy-inputs.ts +225 -0
  354. package/src/components/driver.test.ts +36 -1
  355. package/src/components/driver.ts +87 -4
  356. package/src/components/fan-out-record.ts +29 -1
  357. package/src/components/fan-out-run.test.ts +35 -0
  358. package/src/components/fan-out-run.ts +40 -1
  359. package/src/components/gate-plan.ts +9 -0
  360. package/src/components/pr-pipeline.test.ts +100 -0
  361. package/src/components/pr-pipeline.ts +184 -28
  362. package/src/components/pr-run.test.ts +323 -18
  363. package/src/components/pr-run.ts +355 -76
  364. package/src/components/release-compile.test.ts +34 -0
  365. package/src/components/release-compile.ts +89 -8
  366. package/src/components/release-promote.test.ts +55 -2
  367. package/src/components/release-promote.ts +93 -23
  368. package/src/components/verbs/bom-writer.ts +1 -1
  369. package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
  370. package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
  371. package/src/components/wave-plan.ts +48 -8
  372. package/src/discovery/fold-host-method.test.ts +87 -0
  373. package/src/discovery/fold-import.ts +15 -5
  374. package/src/discovery/fold-package-composite.test.ts +27 -0
  375. package/src/discovery/fold-specifiers.test.ts +8 -0
  376. package/src/discovery/sandbox/bundle.ts +6 -0
  377. package/src/discovery/specifier-resolve.ts +8 -2
  378. package/src/fold/fold.test.ts +72 -0
  379. package/src/fold/fold.ts +71 -0
  380. package/src/fold/subset.test.ts +31 -0
  381. package/src/fold/subset.ts +99 -0
  382. package/src/graph-detail.test.ts +57 -0
  383. package/src/graph-detail.ts +33 -7
  384. package/src/health.schema.json +191 -0
  385. package/src/health.test.ts +62 -0
  386. package/src/health.ts +178 -0
  387. package/src/lexicon-dist.e2e.test.ts +254 -0
  388. package/src/lexicon-module.test.ts +47 -0
  389. package/src/lexicon-module.ts +49 -7
  390. package/src/lexicon-plugin-helpers.ts +6 -4
  391. package/src/lexicon.ts +43 -1
  392. package/src/lifecycle/affected.test.ts +7 -0
  393. package/src/lifecycle/affected.ts +6 -1
  394. package/src/lifecycle/merge-record.test.ts +111 -0
  395. package/src/lifecycle/merge-record.ts +154 -0
  396. package/src/lifecycle/publish-attachments.ts +25 -13
  397. package/src/lifecycle/publish-npm.ts +4 -3
  398. package/src/lifecycle/publish-targets.test.ts +34 -4
  399. package/src/lifecycle/release-at.test.ts +48 -0
  400. package/src/lifecycle/release-at.ts +100 -0
  401. package/src/lifecycle/release-cut.test.ts +41 -0
  402. package/src/lifecycle/release-cut.ts +22 -11
  403. package/src/lifecycle/release-ledger.ts +34 -0
  404. package/src/lifecycle/release-publish.test.ts +43 -13
  405. package/src/lifecycle/release-publish.ts +38 -18
  406. package/src/lifecycle/release-store.ts +57 -13
  407. package/src/lifecycle/run-ledger.test.ts +13 -0
  408. package/src/lifecycle/run-ledger.ts +1 -0
  409. package/src/lifecycle/work-dir.test.ts +77 -0
  410. package/src/lifecycle/work-dir.ts +82 -0
  411. package/src/lint/discover.test.ts +46 -2
  412. package/src/lint/discover.ts +55 -49
  413. package/src/lint/rules/op/index.ts +4 -2
  414. package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
  415. package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
  416. package/src/module-source.test.ts +85 -0
  417. package/src/module-source.ts +112 -0
  418. package/src/op/activities/activity-contracts.ts +3 -0
  419. package/src/op/activities/factory.ts +1 -1
  420. package/src/op/activities/shell-container.e2e.test.ts +67 -0
  421. package/src/op/activities/shell.test.ts +44 -1
  422. package/src/op/activities/shell.ts +110 -3
  423. package/src/op/builders.ts +7 -0
  424. package/src/op/discover-root.test.ts +31 -2
  425. package/src/op/discover.ts +14 -4
  426. package/src/op/gate.test.ts +30 -0
  427. package/src/op/gate.ts +3 -0
  428. package/src/op/op-waves-run.ts +20 -5
  429. package/src/op/op-waves.test.ts +49 -0
  430. package/src/op/operator.ts +2 -2
  431. package/src/op/runtime.ts +2 -0
  432. package/src/op/step-console.test.ts +4 -2
  433. package/src/op/steward.ts +6 -4
  434. package/src/op/work-lease-run.test.ts +88 -2
  435. package/src/op/work-lease-run.ts +52 -12
  436. package/src/pr-forge-bundle.test.ts +2 -2
  437. package/src/pr-forge.test.ts +42 -0
  438. package/src/pr-forge.ts +40 -0
  439. package/src/pr-loop-bundle.test.ts +2 -1
  440. package/src/pr-loop.test.ts +146 -1
  441. package/src/pr-loop.ts +278 -17
  442. package/src/pr-report-url.ts +7 -7
  443. package/src/runner-start-bundle.test.ts +29 -0
  444. package/src/runner-start.test.ts +179 -0
  445. package/src/runner-start.ts +329 -0
  446. package/src/terraform/__fixtures__/advise.test.ts +21 -2
  447. package/src/ts-loader.test.ts +89 -1
  448. package/src/ts-loader.ts +180 -13
  449. package/src/workspace/changes.test.ts +1 -1
  450. package/src/workspace/chant-migrations/fountain-retired-kinds.test.ts +33 -0
  451. package/src/workspace/chant-migrations/fountain-retired-kinds.ts +190 -0
  452. package/src/workspace/chant-migrations.e2e.test.ts +5 -0
  453. package/src/workspace/chant-migrations.ts +2 -0
  454. package/src/workspace/check-contract.test.ts +3 -2
  455. package/src/workspace/check.schema.json +70 -2
  456. package/src/workspace/checks/boxes.ts +9 -9
  457. package/src/workspace/checks/security.test.ts +138 -0
  458. package/src/workspace/checks/security.ts +305 -0
  459. package/src/workspace/checks.ts +5 -0
  460. package/src/workspace/ci-builds.e2e.test.ts +208 -0
  461. package/src/workspace/ci-builds.test.ts +174 -0
  462. package/src/workspace/ci-builds.ts +385 -0
  463. package/src/workspace/ci-green-builds.test.ts +180 -0
  464. package/src/workspace/ci-green-cli.test.ts +54 -0
  465. package/src/workspace/ci-green-cli.ts +26 -5
  466. package/src/workspace/ci-green-forge.ts +30 -4
  467. package/src/workspace/ci-green-runs.test.ts +53 -11
  468. package/src/workspace/ci-green-workflow.test.ts +84 -1
  469. package/src/workspace/ci-green-workflow.ts +139 -2
  470. package/src/workspace/ci-green.test.ts +6 -3
  471. package/src/workspace/ci-green.ts +408 -48
  472. package/src/workspace/ci-last-green.schema.json +49 -0
  473. package/src/workspace/ci-list.schema.json +47 -7
  474. package/src/workspace/ci-report-url.test.ts +220 -0
  475. package/src/workspace/ci-report-url.ts +33 -0
  476. package/src/workspace/ci-run-index.schema.json +6 -4
  477. package/src/workspace/ci-run-index.test.ts +42 -3
  478. package/src/workspace/ci-run-index.ts +19 -5
  479. package/src/workspace/ci-run.test.ts +203 -8
  480. package/src/workspace/ci-run.ts +295 -63
  481. package/src/workspace/ci-runner-key.test.ts +55 -17
  482. package/src/workspace/ci-runner-key.ts +36 -11
  483. package/src/workspace/ci-runner-start.test.ts +151 -0
  484. package/src/workspace/ci-runner-start.ts +193 -0
  485. package/src/workspace/ci-serve-builds.test.ts +86 -0
  486. package/src/workspace/ci-serve.test.ts +209 -6
  487. package/src/workspace/ci-serve.ts +341 -42
  488. package/src/workspace/declaration.schema.json +30 -2
  489. package/src/workspace/declaration.test.ts +40 -0
  490. package/src/workspace/declaration.ts +40 -3
  491. package/src/workspace/export.ts +6 -3
  492. package/src/workspace/ls.schema.json +9 -2
  493. package/src/workspace/ls.ts +10 -4
  494. package/src/workspace/member-pipeline.ts +2 -1
  495. package/src/workspace/merge-record.schema.json +49 -0
  496. package/src/workspace/pin-integrity.test.ts +5 -0
  497. package/src/workspace/pin-integrity.ts +3 -2
  498. package/src/workspace/pr-report.schema.json +75 -8
  499. package/src/workspace/reason-codes.test.ts +2 -0
  500. package/src/workspace/reason-codes.ts +20 -2
  501. package/src/workspace/release-candidates.schema.json +353 -0
  502. package/src/workspace/release-candidates.test.ts +106 -0
  503. package/src/workspace/release-candidates.ts +315 -0
  504. package/src/workspace/run-event.schema.json +3 -1
  505. package/src/workspace/security.test.ts +70 -0
  506. package/src/workspace/security.ts +118 -0
  507. package/src/workspace/status.schema.json +3 -1
  508. package/src/workspace/write-scope.test.ts +29 -1
  509. package/src/workspace/write-scope.ts +12 -1
@@ -0,0 +1,99 @@
1
+ /**
2
+ * `chant run --components` exits 3 when a step refuses a plan that moved
3
+ * since it was approved (#3997), on the single-component path and on the
4
+ * driver's `all` and list paths. Discovery and the capability registry are
5
+ * fakes; the run is the real `runComponents` on the local runtime.
6
+ */
7
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
8
+ import type { Component } from "../../components/component";
9
+ import type { ParsedArgs } from "../registry";
10
+
11
+ const discoverComponentsMock = vi.fn();
12
+ vi.mock("../../components/discover", () => ({
13
+ discoverComponents: (...args: unknown[]) => discoverComponentsMock(...args),
14
+ }));
15
+
16
+ vi.mock("../../config", async () => {
17
+ const actual = await vi.importActual<typeof import("../../config")>("../../config");
18
+ return { ...actual, loadChantConfig: async () => ({ config: {} }) };
19
+ });
20
+
21
+ /** The error a component's step throws, in place of applying. */
22
+ const refusal = new Map<string, Error>();
23
+
24
+ vi.mock("../../op/runtimes/local", async () => {
25
+ const { CapabilityRegistry } = await import("../../components/capability");
26
+ const { runComponents } = await import("../../components/cli-support");
27
+ const { memoryGateLedgerPort } = await import("../../op/gate");
28
+ const registry = new CapabilityRegistry();
29
+ registry.register({
30
+ kind: "fake-apply",
31
+ async run(ctx: { component: string }) {
32
+ const err = refusal.get(ctx.component);
33
+ if (err) throw err;
34
+ return {};
35
+ },
36
+ } as never);
37
+ return {
38
+ createLocalOpRuntime: () => ({
39
+ name: "local",
40
+ runComponents: (path: string, selector: string, options: object) =>
41
+ runComponents(path, selector, { ...options, registry, config: {}, revision: null, gates: memoryGateLedgerPort() }),
42
+ }),
43
+ };
44
+ });
45
+
46
+ const { runOpComponents } = await import("./run");
47
+
48
+ const component = (name: string, dependsOn: string[] = []): Component => ({
49
+ name,
50
+ dependsOn,
51
+ deploy: [{ phase: "Apply", steps: [{ kind: "fake-apply" }] }],
52
+ });
53
+
54
+ const ctx = (selector: string) =>
55
+ ({
56
+ args: { command: "run", path: selector, components: true, on: "local", format: "", fix: false, watch: false, verbose: false, help: false, live: false } as ParsedArgs,
57
+ plugins: [],
58
+ }) as never;
59
+
60
+ beforeEach(() => {
61
+ refusal.clear();
62
+ const estate = [component("net"), component("app", ["net"])];
63
+ discoverComponentsMock.mockResolvedValue({
64
+ components: new Map(estate.map((c) => [c.name, { component: c, exportName: c.name, filePath: `${c.name}.component.ts` }])),
65
+ sourceFiles: [],
66
+ errors: [],
67
+ });
68
+ vi.spyOn(console, "error").mockImplementation(() => {});
69
+ vi.spyOn(console, "log").mockImplementation(() => {});
70
+ vi.spyOn(process.stderr, "write").mockImplementation(() => true);
71
+ });
72
+
73
+ afterEach(() => {
74
+ vi.restoreAllMocks();
75
+ });
76
+
77
+ const marks: Array<[string, number, object]> = [
78
+ ["marked planMoved", 3, { planMoved: true }],
79
+ ["with exitCode 3", 3, { exitCode: 3 }],
80
+ ["with exitCode 7", 1, { exitCode: 7 }],
81
+ ["unmarked", 1, {}],
82
+ ];
83
+
84
+ describe("chant run --components: a step that refuses a moved plan exits 3 (#3997)", () => {
85
+ test.each(marks)("one component, its step refusing %s: exit %i", async (_label, code, mark) => {
86
+ refusal.set("app", Object.assign(new Error("app: refusing to apply, the plan moved"), mark));
87
+ expect(await runOpComponents(ctx("app"))).toBe(code);
88
+ });
89
+
90
+ test.each(marks)("all, a step refusing %s: exit %i", async (_label, code, mark) => {
91
+ refusal.set("net", Object.assign(new Error("net: refusing to apply, the plan moved"), mark));
92
+ expect(await runOpComponents(ctx("all"))).toBe(code);
93
+ });
94
+
95
+ test("a list that pulls in a dependency whose step refuses a moved plan exits 3", async () => {
96
+ refusal.set("net", Object.assign(new Error("net: refusing to apply, the plan moved"), { planMoved: true }));
97
+ expect(await runOpComponents(ctx("app,net"))).toBe(3);
98
+ });
99
+ });
@@ -0,0 +1,79 @@
1
+ /**
2
+ * #4156: `chant run`'s runner key is opt-in hardening. A `CHANT_RUNNER_KEY`
3
+ * that `.chant/trust.json` does not list leaves the run unsigned with no
4
+ * warning, unless the declaration sets `ci.green.signedSince`. A key that is
5
+ * not an Ed25519 PEM is an error either way.
6
+ */
7
+
8
+ import { createPublicKey, generateKeyPairSync } from "node:crypto";
9
+ import { afterEach, describe, expect, test, vi } from "vitest";
10
+ import { cleanScratch, declaration, git, scratchDir, writeFiles } from "../../workspace/__fixtures__/contract-repo";
11
+ import { sshFromEd25519 } from "../../workspace/trust/dsse";
12
+ import type { CommandContext } from "../registry";
13
+ import { runSignerFor } from "./run";
14
+
15
+ const home = process.cwd();
16
+ const saved = process.env.CHANT_RUNNER_KEY;
17
+ afterEach(() => {
18
+ process.chdir(home);
19
+ if (saved === undefined) delete process.env.CHANT_RUNNER_KEY;
20
+ else process.env.CHANT_RUNNER_KEY = saved;
21
+ vi.restoreAllMocks();
22
+ cleanScratch();
23
+ });
24
+
25
+ function key(): { pem: string; ssh: string } {
26
+ const { privateKey } = generateKeyPairSync("ed25519");
27
+ const jwk = createPublicKey(privateKey).export({ format: "jwk" }) as { x: string };
28
+ return { pem: privateKey.export({ type: "pkcs8", format: "pem" }).toString(), ssh: sshFromEd25519(Buffer.from(jwk.x, "base64url")) };
29
+ }
30
+
31
+ function repo(green?: Record<string, unknown>): string {
32
+ const root = scratchDir("chant-run-signer-");
33
+ git(root, "init", "-q");
34
+ for (const [k, v] of [["user.name", "t"], ["user.email", "t@example.com"], ["commit.gpgsign", "false"]]) git(root, "config", k!, v!);
35
+ writeFiles(root, {
36
+ "chant.workspace.json": declaration([{ name: "app", dir: "app", kind: "other", because: "the app" }], green ? { ci: { green } } : {}),
37
+ ".chant/trust.json": JSON.stringify({ schema: 1, runners: [{ principal: "ci-bot", class: "runner", key: key().ssh }], roles: { runner: ["ci-bot"] } }),
38
+ "app/a.txt": "a\n",
39
+ });
40
+ git(root, "add", "-A");
41
+ git(root, "commit", "-q", "-m", "init");
42
+ return root;
43
+ }
44
+
45
+ async function signer(root: string, pem: string, verbose = false) {
46
+ process.chdir(root);
47
+ process.env.CHANT_RUNNER_KEY = pem;
48
+ const err: string[] = [];
49
+ vi.spyOn(console, "error").mockImplementation((s: string) => void err.push(String(s)));
50
+ const result = await runSignerFor({ args: { verbose } } as unknown as CommandContext);
51
+ return { result, err: err.join("\n") };
52
+ }
53
+
54
+ describe("chant run's runner key from the environment (#4156)", () => {
55
+ test("a key trust.json does not list leaves the run unsigned and quiet, with one line under --verbose", async () => {
56
+ const root = repo();
57
+ const quiet = await signer(root, key().pem);
58
+ expect(quiet.result).toBeUndefined();
59
+ expect(quiet.err).toBe("");
60
+ const verbose = await signer(root, key().pem, true);
61
+ expect(verbose.result).toBeUndefined();
62
+ expect(verbose.err).toContain("CHANT_RUNNER_KEY is set, and the run is not signed");
63
+ });
64
+
65
+ test("with ci.green.signedSince the same key warns", async () => {
66
+ const root = repo();
67
+ const head = git(root, "rev-parse", "HEAD");
68
+ writeFiles(root, { "chant.workspace.json": declaration([{ name: "app", dir: "app", kind: "other", because: "the app" }], { ci: { green: { branch: "main", phases: { test: ["test"] }, require: ["test"], signedSince: head } } }) });
69
+ const r = await signer(root, key().pem);
70
+ expect(r.result).toBeUndefined();
71
+ expect(r.err).toContain(`ci.green.signedSince (${head}) will not count it`);
72
+ });
73
+
74
+ test("a key that is not an Ed25519 PEM is an error", async () => {
75
+ const r = await signer(repo(), "not a key");
76
+ expect(r.result).toBeNull();
77
+ expect(r.err).toContain("CHANT_RUNNER_KEY is not an Ed25519 private key in PEM");
78
+ });
79
+ });
@@ -31,7 +31,7 @@ import type { RunEvent } from "../../run-event";
31
31
  import { openCliRunEvents, refusesBothStreams, stdoutLine } from "../run-events";
32
32
  import { maybeRecordAutoRelease } from "../../components/auto-release";
33
33
  import { maybePersistBuildManifest } from "../../components/manifest-persistence";
34
- import type { DriverComponentResult } from "../../components/driver";
34
+ import { failedRunExitCode, type DriverComponentResult } from "../../components/driver";
35
35
  import { isComponentList, type ComponentRunPlan, type RunComponentsResult } from "../../components/cli-support";
36
36
  import { resolveCiRun, type ReleaseRecord, type RunOrigin } from "../../lifecycle/release-ledger";
37
37
  import { gateAdmission, gateApprover } from "../../components/promote";
@@ -730,9 +730,9 @@ type StewardTurnGate =
730
730
  * `chant run <op>` for an Op a declared steward lists is one of that
731
731
  * steward's turns, not a run beside it (#2750, follow-up to #2731).
732
732
  *
733
- * On fountain, the run belongs on the steward's thread: without `--on
733
+ * On fountain, the run belongs in the steward's conversation: without `--on
734
734
  * fountain` this refuses outright, naming the command that posts it there
735
- * instead (the fountain runtime's own turn already refuses a busy teammate,
735
+ * instead (the fountain runtime's own turn already refuses a busy steward,
736
736
  * unretried — see chant-fountain-ops).
737
737
  *
738
738
  * In the local form, the run takes the steward's turn lease
@@ -771,15 +771,15 @@ async function stewardTurnGate(opName: string, ctx: CommandContext): Promise<Ste
771
771
  const form = stewardFormFor(owner, env);
772
772
 
773
773
  if (form === "fountain") {
774
- // `run.on: "fountain"` asks for the steward's thread as much as the flag
774
+ // `run.on: "fountain"` asks for the steward's conversation as much as the flag
775
775
  // does (#2523).
776
776
  if ((await chooseOpRuntime(ctx, true))?.on === "fountain") return { ok: true };
777
777
  return {
778
778
  ok: false,
779
779
  message: `Op "${opName}" is one of steward "${owner.name}"'s Ops, which runs on fountain in environment "${env}"`,
780
780
  hint:
781
- `Run \`chant run ${opName} --on fountain\` so it posts to "${owner.name}"'s thread. ` +
782
- `A plain \`chant run ${opName}\` would run it outside that thread, beside the steward instead of one of its turns.`,
781
+ `Run \`chant run ${opName} --on fountain\` so it posts to "${owner.name}"'s conversation. ` +
782
+ `A plain \`chant run ${opName}\` would run it outside that conversation, beside the steward instead of one of its turns.`,
783
783
  };
784
784
  }
785
785
 
@@ -1283,7 +1283,10 @@ async function runComponentSelection(ctx: CommandContext): Promise<number> {
1283
1283
  }
1284
1284
  }
1285
1285
 
1286
- return result.success ? 0 : 1;
1286
+ // A step that refused a plan that moved since it was approved exits 3
1287
+ // (#3997). `--gated-exit` doesn't remap it: the run failed.
1288
+ if (result.success) return 0;
1289
+ return result.run ? failedRunExitCode(result.run.results) : 1;
1287
1290
  }
1288
1291
 
1289
1292
  /**
@@ -1359,9 +1362,10 @@ async function runOpOnResolvedRuntime(ctx: CommandContext): Promise<number> {
1359
1362
  return 1;
1360
1363
  }
1361
1364
 
1362
- // #4044: a runner key signs the run's record as it settles, so `ci tick`
1363
- // can count the run. `--key` must be a runner key; the PEM in
1364
- // CHANT_RUNNER_KEY that is not one leaves the run unsigned, with a warning.
1365
+ // #4044: a runner key signs the run's record as it settles, so a reader
1366
+ // can see who ran it. `--key` must be a runner key; the PEM in
1367
+ // CHANT_RUNNER_KEY that trust.json does not list leaves the run unsigned,
1368
+ // with a warning only when ci.green.signedSince asks for signed runs (#4156).
1365
1369
  const signRecord = await runSignerFor(ctx);
1366
1370
  if (signRecord === null) {
1367
1371
  await stewardGate?.release?.();
@@ -1564,10 +1568,13 @@ const RUNNER_KEY_ENV = "CHANT_RUNNER_KEY";
1564
1568
  * What signs the run's record (#4044): the runner key `--key` names, or the
1565
1569
  * PEM in `CHANT_RUNNER_KEY`, checked against `.chant/trust.json` at `HEAD`.
1566
1570
  * Undefined when neither is given. Null, after printing why, when `--key`
1567
- * is not a runner key; an unusable `CHANT_RUNNER_KEY` only warns, since the
1568
- * variable may be set for `ci tick` in a job that also runs Ops.
1571
+ * is not a runner key, or either is not an Ed25519 PEM. A `CHANT_RUNNER_KEY`
1572
+ * that trust.json does not list leaves the run unsigned (#4156): quietly,
1573
+ * with a line under `--verbose`, unless `ci.green.signedSince` asks for
1574
+ * signed runs, which warns. The variable may be set for `ci tick` in a job
1575
+ * that also runs Ops.
1569
1576
  */
1570
- async function runSignerFor(ctx: CommandContext): Promise<((record: OpRunRecord) => NonNullable<OpRunRecord["statement"]>) | undefined | null> {
1577
+ export async function runSignerFor(ctx: CommandContext): Promise<((record: OpRunRecord) => NonNullable<OpRunRecord["statement"]>) | undefined | null> {
1571
1578
  const fromFlag = ctx.args.key !== undefined;
1572
1579
  let pem: string | undefined;
1573
1580
  try {
@@ -1577,9 +1584,17 @@ async function runSignerFor(ctx: CommandContext): Promise<((record: OpRunRecord)
1577
1584
  return null;
1578
1585
  }
1579
1586
  if (pem === undefined) return undefined;
1587
+ // Loaded only when a key is given, so a run without one loads no workspace code (level 0, #2525).
1588
+ const { opRunSigner, signedRunsRequired } = await import("../../workspace/ci-green");
1589
+ const { loadRunnerKey } = await import("../../workspace/trust/dsse");
1590
+ try {
1591
+ loadRunnerKey(pem);
1592
+ } catch (err) {
1593
+ // A key that is there and is not one is an error either way: the caller asked to sign (#4156).
1594
+ console.error(formatError({ message: `${fromFlag ? `--key ${ctx.args.key}` : RUNNER_KEY_ENV} is not an Ed25519 private key in PEM: ${err instanceof Error ? err.message : String(err)}` }));
1595
+ return null;
1596
+ }
1580
1597
  try {
1581
- // Loaded only when a key is given, so a run without one loads no workspace code (level 0, #2525).
1582
- const { opRunSigner } = await import("../../workspace/ci-green");
1583
1598
  return opRunSigner(process.cwd(), pem);
1584
1599
  } catch (err) {
1585
1600
  const message = err instanceof Error ? err.message : String(err);
@@ -1587,7 +1602,10 @@ async function runSignerFor(ctx: CommandContext): Promise<((record: OpRunRecord)
1587
1602
  console.error(formatError({ message: `the run would not be signed: ${message}` }));
1588
1603
  return null;
1589
1604
  }
1590
- console.error(formatWarning({ message: `${RUNNER_KEY_ENV} is set, and the run will not be signed: ${message}` }));
1605
+ // #4156: signing is opt-in, so a key trust.json does not list warns only when ci.green.signedSince asks for signed runs.
1606
+ const required = signedRunsRequired(process.cwd());
1607
+ if (required) console.error(formatWarning({ message: `${RUNNER_KEY_ENV} is set, and the run will not be signed, so ci.green.signedSince (${required}) will not count it: ${message}` }));
1608
+ else if (ctx.args.verbose) console.error(`${RUNNER_KEY_ENV} is set, and the run is not signed: ${message}`);
1591
1609
  return undefined;
1592
1610
  }
1593
1611
  }
@@ -666,6 +666,18 @@ describe("resolveCommand", () => {
666
666
  expect(result!.compound).toBe(false);
667
667
  });
668
668
 
669
+ test("`sync` is an alias for `update` (#3988)", () => {
670
+ const result = resolveCommand(makeArgs({ command: "sync" }), [...testRegistry, { name: "update", handler: noop }]);
671
+ expect(result!.def.name).toBe("update");
672
+ expect(result!.compound).toBe(false);
673
+ });
674
+
675
+ test("`sync <path>` resolves to `update` with the path kept (#3988)", () => {
676
+ const result = resolveCommand(makeArgs({ command: "sync", path: "./proj" }), [...testRegistry, { name: "update", handler: noop }]);
677
+ expect(result!.def.name).toBe("update");
678
+ expect(result!.compound).toBe(false);
679
+ });
680
+
669
681
  test("resolves components status as compound command", () => {
670
682
  const result = resolveCommand(makeArgs({ command: "components", path: "status" }), testRegistry);
671
683
  expect(result!.def.name).toBe("components status");
@@ -1021,4 +1033,10 @@ describe("chant --help", () => {
1021
1033
  const commands = new Set(commandRegistry.map((def) => def.name.split(" ")[0]));
1022
1034
  expect([...commands].filter((name) => !listed.has(name))).toEqual([]);
1023
1035
  });
1036
+
1037
+ test("names `sync` as the alias of `update` (#3988)", () => {
1038
+ const source = readFileSync(join(import.meta.dirname, "main.ts"), "utf-8");
1039
+ const help = source.slice(source.indexOf("function printHelp("), source.indexOf("Options:", source.indexOf("function printHelp(")));
1040
+ expect(help).toMatch(/^ {2}update \(alias: sync\)/m);
1041
+ });
1024
1042
  });
package/src/cli/main.ts CHANGED
@@ -238,33 +238,45 @@ CI:
238
238
  its window, newest first, each green, revoked or
239
239
  undecided with its required phases' results. Reads
240
240
  local tags; --forge reads check runs for undecided
241
- commits, and source runs reads the signed Op runs on
242
- the local chant/lifecycle. --since replaces the window.
241
+ commits, and source runs reads the Op runs on the
242
+ local chant/lifecycle, signed or not. --since replaces the window.
243
243
  ci tick [--dry-run] [--forge github] [--source checks|runs] [--key <runner key.pem>]
244
244
  Tag each commit within ci.green's window whose
245
245
  required phases all passed ci/green/<sha>, and revoke
246
246
  a green one that now fails ci/revoked/<sha>. Reads
247
247
  check runs from the forge, or with source runs
248
248
  (ci.green.source or --source) the Op runs on
249
- chant/lifecycle that a runner key signed. Pushes the
249
+ chant/lifecycle; with ci.green.signedSince, the ones a
250
+ runner key signed. Pushes the
250
251
  tags. --key, or the PEM in CHANT_RUNNER_KEY, signs
251
- each tag with a runner key.
252
+ each tag with a runner key. A cancelled run leaves a
253
+ commit unbuilt, not red, and a failed run is run
254
+ again once. A phase with build: newest leaves the
255
+ commits a newer run superseded unbuilt, and a red
256
+ one starts a bisect: a ci/build/<sha> tag the tick
257
+ starts the phase's workflow at. Each commit judged
258
+ gets a _ci/<sha>.json record on chant/lifecycle.
252
259
  ci run plan|apply --env <env> --base <sha> [--pr <n>] [--commit <sha>]
253
- [--forge github|gitlab|forgejo] [--gate <name>] [--branch <name>]
254
- [--events-json] [--report-url <url>] [--image <image>]
260
+ [--forge github|gitlab|forgejo] [--gate <name>] [--wave-gate <name>]
261
+ [--branch <name>] [--events-json] [--report-url <url>] [--image <image>]
255
262
  [--before-script <line>]... [--pr-build]
256
263
  [--pr-build-command <line>] [--chant <command>]
257
264
  [--pass-env <NAME>]... [--secrets <file>] [--key <runner key.pem>]
258
- [-o <dir>]
265
+ [-o <dir>] [--wave <k>] [--require-review]
259
266
  [--no-status] [--status-url <url template>]
260
- Run the pull-request pipeline's plan or apply job as
267
+ [--in-place | --local] [--no-push] [--require-runner-start] [--verbose]
268
+ Run the pull-request pipeline's plan, record or apply job as
261
269
  a pipeline Op in a fresh worktree of --commit (default
262
270
  HEAD) with a clean environment. --base and --pr stand
263
271
  in for the forge's event values. --pr-build runs
264
272
  the job's build and lint lines first. Records the run as
265
- pr-plan or pr-apply on chant/lifecycle, signed with
266
- --key or CHANT_RUNNER_KEY, so ci tick from runs counts
267
- it, and copies the report to -o (default .chant/pr). With a forge
273
+ pr-plan, pr-record or pr-apply on chant/lifecycle, which ci tick
274
+ from runs counts, signed when --key or CHANT_RUNNER_KEY
275
+ gives a runner key, and copies the report to -o
276
+ (default .chant/pr). --in-place runs in this checkout
277
+ at HEAD, as a generated forge job does, and names the
278
+ forge's run in the record; --no-push keeps the record
279
+ local. Exits with the command's code. With a forge
268
280
  token it sets a chant/pr-plan or chant/pr-apply commit
269
281
  status (pending, then success or failure); --no-status
270
282
  turns that off, and --status-url links it.
@@ -275,24 +287,55 @@ CI:
275
287
  With --report-url it also adds the run to
276
288
  <url>/runs/index.json, newest first, cut to the
277
289
  newest --run-index-limit entries (default 200).
278
- ci runs --report-url <url> [--json]
279
- List the runs in the index at the report URL, newest
280
- first.
290
+ Without --report-url, the workspace's ci.reportUrl.
291
+ --local runs the job on this machine: the record is
292
+ kept on the local chant/lifecycle with runOrigin
293
+ local and is never pushed, no status is set, nothing
294
+ goes under a report URL, and ci tick does not count it.
295
+ --require-runner-start refuses the job before its
296
+ first step unless a runner-start statement, signed by
297
+ a runner key .chant/trust.json at --base lists, names
298
+ this forge job.
299
+ ci runner-start sign --key <runner key.pem> --principal <name> --job <json|@file> [-o <file>]
300
+ For the provisioner that starts a runner for one job:
301
+ sign the job's system, repo, run, attempt, job,
302
+ commit and runner name, and print the envelope.
303
+ ci runner-start verify [--base <rev>] [--commit <sha>] [--json]
304
+ In the job: check the statement in CHANT_RUNNER_START,
305
+ or the file CHANT_RUNNER_START_FILE names (default
306
+ /run/chant/runner-start.json), against the forge's
307
+ variables and .chant/trust.json at --base (default
308
+ origin/HEAD). Exits 1 unless it verifies.
309
+ ci run --at <sha> Ask for every required phase at a commit of the
310
+ green branch with a ci/build/<sha> tag, and start the
311
+ workflows holding them there on GitHub when a token
312
+ is set; otherwise the next tick, or ci serve
313
+ --build-command, does.
314
+ ci runs [--report-url <url>] [--json]
315
+ List the runs in the index at the report URL (default
316
+ the workspace's ci.reportUrl), newest first.
281
317
  ci serve --forge forgejo|github --repo <owner/repo> --env <env>
282
318
  [--interval <s>] [--max-jobs <n>] [--state <dir>]
283
319
  [--author <login>]... [--once] [--rerun-failed] [--unsigned]
284
- [ci run's job flags]
320
+ [--apply-on-merge] [--build-command <line>] [ci run's job flags]
285
321
  Poll the forge for open pull requests and the target
286
322
  branch's head, and run ci run plan for each new pull
287
- request head and ci run apply for each new commit on
288
- the branch. Dedupes on job and commit in a state file
323
+ request head and ci run record for each new commit on
324
+ the branch, then ci tick from the runs when the
325
+ workspace declares ci.green. A merge deploys nothing;
326
+ --apply-on-merge runs ci run apply in place of the
327
+ record, and --wave-gate needs it. Dedupes on job and commit in a state file
289
328
  under --state, takes a lease per apply group, keeps
290
329
  each apply's resume record there, and runs at most
291
330
  --max-jobs (default 1) jobs at once. --once runs one
292
331
  poll and waits for its jobs. --rerun-failed runs a
293
- failed apply or plan again. Refuses to start when its
294
- runner key is not listed in .chant/trust.json at the
295
- target's head, unless --unsigned
332
+ failed apply or plan again. Signs runs with a runner
333
+ key when .chant/trust.json at the target's head lists
334
+ it, and serves unsigned otherwise; with
335
+ ci.green.signedSince it refuses to start without one.
336
+ --build-command runs <line> once per ci/build/<sha>
337
+ tag, in a fresh worktree of the commit, with
338
+ CHANT_BUILD_COMMIT and CHANT_BUILD_PHASES set
296
339
  ci serve|run --setup-key [--trust [--principal <name>]] [--state <dir>] [--branch <name>]
297
340
  Make the host's runner key (Ed25519, mode 0600) in the
298
341
  state directory when there is none and print its public
@@ -310,10 +353,14 @@ CI:
310
353
  or a GitHub App's id and key, pushes the tags with a
311
354
  token that may tag commits changing workflow files.
312
355
  --runner-key-secret hands the tick the runner key it
313
- signs its tags with.
356
+ signs its tags with. For each phase that sets build,
357
+ it also writes the triggers and concurrency of the
358
+ workflows holding it: newest builds on pushes and by
359
+ hand, one run at a time, and every gives each commit
360
+ a run of its own.
314
361
 
315
362
  Releases:
316
- release cut [<label>] [--yes] [--build <command>] [--param <k=v>]
363
+ release cut [<label>] [--at <sha>] [--yes] [--build <command>] [--param <k=v>]
317
364
  [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>]
318
365
  [--actor <name>] [--sign [<key file>]] [--json]
319
366
  A dry run of a cut of this member from HEAD, which
@@ -332,19 +379,29 @@ Releases:
332
379
  requires it. A project with no components releases
333
380
  what chant build --env <env> writes: --src <dir> is
334
381
  the build root, -o <file> the output (dist/build.yaml),
335
- --lexicon narrows it.
382
+ --lexicon narrows it. --at <sha> cuts that commit of
383
+ trunk from a temporary worktree instead of HEAD, so
384
+ the checkout need not be clean. The label reaches
385
+ every compile and build as CHANT_RELEASE_LABEL.
386
+ release candidates [--env <env>] [--json]
387
+ The green commits of trunk a release can be cut from,
388
+ newest first, each with the commits, work items, pull
389
+ requests and flags it would ship, and what each
390
+ environment runs.
336
391
  release publish <label|digest> [--yes] [--allow-unsigned]
337
392
  [--require-provenance] [--json]
338
393
  Publish a cut release to the targets release.publish
339
394
  in chant.config.ts declares (npm, OCI registries,
340
- forge release assets), each with an SBOM and the
341
- release's provenance. A dry run without --yes.
342
- Refuses a revoked release, one whose signature does
343
- not verify against .chant/allowed_signers unless
344
- --allow-unsigned, and with a target a checkout that
345
- is not the release's commit. Warns first when the run
346
- has no OIDC token, so what it publishes carries no
347
- OIDC provenance; --require-provenance refuses instead.
395
+ forge release assets), each with the release's
396
+ provenance and an SBOM when it has a source. A dry
397
+ run without --yes. Refuses a revoked release, and
398
+ with a target a checkout that is not the release's
399
+ commit. Under security verified-releases it refuses
400
+ one whose signature does not verify against
401
+ .chant/allowed_signers unless --allow-unsigned; under
402
+ supply-chain it warns when the run has no OIDC token
403
+ and refuses a target with no SBOM source.
404
+ --require-provenance refuses a run with no OIDC.
348
405
  release verify <label|digest> [--json]
349
406
  Check, offline, that the release was signed by a key
350
407
  .chant/allowed_signers lists at the release's commit.
@@ -875,6 +932,10 @@ Lifecycle (alias: lc):
875
932
  estate size rather than stack count (#2405)
876
933
  lifecycle affected Stacks a change affects (--base <ref> [--include-dependents])
877
934
  --json: emit the ChangeSet as JSON
935
+ lifecycle health <env> Whether each declared service is up, separate from
936
+ drift: ECS running vs desired, Route 53 health
937
+ checks, Container Apps revisions, Cloud Run Ready.
938
+ A failed read is "not read", never unhealthy (--json)
878
939
  lifecycle whoami <env> Who chant would act as in each configured lexicon,
879
940
  and what that principal is scoped to — read-only,
880
941
  before anything acts (--json, --strict)
@@ -919,7 +980,12 @@ Component release ledger + status:
919
980
  report and note (--base <ref> --pr <n>
920
981
  [--forge github|gitlab|forgejo] posts them);
921
982
  applies nothing
922
- components pr-apply On merge: plan the same members again and apply
983
+ components pr-record On merge (#4168): plan the same members again,
984
+ store the change set and write the merge record
985
+ _merges/<commit>.json; applies and refuses
986
+ nothing, and a plan that differs from the
987
+ approved one sets a failing chant/record status
988
+ components pr-apply With --apply-on-merge: plan the same members again and apply
923
989
  them only if an approval stands for that digest
924
990
  (chant approve pr-<n> pr-apply --plan <digest>);
925
991
  a moved plan refuses with exit 3
@@ -929,6 +995,10 @@ Component release ledger + status:
929
995
  member's loop, gate op pr-<n>-<member>;
930
996
  --resume <file> finishes an apply that failed
931
997
  partway under the same approval;
998
+ --wave-gate <name>: apply a wave at a time, each
999
+ planned after the one before applied, a moved
1000
+ wave waiting at <name>-wave-<k>, with --wave <k>
1001
+ for one wave per CI job;
932
1002
  --events-json, both commands: the run-event
933
1003
  stream on stdout; --report-url <s3 or file URL>,
934
1004
  both commands: copy the report, the change set
@@ -998,7 +1068,8 @@ Servers:
998
1068
  serve mcp Start the MCP server (stdio)
999
1069
 
1000
1070
  Project:
1001
- update Sync lexicon types into .chant/types/
1071
+ update (alias: sync) Sync lexicon types into .chant/types/ (does not install
1072
+ packages: run npm install first)
1002
1073
  doctor Check project health and configuration
1003
1074
 
1004
1075
  Options:
@@ -1217,6 +1288,9 @@ async function loadPluginsOrExit(path: string, lexicon?: string): Promise<import
1217
1288
  plugins = await loadPlugins(lexiconNames);
1218
1289
  } catch (error) {
1219
1290
  const errorMessage = error instanceof Error ? error.message : String(error);
1291
+ // #4116: a lexicon package the project has not installed is named as such first.
1292
+ const { reportUninstalledDependencies } = await import("./dependencies");
1293
+ reportUninstalledDependencies([errorMessage], resolve(path));
1220
1294
  console.error(formatError({ message: errorMessage }));
1221
1295
  process.exit(1);
1222
1296
  }
@@ -1366,6 +1440,8 @@ export const commandRegistry: CommandDef[] = [
1366
1440
  // #4094: one pull-request job as a pipeline Op in a fresh checkout, the chant runner's first slice.
1367
1441
  { name: "ci run", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-run")).runCiRun(ctx) },
1368
1442
  // #4098: the run index at the report URL.
1443
+ // #4147: a provisioner's signed word that it started a runner for one job, signed and checked.
1444
+ { name: "ci runner-start", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-runner-start")).runCiRunnerStart(ctx) },
1369
1445
  { name: "ci runs", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-run-index")).runCiRuns(ctx) },
1370
1446
  { name: "ci serve", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/ci-serve")).runCiServe(ctx) },
1371
1447
  // #4026: the release cut from a green commit (ws-104). Imported on first use.
@@ -1374,6 +1450,7 @@ export const commandRegistry: CommandDef[] = [
1374
1450
  { name: "release publish", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleasePublish(ctx) },
1375
1451
  // #4049: check a release's signature offline.
1376
1452
  { name: "release verify", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseVerify(ctx) },
1453
+ { name: "release candidates", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCandidates(ctx) },
1377
1454
  { name: "release list", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseList(ctx) },
1378
1455
  { name: "release export", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseExport(ctx) },
1379
1456
  { name: "release compile", runsNoConfig: true, handler: async (ctx) => (await import("./handlers/release")).runReleaseCompile(ctx) },
@@ -1484,6 +1561,8 @@ export const commandRegistry: CommandDef[] = [
1484
1561
  { name: "lifecycle plan", requiresPlugins: true, handler: runLifecyclePlan },
1485
1562
  { name: "lifecycle affected", requiresPlugins: true, handler: runLifecycleAffected },
1486
1563
  { name: "lifecycle whoami", requiresPlugins: true, handler: runLifecycleWhoami },
1564
+ // #4177 — runtime health, separate from drift. Imported on first use.
1565
+ { name: "lifecycle health", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/lifecycle-health")).runLifecycleHealth(ctx) },
1487
1566
  { name: "lifecycle teardown", requiresPlugins: true, handler: runLifecycleTeardown },
1488
1567
  { name: "lifecycle log", handler: runLifecycleLog },
1489
1568
  // #4036 — retention's garbage collection. Imported on first use.
@@ -1499,6 +1578,7 @@ export const commandRegistry: CommandDef[] = [
1499
1578
  // #3183 — the pull-request loop the generated CI runs. Imported on first use.
1500
1579
  { name: "components pr-plan", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrPlan(ctx) },
1501
1580
  { name: "components pr-apply", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrApply(ctx) },
1581
+ { name: "components pr-record", requiresPlugins: true, handler: async (ctx) => (await import("./handlers/pr")).runComponentsPrRecord(ctx) },
1502
1582
  { name: "components status", requiresPlugins: true, handler: runComponentsStatus },
1503
1583
  { name: "components release", handler: runComponentsReleaseRecord },
1504
1584
  { name: "components export", handler: runComponentsExport },
@@ -1725,6 +1805,13 @@ const isMain = isEntryPoint(process.argv[1], import.meta.url);
1725
1805
  if (isMain) {
1726
1806
  main().catch(async (err) => {
1727
1807
  const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
1808
+ // #4116: a failure to import a package the project has not installed is named as such first.
1809
+ try {
1810
+ const { reportUninstalledDependencies } = await import("./dependencies");
1811
+ reportUninstalledDependencies([err instanceof Error ? err.message : String(err)], process.cwd());
1812
+ } catch {
1813
+ // The original error below is what matters.
1814
+ }
1728
1815
  if (verbose && err instanceof Error && err.stack) {
1729
1816
  console.error(err.stack);
1730
1817
  } else {