@intentius/chant 0.124.32 → 0.124.34

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 (320) hide show
  1. package/dist/ci-jobs.d.ts +4 -0
  2. package/dist/ci-jobs.d.ts.map +1 -1
  3. package/dist/ci-jobs.js +9 -2
  4. package/dist/cli/handlers/build.d.ts.map +1 -1
  5. package/dist/cli/handlers/build.js +18 -3
  6. package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
  7. package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
  8. package/dist/cli/handlers/lifecycle-health.js +150 -0
  9. package/dist/cli/handlers/lifecycle.d.ts +26 -0
  10. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  11. package/dist/cli/handlers/lifecycle.js +1 -1
  12. package/dist/cli/handlers/pr.d.ts +2 -0
  13. package/dist/cli/handlers/pr.d.ts.map +1 -1
  14. package/dist/cli/handlers/pr.js +124 -2
  15. package/dist/cli/handlers/promote.d.ts.map +1 -1
  16. package/dist/cli/handlers/promote.js +277 -74
  17. package/dist/cli/handlers/release.d.ts +16 -1
  18. package/dist/cli/handlers/release.d.ts.map +1 -1
  19. package/dist/cli/handlers/release.js +83 -11
  20. package/dist/cli/main.d.ts.map +1 -1
  21. package/dist/cli/main.js +99 -22
  22. package/dist/cli/parse-args.d.ts.map +1 -1
  23. package/dist/cli/parse-args.js +30 -1
  24. package/dist/cli/registry.d.ts +21 -5
  25. package/dist/cli/registry.d.ts.map +1 -1
  26. package/dist/components/deploy-inputs.d.ts +91 -0
  27. package/dist/components/deploy-inputs.d.ts.map +1 -0
  28. package/dist/components/deploy-inputs.js +184 -0
  29. package/dist/components/driver.d.ts.map +1 -1
  30. package/dist/components/driver.js +9 -0
  31. package/dist/components/gate-plan.d.ts +8 -0
  32. package/dist/components/gate-plan.d.ts.map +1 -1
  33. package/dist/components/gate-plan.js +6 -0
  34. package/dist/components/pr-pipeline.d.ts +33 -10
  35. package/dist/components/pr-pipeline.d.ts.map +1 -1
  36. package/dist/components/pr-pipeline.js +42 -11
  37. package/dist/components/pr-run.d.ts +1 -1
  38. package/dist/components/pr-run.d.ts.map +1 -1
  39. package/dist/components/promote-rounds.d.ts +102 -0
  40. package/dist/components/promote-rounds.d.ts.map +1 -0
  41. package/dist/components/promote-rounds.js +174 -0
  42. package/dist/components/release-compile.d.ts +37 -2
  43. package/dist/components/release-compile.d.ts.map +1 -1
  44. package/dist/components/release-compile.js +64 -8
  45. package/dist/components/release-gate.d.ts +30 -0
  46. package/dist/components/release-gate.d.ts.map +1 -1
  47. package/dist/components/release-gate.js +92 -42
  48. package/dist/components/release-promote.d.ts +17 -3
  49. package/dist/components/release-promote.d.ts.map +1 -1
  50. package/dist/components/release-promote.js +79 -18
  51. package/dist/components/verbs/bom-writer.d.ts +1 -1
  52. package/dist/components/verbs/bom-writer.d.ts.map +1 -1
  53. package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
  54. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
  55. package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
  56. package/dist/components/wave-plan.d.ts +10 -0
  57. package/dist/components/wave-plan.d.ts.map +1 -1
  58. package/dist/components/wave-plan.js +32 -4
  59. package/dist/graph-declared.d.ts +4 -0
  60. package/dist/graph-declared.d.ts.map +1 -1
  61. package/dist/graph-declared.js +11 -1
  62. package/dist/graph-detail.d.ts.map +1 -1
  63. package/dist/graph-detail.js +32 -7
  64. package/dist/health.d.ts +110 -0
  65. package/dist/health.d.ts.map +1 -0
  66. package/dist/health.js +105 -0
  67. package/dist/health.schema.json +191 -0
  68. package/dist/lexicon.d.ts +27 -0
  69. package/dist/lexicon.d.ts.map +1 -1
  70. package/dist/lifecycle/merge-record.d.ts +104 -0
  71. package/dist/lifecycle/merge-record.d.ts.map +1 -0
  72. package/dist/lifecycle/merge-record.js +102 -0
  73. package/dist/lifecycle/publish-attachments.d.ts +8 -5
  74. package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
  75. package/dist/lifecycle/publish-attachments.js +27 -13
  76. package/dist/lifecycle/publish-npm.d.ts +3 -2
  77. package/dist/lifecycle/publish-npm.d.ts.map +1 -1
  78. package/dist/lifecycle/publish-npm.js +4 -3
  79. package/dist/lifecycle/release-at.d.ts +40 -0
  80. package/dist/lifecycle/release-at.d.ts.map +1 -0
  81. package/dist/lifecycle/release-at.js +83 -0
  82. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  83. package/dist/lifecycle/release-cut.js +23 -12
  84. package/dist/lifecycle/release-publish.d.ts +28 -12
  85. package/dist/lifecycle/release-publish.d.ts.map +1 -1
  86. package/dist/lifecycle/release-publish.js +29 -17
  87. package/dist/lifecycle/release-store.d.ts +25 -2
  88. package/dist/lifecycle/release-store.d.ts.map +1 -1
  89. package/dist/lifecycle/release-store.js +40 -13
  90. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  91. package/dist/lifecycle/run-ledger.js +1 -0
  92. package/dist/lint/rules/op/index.d.ts +2 -1
  93. package/dist/lint/rules/op/index.d.ts.map +1 -1
  94. package/dist/lint/rules/op/index.js +4 -2
  95. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
  96. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
  97. package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
  98. package/dist/op/activities/activity-contracts.d.ts +5 -0
  99. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  100. package/dist/op/activities/activity-contracts.js +3 -0
  101. package/dist/op/activities/shell.d.ts +38 -0
  102. package/dist/op/activities/shell.d.ts.map +1 -1
  103. package/dist/op/activities/shell.js +89 -3
  104. package/dist/op/builders.d.ts +7 -0
  105. package/dist/op/builders.d.ts.map +1 -1
  106. package/dist/op/builders.js +7 -0
  107. package/dist/op/runtime.d.ts +2 -0
  108. package/dist/op/runtime.d.ts.map +1 -1
  109. package/dist/pr-loop.d.ts +14 -3
  110. package/dist/pr-loop.d.ts.map +1 -1
  111. package/dist/pr-loop.js +15 -5
  112. package/dist/pr-report-url.d.ts +2 -2
  113. package/dist/pr-report-url.d.ts.map +1 -1
  114. package/dist/runner-start.d.ts +179 -0
  115. package/dist/runner-start.d.ts.map +1 -0
  116. package/dist/runner-start.js +277 -0
  117. package/dist/wave-runtime.mjs +236 -229
  118. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  119. package/dist/workspace/__fixtures__/contract-repo.js +13 -2
  120. package/dist/workspace/check.schema.json +88 -2
  121. package/dist/workspace/checks/ci-workflow.d.ts +17 -0
  122. package/dist/workspace/checks/ci-workflow.d.ts.map +1 -0
  123. package/dist/workspace/checks/ci-workflow.js +123 -0
  124. package/dist/workspace/checks/security.d.ts +50 -0
  125. package/dist/workspace/checks/security.d.ts.map +1 -0
  126. package/dist/workspace/checks/security.js +305 -0
  127. package/dist/workspace/checks.d.ts +2 -0
  128. package/dist/workspace/checks.d.ts.map +1 -1
  129. package/dist/workspace/checks.js +8 -0
  130. package/dist/workspace/ci-builds.d.ts +163 -0
  131. package/dist/workspace/ci-builds.d.ts.map +1 -0
  132. package/dist/workspace/ci-builds.js +315 -0
  133. package/dist/workspace/ci-green-cli.d.ts +2 -1
  134. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  135. package/dist/workspace/ci-green-cli.js +25 -5
  136. package/dist/workspace/ci-green-forge.d.ts +12 -0
  137. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  138. package/dist/workspace/ci-green-forge.js +12 -0
  139. package/dist/workspace/ci-green-workflow.d.ts +55 -1
  140. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  141. package/dist/workspace/ci-green-workflow.js +128 -1
  142. package/dist/workspace/ci-green.d.ts +89 -4
  143. package/dist/workspace/ci-green.d.ts.map +1 -1
  144. package/dist/workspace/ci-green.js +310 -23
  145. package/dist/workspace/ci-init.d.ts +100 -0
  146. package/dist/workspace/ci-init.d.ts.map +1 -0
  147. package/dist/workspace/ci-init.js +434 -0
  148. package/dist/workspace/ci-last-green.schema.json +49 -0
  149. package/dist/workspace/ci-list.schema.json +35 -1
  150. package/dist/workspace/ci-run-index.d.ts +1 -1
  151. package/dist/workspace/ci-run-index.d.ts.map +1 -1
  152. package/dist/workspace/ci-run-index.schema.json +1 -1
  153. package/dist/workspace/ci-run.d.ts +12 -6
  154. package/dist/workspace/ci-run.d.ts.map +1 -1
  155. package/dist/workspace/ci-run.js +72 -16
  156. package/dist/workspace/ci-runner-start.d.ts +77 -0
  157. package/dist/workspace/ci-runner-start.d.ts.map +1 -0
  158. package/dist/workspace/ci-runner-start.js +185 -0
  159. package/dist/workspace/ci-serve.d.ts +76 -7
  160. package/dist/workspace/ci-serve.d.ts.map +1 -1
  161. package/dist/workspace/ci-serve.js +153 -19
  162. package/dist/workspace/ci-workflow.d.ts +91 -0
  163. package/dist/workspace/ci-workflow.d.ts.map +1 -0
  164. package/dist/workspace/ci-workflow.js +33 -0
  165. package/dist/workspace/declaration.d.ts +35 -0
  166. package/dist/workspace/declaration.d.ts.map +1 -1
  167. package/dist/workspace/declaration.js +12 -2
  168. package/dist/workspace/declaration.schema.json +51 -0
  169. package/dist/workspace/init.js +2 -2
  170. package/dist/workspace/ls.d.ts +2 -0
  171. package/dist/workspace/ls.d.ts.map +1 -1
  172. package/dist/workspace/ls.js +1 -1
  173. package/dist/workspace/ls.schema.json +2 -1
  174. package/dist/workspace/member-pipeline.d.ts +1 -0
  175. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  176. package/dist/workspace/member-pipeline.js +2 -0
  177. package/dist/workspace/merge-record.schema.json +49 -0
  178. package/dist/workspace/pr-report.schema.json +3 -3
  179. package/dist/workspace/reason-codes.d.ts +26 -0
  180. package/dist/workspace/reason-codes.d.ts.map +1 -1
  181. package/dist/workspace/reason-codes.js +28 -0
  182. package/dist/workspace/release-candidates.d.ts +129 -0
  183. package/dist/workspace/release-candidates.d.ts.map +1 -0
  184. package/dist/workspace/release-candidates.js +233 -0
  185. package/dist/workspace/release-candidates.schema.json +353 -0
  186. package/dist/workspace/release-workflow.d.ts +31 -0
  187. package/dist/workspace/release-workflow.d.ts.map +1 -0
  188. package/dist/workspace/release-workflow.js +150 -0
  189. package/dist/workspace/security.d.ts +40 -0
  190. package/dist/workspace/security.d.ts.map +1 -0
  191. package/dist/workspace/security.js +99 -0
  192. package/package.json +8 -1
  193. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
  194. package/src/ci-jobs.ts +11 -2
  195. package/src/cli/handlers/build.test.ts +36 -0
  196. package/src/cli/handlers/build.ts +18 -3
  197. package/src/cli/handlers/lifecycle-health.test.ts +123 -0
  198. package/src/cli/handlers/lifecycle-health.ts +157 -0
  199. package/src/cli/handlers/lifecycle.ts +1 -1
  200. package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
  201. package/src/cli/handlers/pr-record.test.ts +201 -0
  202. package/src/cli/handlers/pr.test.ts +22 -0
  203. package/src/cli/handlers/pr.ts +127 -4
  204. package/src/cli/handlers/promote.test.ts +17 -0
  205. package/src/cli/handlers/promote.ts +296 -84
  206. package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
  207. package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
  208. package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
  209. package/src/cli/handlers/release.e2e.test.ts +19 -28
  210. package/src/cli/handlers/release.ts +81 -11
  211. package/src/cli/main.ts +99 -22
  212. package/src/cli/parse-args.ts +22 -1
  213. package/src/cli/registry.ts +21 -5
  214. package/src/components/deploy-inputs.test.ts +108 -0
  215. package/src/components/deploy-inputs.ts +225 -0
  216. package/src/components/driver.ts +11 -0
  217. package/src/components/gate-plan.ts +9 -0
  218. package/src/components/pr-pipeline.ts +56 -15
  219. package/src/components/pr-run.ts +1 -1
  220. package/src/components/promote-rounds.test.ts +220 -0
  221. package/src/components/promote-rounds.ts +232 -0
  222. package/src/components/release-compile.test.ts +34 -0
  223. package/src/components/release-compile.ts +89 -8
  224. package/src/components/release-gate.test.ts +47 -0
  225. package/src/components/release-gate.ts +111 -46
  226. package/src/components/release-promote.test.ts +55 -2
  227. package/src/components/release-promote.ts +93 -23
  228. package/src/components/verbs/bom-writer.ts +1 -1
  229. package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
  230. package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
  231. package/src/components/wave-plan.ts +32 -4
  232. package/src/graph-declared-exports.test.ts +40 -0
  233. package/src/graph-declared.ts +9 -1
  234. package/src/graph-detail.test.ts +57 -0
  235. package/src/graph-detail.ts +33 -7
  236. package/src/health.schema.json +191 -0
  237. package/src/health.test.ts +62 -0
  238. package/src/health.ts +178 -0
  239. package/src/lexicon.ts +41 -1
  240. package/src/lifecycle/merge-record.test.ts +111 -0
  241. package/src/lifecycle/merge-record.ts +154 -0
  242. package/src/lifecycle/publish-attachments.ts +25 -13
  243. package/src/lifecycle/publish-npm.ts +4 -3
  244. package/src/lifecycle/publish-targets.test.ts +34 -4
  245. package/src/lifecycle/release-at.test.ts +48 -0
  246. package/src/lifecycle/release-at.ts +100 -0
  247. package/src/lifecycle/release-cut.test.ts +41 -0
  248. package/src/lifecycle/release-cut.ts +22 -11
  249. package/src/lifecycle/release-publish.test.ts +43 -13
  250. package/src/lifecycle/release-publish.ts +38 -18
  251. package/src/lifecycle/release-store.ts +57 -13
  252. package/src/lifecycle/run-ledger.test.ts +13 -0
  253. package/src/lifecycle/run-ledger.ts +1 -0
  254. package/src/lint/rules/op/index.ts +4 -2
  255. package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
  256. package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
  257. package/src/op/activities/activity-contracts.ts +3 -0
  258. package/src/op/activities/shell-container.e2e.test.ts +67 -0
  259. package/src/op/activities/shell.test.ts +44 -1
  260. package/src/op/activities/shell.ts +110 -3
  261. package/src/op/builders.ts +7 -0
  262. package/src/op/runtime.ts +2 -0
  263. package/src/op/step-console.test.ts +4 -2
  264. package/src/pr-loop.test.ts +29 -0
  265. package/src/pr-loop.ts +26 -7
  266. package/src/pr-report-url.ts +2 -2
  267. package/src/runner-start-bundle.test.ts +29 -0
  268. package/src/runner-start.test.ts +179 -0
  269. package/src/runner-start.ts +329 -0
  270. package/src/workspace/__fixtures__/contract-repo.ts +11 -2
  271. package/src/workspace/check-contract.test.ts +4 -2
  272. package/src/workspace/check.schema.json +88 -2
  273. package/src/workspace/checks/ci-workflow.test.ts +150 -0
  274. package/src/workspace/checks/ci-workflow.ts +129 -0
  275. package/src/workspace/checks/security.test.ts +138 -0
  276. package/src/workspace/checks/security.ts +305 -0
  277. package/src/workspace/checks.ts +8 -0
  278. package/src/workspace/ci-builds.e2e.test.ts +208 -0
  279. package/src/workspace/ci-builds.test.ts +174 -0
  280. package/src/workspace/ci-builds.ts +385 -0
  281. package/src/workspace/ci-green-builds.test.ts +180 -0
  282. package/src/workspace/ci-green-cli.test.ts +54 -0
  283. package/src/workspace/ci-green-cli.ts +26 -5
  284. package/src/workspace/ci-green-forge.ts +24 -0
  285. package/src/workspace/ci-green-workflow.test.ts +84 -1
  286. package/src/workspace/ci-green-workflow.ts +139 -2
  287. package/src/workspace/ci-green.test.ts +5 -2
  288. package/src/workspace/ci-green.ts +359 -27
  289. package/src/workspace/ci-init.test.ts +159 -0
  290. package/src/workspace/ci-init.ts +445 -0
  291. package/src/workspace/ci-last-green.schema.json +49 -0
  292. package/src/workspace/ci-list.schema.json +35 -1
  293. package/src/workspace/ci-report-url.test.ts +6 -1
  294. package/src/workspace/ci-run-index.schema.json +1 -1
  295. package/src/workspace/ci-run-index.ts +1 -1
  296. package/src/workspace/ci-run.test.ts +24 -7
  297. package/src/workspace/ci-run.ts +78 -16
  298. package/src/workspace/ci-runner-start.test.ts +151 -0
  299. package/src/workspace/ci-runner-start.ts +193 -0
  300. package/src/workspace/ci-serve-builds.test.ts +86 -0
  301. package/src/workspace/ci-serve.test.ts +59 -0
  302. package/src/workspace/ci-serve.ts +177 -22
  303. package/src/workspace/ci-workflow.ts +71 -0
  304. package/src/workspace/declaration.schema.json +51 -0
  305. package/src/workspace/declaration.test.ts +17 -0
  306. package/src/workspace/declaration.ts +53 -3
  307. package/src/workspace/init.ts +2 -2
  308. package/src/workspace/ls.schema.json +2 -1
  309. package/src/workspace/ls.ts +3 -2
  310. package/src/workspace/member-pipeline.ts +2 -1
  311. package/src/workspace/merge-record.schema.json +49 -0
  312. package/src/workspace/pr-report.schema.json +3 -3
  313. package/src/workspace/reason-codes.test.ts +4 -0
  314. package/src/workspace/reason-codes.ts +28 -0
  315. package/src/workspace/release-candidates.schema.json +353 -0
  316. package/src/workspace/release-candidates.test.ts +106 -0
  317. package/src/workspace/release-candidates.ts +315 -0
  318. package/src/workspace/release-workflow.ts +167 -0
  319. package/src/workspace/security.test.ts +70 -0
  320. package/src/workspace/security.ts +118 -0
@@ -23,6 +23,7 @@ import { expandGlob } from "./glob";
23
23
  import { EXAMPLES_KIND, holdsChantProject } from "./kinds";
24
24
  import { parseJsonText, pointerToken, type TextLocation } from "./jsonc";
25
25
  import type { ReasonCode } from "./reason-codes";
26
+ import type { SecurityGroup } from "./security";
26
27
  import { joinPath, type WorkspaceTree } from "./tree";
27
28
  import { loadAjv2020 } from "./ajv2020";
28
29
  import { readChantPackageVersion } from "../package-version";
@@ -184,6 +185,13 @@ export interface AgentDeclaration {
184
185
  /** What a check run that finished as skipped counts as in a CI phase (#3573). */
185
186
  export type CiSkipped = "pass" | "fail";
186
187
 
188
+ /**
189
+ * Which commits a phase runs on (#4169): `every` first-parent commit of the
190
+ * branch, or only the `newest` one when a runner frees, the commits between
191
+ * left unbuilt and a red one bisected.
192
+ */
193
+ export type CiBuild = "every" | "newest";
194
+
187
195
  /** One phase of `ci.green`: the check runs that make it up (#3573, ws-103). */
188
196
  export interface CiPhase {
189
197
  name: string;
@@ -191,6 +199,12 @@ export interface CiPhase {
191
199
  runs: string[];
192
200
  /** `fail` (the default): a skipped run never passes. `pass`: it passes, for a job a path filter runs only on some commits. */
193
201
  skipped: CiSkipped;
202
+ /**
203
+ * Which commits the phase runs on (#4169), when the declaration says.
204
+ * Absent means `every`, and `ci workflow` then leaves the phase's
205
+ * workflows as they are.
206
+ */
207
+ build?: CiBuild;
194
208
  pointer: string;
195
209
  }
196
210
 
@@ -207,6 +221,12 @@ export interface CiGreen {
207
221
  phases: CiPhase[];
208
222
  /** The phases a commit must pass, by name, in file order. */
209
223
  require: string[];
224
+ /**
225
+ * The phases that must pass before the branch moves (#4168 4.4), by name,
226
+ * in file order: the quick checks a pull request, a merge queue or a local
227
+ * run gives before a change lands. Absent or empty when the declaration names none.
228
+ */
229
+ preMerge?: string[];
210
230
  /**
211
231
  * The full sha of the first commit on the branch whose ci tags need a
212
232
  * statement signed by a runner key (#4031). A tag on a strict
@@ -230,9 +250,20 @@ export interface CiGreen {
230
250
  /** Where `ci tick` reads phase results (#4044): forge check runs, or chant Op runs. */
231
251
  export type CiSource = "checks" | "runs";
232
252
 
253
+ /**
254
+ * A prescribed delivery workflow (#4168 section 7). `trunk-curated`: a merge
255
+ * records the plan and deploys nothing, and releases are cut from green
256
+ * commits and promoted through the environments. `trunk-curated+apply-on-merge`
257
+ * adds the apply on merge as an opt-in. `chant workspace check` reports what a
258
+ * member under one still lacks (./checks/ci-workflow.ts, WSP151).
259
+ */
260
+ export type CiWorkflow = "trunk-curated" | "trunk-curated+apply-on-merge";
261
+
233
262
  /** The declaration's `ci` block (#3573). */
234
263
  export interface CiDeclaration {
235
264
  green: CiGreen | null;
265
+ /** `ci.workflow` (#4168 step 6): the workflow every member follows unless it names its own; absent when the declaration sets none. */
266
+ workflow?: CiWorkflow;
236
267
  /**
237
268
  * `ci.reportUrl` (#4160): where the pull-request jobs copy their reports
238
269
  * and `chant ci run` keeps the run index, `<reportUrl>/runs/index.json`.
@@ -557,6 +588,10 @@ export interface Member {
557
588
  because: string | null;
558
589
  /** Whether `chant workspace export` takes the member (#2552, D10). False when the entry does not say. */
559
590
  travel: boolean;
591
+ /** The security groups this member adds to the declaration's top-level `security` (#4168 step 7), in file order; empty when it names none. */
592
+ security: SecurityGroup[];
593
+ /** The member's own `ci.workflow` (#4168 step 6), which wins over the top level's; null when it names none. */
594
+ ciWorkflow: CiWorkflow | null;
560
595
  suppress: Suppression[];
561
596
  /** The entry's JSON Pointer in the file, for messages. */
562
597
  pointer: string;
@@ -624,6 +659,8 @@ export interface Declaration {
624
659
  * then uses 7.
625
660
  */
626
661
  metrics: { failureWindowDays?: number } | null;
662
+ /** The security groups the workspace opts into (#4168 section 8, ./security.ts), in file order; empty when the declaration has no `security` key. */
663
+ security: SecurityGroup[];
627
664
  /** The file, relative to the workspace root's tree (`chant.workspace.json` or `.jsonc`). */
628
665
  file: string;
629
666
  }
@@ -850,6 +887,8 @@ export function parseDeclaration(text: string, file: string, reader: string = re
850
887
  upstream: (e.upstream as string | undefined) ?? null,
851
888
  because: (e.because as string | undefined) ?? null,
852
889
  travel: e.travel === true,
890
+ security: [...((e.security as SecurityGroup[] | undefined) ?? [])],
891
+ ciWorkflow: ((e.ci as { workflow?: CiWorkflow } | undefined)?.workflow) ?? null,
853
892
  suppress,
854
893
  pointer,
855
894
  };
@@ -1002,6 +1041,7 @@ export function parseDeclaration(text: string, file: string, reader: string = re
1002
1041
  ci,
1003
1042
  retention: retentionOf(obj.retention),
1004
1043
  metrics: metricsOf(obj.metrics),
1044
+ security: [...((obj.security as SecurityGroup[] | undefined) ?? [])],
1005
1045
  file,
1006
1046
  };
1007
1047
  written.set(declaration, raw);
@@ -1096,7 +1136,8 @@ function retentionOf(raw: unknown): RetentionOverride | null {
1096
1136
 
1097
1137
  function ciOf(raw: unknown, at: (pointer: string, key?: boolean) => ErrorLocation): CiDeclaration | null {
1098
1138
  if (raw === undefined) return null;
1099
- const block = raw as { reportUrl?: string; green?: { branch: string; window?: string; phases: Record<string, string[] | { runs: string[]; skipped?: CiSkipped }>; require: string[]; signedSince?: string; source?: CiSource } };
1139
+ const block = raw as { reportUrl?: string; workflow?: CiWorkflow; green?: { branch: string; window?: string; phases: Record<string, string[] | { runs: string[]; skipped?: CiSkipped; build?: CiBuild }>; require: string[]; preMerge?: string[]; signedSince?: string; source?: CiSource } };
1140
+ const workflow = block.workflow !== undefined ? { workflow: block.workflow } : {};
1100
1141
  let reportUrl: string | null = null;
1101
1142
  if (block.reportUrl !== undefined) {
1102
1143
  // #4160: refused here as pr-plan and ci run refuse the flag, so a reader never gets a URL no job can write to.
@@ -1104,12 +1145,13 @@ function ciOf(raw: unknown, at: (pointer: string, key?: boolean) => ErrorLocatio
1104
1145
  if ("error" in target) throw new WorkspaceReadError("declaration-invalid", target.error, at("/ci/reportUrl"));
1105
1146
  reportUrl = target.url;
1106
1147
  }
1107
- if (block.green === undefined) return { green: null, reportUrl };
1148
+ if (block.green === undefined) return { green: null, ...workflow, reportUrl };
1108
1149
  const g = block.green;
1109
1150
  const phases: CiPhase[] = Object.entries(g.phases).map(([name, p]) => ({
1110
1151
  name,
1111
1152
  runs: [...(Array.isArray(p) ? p : p.runs)],
1112
1153
  skipped: Array.isArray(p) ? "fail" : (p.skipped ?? "fail"),
1154
+ ...(!Array.isArray(p) && p.build !== undefined ? { build: p.build } : {}),
1113
1155
  pointer: `/ci/green/phases/${pointerToken(name)}`,
1114
1156
  }));
1115
1157
  for (const [i, name] of g.require.entries()) {
@@ -1120,7 +1162,15 @@ function ciOf(raw: unknown, at: (pointer: string, key?: boolean) => ErrorLocatio
1120
1162
  at(`/ci/green/require/${i}`),
1121
1163
  );
1122
1164
  }
1123
- return { green: { branch: g.branch, window: g.window ?? DEFAULT_CI_WINDOW, phases, require: [...g.require], signedSince: g.signedSince ?? null, source: g.source ?? "checks", pointer: "/ci/green" }, reportUrl };
1165
+ for (const [i, name] of (g.preMerge ?? []).entries()) {
1166
+ if (phases.some((p) => p.name === name)) continue;
1167
+ throw new WorkspaceReadError(
1168
+ "declaration-invalid",
1169
+ `ci.green.preMerge names the phase ${JSON.stringify(name)}, which ci.green.phases does not declare; declared phases: ${phases.map((p) => p.name).join(", ")}`,
1170
+ at(`/ci/green/preMerge/${i}`),
1171
+ );
1172
+ }
1173
+ return { green: { branch: g.branch, window: g.window ?? DEFAULT_CI_WINDOW, phases, require: [...g.require], ...(g.preMerge !== undefined ? { preMerge: [...g.preMerge] } : {}), signedSince: g.signedSince ?? null, source: g.source ?? "checks", pointer: "/ci/green" }, ...workflow, reportUrl };
1124
1174
  }
1125
1175
 
1126
1176
  function agentsOf(raw: unknown, members: Member[], at: (pointer: string, key?: boolean) => ErrorLocation): AgentDeclaration[] {
@@ -559,7 +559,7 @@ export async function runWorkspaceInit(ctx: CommandContext): Promise<number> {
559
559
  console.error(
560
560
  formatError({
561
561
  message: `${join(root, existing[0])} already exists`,
562
- hint: "Edit it by hand, or run chant workspace ls to see what it declares.",
562
+ hint: "Edit it by hand, or run chant workspace ls to see what it declares. To set it up for a delivery workflow, run chant ci init --workflow trunk-curated.",
563
563
  }),
564
564
  );
565
565
  return 1;
@@ -612,7 +612,7 @@ export async function runWorkspaceInit(ctx: CommandContext): Promise<number> {
612
612
  return 0;
613
613
  }
614
614
  writeFileSync(target, text, { flag: "wx" });
615
- console.log(formatSuccess(`Wrote ${target}. chant workspace ls lists what it declares.`));
615
+ console.log(formatSuccess(`Wrote ${target}. chant workspace ls lists what it declares, and chant ci init --workflow trunk-curated sets up merging and releases.`));
616
616
  return 0;
617
617
  }
618
618
 
@@ -21,7 +21,8 @@
21
21
  "properties": {
22
22
  "name": { "type": "string" },
23
23
  "runs": { "type": "array", "items": { "type": "string" }, "description": "Check-run name patterns, * standing for any run of characters." },
24
- "skipped": { "enum": ["pass", "fail"], "description": "What a run that finished as skipped counts as." }
24
+ "skipped": { "enum": ["pass", "fail"], "description": "What a run that finished as skipped counts as." },
25
+ "build": { "enum": ["every", "newest"], "description": "Added in contract 1 by #4169, written by chant 0.124.33 and newer, and only when the declaration sets it: whether the phase runs on every first-parent commit or only the newest one when a runner frees. Absent means every." }
25
26
  }
26
27
  }
27
28
  },
@@ -142,7 +142,8 @@ export interface LsDiagram {
142
142
  export interface LsCiGreen {
143
143
  branch: string;
144
144
  window: string;
145
- phases: { name: string; runs: string[]; skipped: "pass" | "fail" }[];
145
+ /** `build` is present only when the declaration sets it (#4169). */
146
+ phases: { name: string; runs: string[]; skipped: "pass" | "fail"; build?: "every" | "newest" }[];
146
147
  require: string[];
147
148
  /** The first commit whose ci tags need a signed statement (#4031). Present only when the declaration sets it. */
148
149
  signedSince?: string;
@@ -328,7 +329,7 @@ function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declarati
328
329
  ? {
329
330
  branch: declaration.ci.green.branch,
330
331
  window: declaration.ci.green.window,
331
- phases: declaration.ci.green.phases.map((p) => ({ name: p.name, runs: p.runs, skipped: p.skipped })),
332
+ phases: declaration.ci.green.phases.map((p) => ({ name: p.name, runs: p.runs, skipped: p.skipped, ...(p.build ? { build: p.build } : {}) })),
332
333
  require: declaration.ci.green.require,
333
334
  ...(declaration.ci.green.signedSince !== null ? { signedSince: declaration.ci.green.signedSince } : {}),
334
335
  ...(declaration.ci.green.source === "runs" ? { source: "runs" as const } : {}),
@@ -212,7 +212,7 @@ export interface ComponentPlanInput {
212
212
  params?: string[];
213
213
  paramsFile?: string;
214
214
  /** `--pr-loop`, with its `--gate` and `--branch` (#3465). */
215
- prLoop?: { gate?: string; branch?: string };
215
+ prLoop?: { gate?: string; branch?: string; applyOnMerge?: boolean };
216
216
  /** `--wave-gate` with its `--canary` and `--base` (#3049). */
217
217
  waveGate?: { gate: string; canary?: string[]; base?: string };
218
218
  /** `--image` (#4002). */
@@ -258,6 +258,7 @@ export function planMemberComponentPipeline(input: ComponentPlanInput): MemberCo
258
258
  parts.push("--pr-loop");
259
259
  if (input.prLoop.gate) parts.push("--gate", input.prLoop.gate);
260
260
  if (input.prLoop.branch) parts.push("--branch", input.prLoop.branch);
261
+ if (input.prLoop.applyOnMerge) parts.push("--apply-on-merge");
261
262
  }
262
263
  if (input.image) parts.push("--image", input.image);
263
264
  for (const line of input.beforeScript ?? []) parts.push("--before-script", line);
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://intentius.io/chant/schemas/workspace/merge-record/v1/merge-record.schema.json",
4
+ "title": "chant merge record",
5
+ "description": "What landed on the target branch, keyed by commit: _merges/<commit>.json on chant/lifecycle (#4168, chant 0.124.33 and newer). The record job writes it on merge. It applies nothing; a release that carries the commit deploys it.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["$schema", "contract", "commit", "base", "env", "source", "op", "gate", "digest", "plan", "approvedBy", "components", "recordedAt"],
9
+ "properties": {
10
+ "$schema": { "const": "https://intentius.io/chant/schemas/workspace/merge-record/v1/merge-record.schema.json" },
11
+ "contract": { "const": 1 },
12
+ "commit": { "type": "string", "pattern": "^[0-9a-f]{40,64}$", "description": "The merged commit." },
13
+ "base": { "type": "string", "pattern": "^[0-9a-f]{40,64}$", "description": "The commit the change is measured from." },
14
+ "env": { "type": "string", "minLength": 1 },
15
+ "member": { "type": "string", "minLength": 1, "description": "The workspace member whose pipeline wrote it." },
16
+ "source": {
17
+ "description": "Where the change came from: the pull request that merged it, else the work items its Chant-Record: work:<id> trailers name, else a direct push.",
18
+ "oneOf": [
19
+ { "type": "object", "additionalProperties": false, "required": ["kind", "pr"], "properties": { "kind": { "const": "pull-request" }, "pr": { "type": "integer", "minimum": 1 } } },
20
+ {
21
+ "type": "object",
22
+ "additionalProperties": false,
23
+ "required": ["kind", "records"],
24
+ "properties": {
25
+ "kind": { "const": "work-item" },
26
+ "records": {
27
+ "type": "array",
28
+ "minItems": 1,
29
+ "items": { "type": "object", "additionalProperties": false, "required": ["kind", "id"], "properties": { "kind": { "const": "work" }, "id": { "type": "string", "minLength": 1 } } }
30
+ }
31
+ }
32
+ },
33
+ { "type": "object", "additionalProperties": false, "required": ["kind"], "properties": { "kind": { "const": "direct-push" } } }
34
+ ]
35
+ },
36
+ "op": { "type": ["string", "null"], "description": "The pull request's gate op, pr-<n> or pr-<n>-<member>; null without a pull request." },
37
+ "gate": { "type": "string", "minLength": 1 },
38
+ "digest": { "type": ["string", "null"], "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$", "description": "The change-set digest planned on the merged commit; null when nothing was planned. The change set is stored under _change-sets/." },
39
+ "plan": {
40
+ "enum": ["approved", "changed", "unapproved", "nothing", "plan-failed"],
41
+ "description": "approved: an approval stands for digest. changed: one stands for approvedDigest, so the plan moved after review; the record job's chant/record status fails. unapproved: none stands. nothing: no component deploys what the change touches. plan-failed: a member failed to plan."
42
+ },
43
+ "approvedDigest": { "type": "string", "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$" },
44
+ "approvedBy": { "type": "array", "items": { "type": "string" } },
45
+ "authorApproved": { "type": "array", "items": { "type": "string" }, "description": "Approvers who wrote the change (#4000), read with a forge." },
46
+ "components": { "type": "array", "items": { "type": "string" } },
47
+ "recordedAt": { "type": "string", "minLength": 1, "description": "ISO 8601." }
48
+ }
49
+ }
@@ -8,7 +8,7 @@
8
8
  "properties": {
9
9
  "$schema": { "const": "https://intentius.io/chant/schemas/workspace/pr-report/v1/pr-report.schema.json" },
10
10
  "contract": { "const": 1 },
11
- "stage": { "enum": ["plan", "apply"] },
11
+ "stage": { "enum": ["plan", "apply", "record"], "description": "plan: on the pull request. record (#4168, chant 0.124.33 and newer): on merge, the default; it plans the merged commit and writes the merge record, applying nothing. apply: on merge with --apply-on-merge." },
12
12
  "pr": {
13
13
  "type": ["integer", "null"],
14
14
  "minimum": 1,
@@ -33,8 +33,8 @@
33
33
  "description": "The change-set digest of what this stage planned: computePlanDigest(\"change-set\", ...) over every member's { member, planDigest }. What the gate binds."
34
34
  },
35
35
  "status": {
36
- "enum": ["planned", "plan-failed", "nothing", "applied", "failed", "wave-applied", "refused", "waiting"],
37
- "description": "planned: every member planned (plan stage). plan-failed: a member failed to plan, so nothing can apply. nothing: no component deploys what the change touches. applied: every member applied. failed: a member failed to apply and its dependents did not run. wave-applied (#4003, chant 0.124.32 and newer): with --wave-gate and --wave, the wave applied and a later wave is still to run. refused: nothing applied, see refusal. waiting (#3999): the approved plans applied except members held back after an upstream applied, which wait on an approval of the plan the last of rounds made."
36
+ "enum": ["planned", "recorded", "plan-failed", "nothing", "applied", "failed", "wave-applied", "refused", "waiting"],
37
+ "description": "planned: every member planned (plan stage). recorded (#4168, chant 0.124.33 and newer): every member planned on the merged commit and the merge record was written (record stage); approval says whether the plan is the approved one, and nothing applied. plan-failed: a member failed to plan, so nothing can apply. nothing: no component deploys what the change touches. applied: every member applied. failed: a member failed to apply and its dependents did not run. wave-applied (#4003, chant 0.124.32 and newer): with --wave-gate and --wave, the wave applied and a later wave is still to run. refused: nothing applied, see refusal. waiting (#3999): the approved plans applied except members held back after an upstream applied, which wait on an approval of the plan the last of rounds made."
38
38
  },
39
39
  "refusal": {
40
40
  "enum": ["not-approved", "plan-changed", "not-a-reviewer", "author-approved", "no-pull-request", "wave-not-approved", "wave-plan-changed", "earlier-wave"],
@@ -28,6 +28,8 @@ import { STATUS_ERROR_CODES, STATUS_GATE_REASON_CODES, STATUS_REASON_CODES, STAT
28
28
  import { WORK_WARNING_CODES } from "./work";
29
29
  import { BOX_FINDING_CODES } from "./checks/boxes";
30
30
  import { DIAGRAM_FINDING_CODES } from "./checks/diagrams";
31
+ import { SECURITY_FINDING_CODES } from "./checks/security";
32
+ import { CI_WORKFLOW_FINDING_CODES } from "./ci-workflow";
31
33
  import { WORK_ERROR_CODES, WORK_HISTORY_ERROR_CODES, WORK_LEASE_REFUSALS } from "./work-cli";
32
34
  import { RUN_STATEMENT_ERROR_CODES, RUNS_ERROR_CODES, RUNS_REASON_CODES, RUNS_WRITE_ERROR_CODES } from "./runs-cli";
33
35
  import { RUN_STATEMENT_FAILURE_CODES } from "./trust/run-statement";
@@ -66,6 +68,8 @@ const PER_COMMAND: Record<string, readonly string[]> = {
66
68
  CHECK_ERROR_CODES,
67
69
  BOX_FINDING_CODES,
68
70
  DIAGRAM_FINDING_CODES,
71
+ SECURITY_FINDING_CODES,
72
+ CI_WORKFLOW_FINDING_CODES,
69
73
  STATUS_REASON_CODES,
70
74
  STATUS_ERROR_CODES,
71
75
  STATUS_GATE_REASON_CODES,
@@ -229,6 +229,34 @@ export const REASONS = {
229
229
  "diagram-source-missing": "A declared diagram's source file does not exist in the tree read.",
230
230
  "diagram-render-missing": "A declared diagram's render file does not exist in the tree read.",
231
231
  "diagram-render-drift": "A declared diagram records a sourceHash, and the source's bytes now hash to something else: the render is stale for its source.",
232
+ // The security group checklists (check, #4168 step 7). Each is also a WSP finding.
233
+ "security-signers-missing": "The workspace opts into verified-releases, and there is no .chant/allowed_signers to verify a release's signature against.",
234
+ "security-release-gate-missing": "Under verified-releases, identity.gates has no release entry, or it names no class, so a cut is not held to a class of signers.",
235
+ "security-publish-allows-unsigned": "Under verified-releases, a pipeline file runs release publish with --allow-unsigned.",
236
+ "security-sbom-disabled": "Under supply-chain, a member's chant.config.ts sets sbom: false on a publish target.",
237
+ "security-sbom-unsourced": "Under supply-chain, a member publishes, names no sbom file and has no package-lock.json, pom.xml or Cargo.lock to write one from.",
238
+ "security-publish-no-provenance": "Under supply-chain, a pipeline file runs release publish without --require-provenance.",
239
+ "security-component-no-sbom": "Under supply-chain, a member's components have no generate-sbom step.",
240
+ "security-component-no-verify": "Under supply-chain, a member's components have no verify step.",
241
+ "security-signed-since-unset": "Under who-ran-it, ci.green sets no signedSince, or there is no ci.green block.",
242
+ "security-runner-untrusted": "Under who-ran-it, .chant/trust.json lists no runner key.",
243
+ "security-tick-no-key": "Under who-ran-it, a pipeline file runs ci tick with no runner key.",
244
+ "security-review-not-required": "Under review-rules, an apply job passes --no-require-review, which the group overrides.",
245
+ "security-attribution-any": "Under review-rules, identity.attribution is not identified.",
246
+ "security-gate-unsigned": "Under review-rules, an apply job names a gate identity.gates does not list, or identity.gates lists none.",
247
+ "security-runner-start-no-key": "Under isolated-runners, .chant/trust.json lists no key under runners for a provisioner to sign runner-start statements with.",
248
+ "security-runner-start-no-role": "Under isolated-runners, no principal under runners in .chant/trust.json holds the runner role.",
249
+ "security-trust-unreadable": "Under isolated-runners, .chant/trust.json can't be read, so no runner-start statement can be checked.",
250
+ // The ci.workflow requirements (check, #4168 step 6). Each is also a WSP151 finding.
251
+ "ci-workflow-green-undeclared": "Under ci.workflow, the declaration has no ci.green block, so no commit gets a verdict and no release can be cut.",
252
+ "ci-workflow-no-pre-merge": "Under ci.workflow, ci.green.preMerge names no phase, so nothing says which checks run before the branch moves.",
253
+ "ci-workflow-apply-in-green": "Under ci.workflow, a phase in ci.green.require matches an apply job, which would wait on its own verdict.",
254
+ "ci-workflow-no-components": "Under ci.workflow, a member declares no component, so nothing of it can be cut or promoted.",
255
+ "ci-workflow-no-environments": "Under ci.workflow, a member's chant.config.ts declares no environments, so a release has nowhere to be promoted to.",
256
+ "ci-workflow-ungated-env": "Under ci.workflow, a component has no gate, so a promote to the last environment waits on no approval.",
257
+ "ci-workflow-pipeline-missing": "Under ci.workflow, no pipeline file runs the plan job, the merge job or chant ci tick.",
258
+ "ci-workflow-pipeline-differs": "Under ci.workflow, the merge job does not match the workflow: it applies under trunk-curated, or records under trunk-curated+apply-on-merge.",
259
+ "ci-workflow-publish-missing": "Under ci.workflow, no pipeline file runs chant release publish.",
232
260
  // A box that holds a credential (check, #2726). Each is also a WSP finding.
233
261
  "box-credential-declared": "A file in a box member's directory carries a literal secret: a credential's shape, or a literal where a credential goes. A variable or secret-manager reference is not one.",
234
262
  "box-capability-unbrokered": "A capability in a member's box block names no broker, so the box would hold its credential.",