@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,208 @@
1
+ /**
2
+ * #4169 end to end: `chant ci tick` and `chant ci run --at`, as a process,
3
+ * against a bare git remote and a local server that answers GitHub's check
4
+ * run, re-run and workflow dispatch calls the way Actions would for a
5
+ * `suites` phase declared `build: "newest"`.
6
+ *
7
+ * Seven commits go to main at once: a green base, five commits the suites
8
+ * never ran on (GitHub dropped their waiting runs), and the newest, whose
9
+ * suites run fails. The fourth of the five broke the suites. The tick runs
10
+ * each failed job again once, then bisects: it halves the five, steps back
11
+ * from the red, and names the culprit, leaving the two commits below the
12
+ * green middle one unbuilt.
13
+ */
14
+
15
+ import { spawn } from "node:child_process";
16
+ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
17
+ import type { AddressInfo } from "node:net";
18
+ import { join } from "node:path";
19
+ import { pathToFileURL } from "node:url";
20
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
21
+ import { cleanScratch, declaration, git, REPO, scratchDir, writeFiles } from "./__fixtures__/contract-repo";
22
+
23
+ afterAll(cleanScratch);
24
+
25
+ const GREEN = {
26
+ branch: "main",
27
+ phases: { check: { runs: ["check"], build: "every" }, suites: { runs: ["suites"], build: "newest" } },
28
+ require: ["check", "suites"],
29
+ };
30
+ const SUITES_WORKFLOW = "name: release-suites\non:\n push:\n branches: [main]\n workflow_dispatch: {}\njobs:\n suites:\n runs-on: ubuntu-latest\n steps:\n - run: true\n";
31
+
32
+ interface Run {
33
+ id: number;
34
+ name: string;
35
+ status: string;
36
+ conclusion: string | null;
37
+ }
38
+
39
+ /** The fake GitHub: runs per commit, and what dispatching the suites at a commit concludes. */
40
+ class FakeGithub {
41
+ runs = new Map<string, Run[]>();
42
+ dispatched: string[] = [];
43
+ reruns: number[] = [];
44
+ private id = 1;
45
+ server!: Server;
46
+ constructor(
47
+ readonly origin: string,
48
+ readonly suitesPass: (sha: string) => boolean,
49
+ ) {}
50
+
51
+ add(sha: string, name: string, conclusion: string): void {
52
+ this.runs.set(sha, [...(this.runs.get(sha) ?? []), { id: this.id++, name, status: "completed", conclusion }]);
53
+ }
54
+
55
+ async listen(): Promise<string> {
56
+ this.server = createServer((req, res) => void this.handle(req, res));
57
+ await new Promise<void>((done) => this.server.listen(0, "127.0.0.1", done));
58
+ return `http://127.0.0.1:${(this.server.address() as AddressInfo).port}`;
59
+ }
60
+
61
+ private async handle(req: IncomingMessage, res: ServerResponse): Promise<void> {
62
+ const body = await new Promise<string>((done) => {
63
+ let text = "";
64
+ req.on("data", (c) => (text += c));
65
+ req.on("end", () => done(text));
66
+ });
67
+ const url = new URL(req.url ?? "/", "http://x");
68
+ const send = (status: number, json?: unknown) => {
69
+ res.writeHead(status, { "Content-Type": "application/json" });
70
+ res.end(json === undefined ? "" : JSON.stringify(json));
71
+ };
72
+ let m = /^\/repos\/acme\/app\/commits\/([0-9a-f]+)\/check-runs$/.exec(url.pathname);
73
+ if (req.method === "GET" && m) {
74
+ const runs = this.runs.get(m[1]) ?? [];
75
+ return send(200, { total_count: runs.length, check_runs: runs.map((r) => ({ ...r, completed_at: null, html_url: null })) });
76
+ }
77
+ m = /^\/repos\/acme\/app\/actions\/jobs\/(\d+)\/rerun$/.exec(url.pathname);
78
+ if (req.method === "POST" && m) {
79
+ const id = Number(m[1]);
80
+ this.reruns.push(id);
81
+ // The job runs again and fails the same way.
82
+ for (const [sha, runs] of this.runs) {
83
+ const r = runs.find((x) => x.id === id);
84
+ if (r) this.add(sha, r.name, r.conclusion ?? "failure");
85
+ }
86
+ return send(201, {});
87
+ }
88
+ m = /^\/repos\/acme\/app\/actions\/workflows\/([^/]+)\/dispatches$/.exec(url.pathname);
89
+ if (req.method === "POST" && m) {
90
+ const ref = (JSON.parse(body) as { ref: string }).ref;
91
+ const sha = git(this.origin, "rev-parse", `refs/tags/${ref}^{commit}`);
92
+ this.dispatched.push(sha);
93
+ this.add(sha, "suites", this.suitesPass(sha) ? "success" : "failure");
94
+ return send(204);
95
+ }
96
+ send(404, { message: "not found" });
97
+ }
98
+ }
99
+
100
+ function chant(cwd: string, api: string, ...args: string[]): Promise<{ code: number; out: string; err: string }> {
101
+ return new Promise((done) => {
102
+ const child = spawn(process.execPath, ["--import", pathToFileURL(join(REPO, "node_modules/tsx/dist/loader.mjs")).href, join(REPO, "packages/core/src/cli/main.ts"), ...args], {
103
+ cwd,
104
+ env: { ...process.env, GITHUB_API_URL: api, GITHUB_REPOSITORY: "acme/app", GITHUB_TOKEN: "t", GIT_TERMINAL_PROMPT: "0", NO_COLOR: "1" },
105
+ stdio: ["ignore", "pipe", "pipe"],
106
+ });
107
+ let out = "";
108
+ let err = "";
109
+ child.stdout.on("data", (c) => (out += c));
110
+ child.stderr.on("data", (c) => (err += c));
111
+ child.on("close", (code) => done({ code: code ?? 1, out, err }));
112
+ });
113
+ }
114
+
115
+ describe("a newest phase, bisected end to end (#4169)", () => {
116
+ let root: string;
117
+ let origin: string;
118
+ let forge: FakeGithub;
119
+ let api: string;
120
+ const shas: Record<string, string> = {};
121
+
122
+ beforeAll(async () => {
123
+ origin = scratchDir("chant-builds-e2e-origin-");
124
+ git(origin, "init", "-q", "--bare");
125
+ root = scratchDir("chant-builds-e2e-work-");
126
+ git(root, "init", "-q");
127
+ git(root, "symbolic-ref", "HEAD", "refs/heads/main");
128
+ git(root, "config", "user.name", "t");
129
+ git(root, "config", "user.email", "t@example.com");
130
+ git(root, "remote", "add", "origin", origin);
131
+ writeFiles(root, {
132
+ "chant.workspace.json": declaration([{ name: "app", dir: "app", kind: "other", because: "the app" }], { ci: { green: GREEN } }),
133
+ ".github/workflows/release-suites.yml": SUITES_WORKFLOW,
134
+ });
135
+ for (const name of ["base", "u1", "u2", "u3", "u4", "u5", "top"]) {
136
+ writeFiles(root, { [`app/${name}.txt`]: `${name}\n` });
137
+ git(root, "add", "-A");
138
+ git(root, "commit", "-q", "-m", name);
139
+ shas[name] = git(root, "rev-parse", "HEAD");
140
+ }
141
+ git(root, "push", "-q", "origin", "main");
142
+ const order = ["base", "u1", "u2", "u3", "u4", "u5", "top"].map((n) => shas[n]);
143
+ // u4 broke the suites: they pass below it.
144
+ forge = new FakeGithub(origin, (sha) => order.indexOf(sha) < order.indexOf(shas.u4));
145
+ for (const sha of order) forge.add(sha, "check", "success");
146
+ forge.add(shas.base, "suites", "success");
147
+ forge.add(shas.top, "suites", "failure");
148
+ api = await forge.listen();
149
+ });
150
+
151
+ afterAll(() => forge?.server.close());
152
+
153
+ const tags = () => git(origin, "for-each-ref", "--format=%(refname:lstrip=2)", "refs/tags/").split("\n").filter(Boolean).sort();
154
+ const record = (name: string) => JSON.parse(git(origin, "show", `refs/heads/chant/lifecycle:_ci/${shas[name]}.json`));
155
+ const tick = async () => {
156
+ const r = await chant(root, api, "ci", "tick");
157
+ expect(r.code, r.err).toBe(0);
158
+ return r.out;
159
+ };
160
+
161
+ test("retries once, bisects to the culprit, and leaves the commits below the green one unbuilt", async () => {
162
+ // The failed suites run again once, and fail again.
163
+ expect(await tick()).toContain(`ran suites at ${shas.top.slice(0, 12)} again`);
164
+ expect(forge.reruns).toHaveLength(1);
165
+ expect(record("top")).toMatchObject({ state: "pending", phases: { suites: { reason: "retrying" } } });
166
+
167
+ // Red now: five unbuilt below it, so the middle one, u3.
168
+ await tick();
169
+ expect(forge.dispatched).toEqual([shas.u3]);
170
+ // u3 passes and goes green; then the step back from the red, u5, then u4,
171
+ // each red run of them run again once before the next step.
172
+ await tick();
173
+ expect(tags()).toContain(`ci/green/${shas.u3}`);
174
+ expect(forge.dispatched).toEqual([shas.u3, shas.u5]);
175
+ expect(await tick()).toContain(`ran suites at ${shas.u5.slice(0, 12)} again`);
176
+ await tick();
177
+ expect(forge.dispatched).toEqual([shas.u3, shas.u5, shas.u4]);
178
+ expect(await tick()).toContain(`ran suites at ${shas.u4.slice(0, 12)} again`);
179
+ await tick();
180
+ expect(forge.dispatched).toHaveLength(3);
181
+ expect(forge.reruns).toHaveLength(3);
182
+
183
+ for (const name of ["u4", "u5", "top"]) expect(record(name), name).toMatchObject({ state: "red", phases: { suites: { culprit: shas.u4 } } });
184
+ for (const name of ["u1", "u2"]) expect(record(name), name).toMatchObject({ state: "unbuilt", phases: { suites: { verdict: "unbuilt", reason: "superseded" } } });
185
+ expect(record("u3")).toMatchObject({ state: "green" });
186
+ expect(tags().filter((t) => t.startsWith("ci/build/"))).toEqual([]);
187
+ expect(tags()).toEqual([`ci/green/${shas.base}`, `ci/green/${shas.u3}`].sort());
188
+
189
+ const last = await chant(root, api, "ci", "last-green", "--json");
190
+ const doc = JSON.parse(last.out);
191
+ expect(doc.commit.sha).toBe(shas.u3);
192
+ expect(doc.newer.map((n: { sha: string; record: { state: string; culprit?: string } }) => [n.sha, n.record.state, n.record.culprit])).toEqual([
193
+ [shas.top, "red", shas.u4],
194
+ [shas.u5, "red", shas.u4],
195
+ [shas.u4, "red", shas.u4],
196
+ ]);
197
+ }, 180_000);
198
+
199
+ test("ci run --at asks for a chosen commit and starts the suites there", async () => {
200
+ const r = await chant(root, api, "ci", "run", "--at", shas.u1);
201
+ expect(r.code, r.err).toBe(0);
202
+ expect(r.out).toContain(`Asked for check, suites at ${shas.u1.slice(0, 12)}: ci/build/${shas.u1}. Started release-suites.yml.`);
203
+ expect(tags()).toContain(`ci/build/${shas.u1}`);
204
+ await tick();
205
+ expect(tags()).toContain(`ci/green/${shas.u1}`);
206
+ expect(tags()).not.toContain(`ci/build/${shas.u1}`);
207
+ }, 60_000);
208
+ });
@@ -0,0 +1,174 @@
1
+ /**
2
+ * #4169 — which commits get built: supersession, retries, requests and
3
+ * bisection over judged commits, and the records each commit gets.
4
+ */
5
+
6
+ import { describe, expect, test } from "vitest";
7
+ import { agedOut, BUILD_REQUEST_TTL_MS, planBuilds, recordOf, RETRY_TTL_MS, sameRecord, type BuildRequest, type CiRecord } from "./ci-builds";
8
+ import { evaluateCommit, type TickCommit } from "./ci-green";
9
+ import type { CheckRun } from "./ci-green-forge";
10
+ import type { CiGreen } from "./declaration";
11
+
12
+ const NOW = new Date("2026-10-20T12:00:00Z");
13
+ const ago = (ms: number) => new Date(NOW.getTime() - ms).toISOString().replace(/\.\d{3}Z$/, "Z");
14
+
15
+ const GREEN: CiGreen = {
16
+ branch: "main",
17
+ window: "24h",
18
+ phases: [
19
+ { name: "check", runs: ["check"], skipped: "fail", build: "every", pointer: "/ci/green/phases/check" },
20
+ { name: "suites", runs: ["suites"], skipped: "fail", build: "newest", pointer: "/ci/green/phases/suites" },
21
+ ],
22
+ require: ["check", "suites"],
23
+ signedSince: null,
24
+ source: "checks",
25
+ pointer: "/ci/green",
26
+ };
27
+
28
+ let nextId = 1;
29
+ const run = (name: string, conclusion: string | null, status = "completed"): CheckRun => ({ id: nextId++, name, status, conclusion: status === "completed" ? conclusion : null, completedAt: null, url: null });
30
+ const sha = (n: number) => String(n).padStart(40, "0");
31
+
32
+ /** Commits newest first, each with its runs, judged as the tick judges them. */
33
+ function judged(runsPerCommit: CheckRun[][], before: TickCommit["before"][] = []): { commits: TickCommit[]; runs: Map<string, CheckRun[]> } {
34
+ const runs = new Map<string, CheckRun[]>();
35
+ const commits = runsPerCommit.map((r, i) => {
36
+ const s = sha(runsPerCommit.length - i);
37
+ runs.set(s, r);
38
+ return { sha: s, committed: NOW.toISOString(), before: before[i] ?? "none", verdict: evaluateCommit(GREEN, r), made: null } as TickCommit;
39
+ });
40
+ return { commits, runs };
41
+ }
42
+
43
+ const plan = (commits: TickCommit[], runs: Map<string, CheckRun[]>, extra: Partial<Parameters<typeof planBuilds>[0]> = {}) =>
44
+ planBuilds({ green: GREEN, commits, runs, requests: new Map(), records: new Map(), now: NOW, retryVia: null, ...extra });
45
+
46
+ const suites = (c: TickCommit) => c.verdict!.phases.find((p) => p.phase === "suites")!;
47
+
48
+ describe("supersession (#4169)", () => {
49
+ test("a newest phase with no run below a commit that has one is unbuilt, covered by it; the newest commit stays pending", () => {
50
+ const { commits, runs } = judged([[run("check", "success")], [run("check", "success"), run("suites", "success")], [run("check", "success")]]);
51
+ plan(commits, runs);
52
+ expect(commits.map((c) => c.verdict!.verdict)).toEqual(["pending", "pass", "unbuilt"]);
53
+ expect(suites(commits[2])).toMatchObject({ verdict: "unbuilt", reason: "superseded", coveredBy: commits[1].sha });
54
+ expect(suites(commits[0])).toMatchObject({ verdict: "pending" });
55
+ });
56
+
57
+ test("an every phase is never superseded, and a cancelled run is unbuilt under either", () => {
58
+ const { commits, runs } = judged([[run("check", "success"), run("suites", "success")], [run("suites", "success")], [run("check", "cancelled"), run("suites", "success")]]);
59
+ plan(commits, runs);
60
+ expect(commits.map((c) => c.verdict!.verdict)).toEqual(["pass", "pending", "unbuilt"]);
61
+ expect(commits[2].verdict!.phases[0]).toMatchObject({ phase: "check", verdict: "unbuilt", reason: "cancelled" });
62
+ });
63
+ });
64
+
65
+ describe("bisect (#4169)", () => {
66
+ const green = () => [run("check", "success"), run("suites", "success")];
67
+ const red = () => [run("check", "success"), run("suites", "failure")];
68
+ const none = () => [run("check", "success")];
69
+
70
+ test("a red newest commit asks for the one just below it when four or fewer are unbuilt", () => {
71
+ const { commits, runs } = judged([red(), none(), none(), green()]);
72
+ const p = plan(commits, runs);
73
+ expect(p.requests).toEqual([expect.objectContaining({ commit: commits[1].sha, phases: ["suites"], reason: "bisect", tag: `ci/build/${commits[1].sha}` })]);
74
+ expect(suites(commits[1])).toMatchObject({ verdict: "pending", reason: "requested" });
75
+ });
76
+
77
+ test("halves the range above four, and asks for nothing while a request in it waits", () => {
78
+ const { commits, runs } = judged([red(), none(), none(), none(), none(), none(), none(), green()]);
79
+ const p = plan(commits, runs);
80
+ expect(p.requests.map((r) => r.commit)).toEqual([commits[4].sha]);
81
+ const waiting: BuildRequest = { ...p.requests[0], at: ago(60_000) };
82
+ const again = judged([red(), none(), none(), none(), none(), none(), none(), green()]);
83
+ expect(plan(again.commits, again.runs, { requests: new Map([[waiting.commit, waiting]]) }).requests).toEqual([]);
84
+ });
85
+
86
+ test("names the culprit on it and every commit after it once the red sits on a green", () => {
87
+ const { commits, runs } = judged([red(), none(), red(), green()]);
88
+ // The commit below the newest red is unbuilt between two reds: the culprit is at or below the older red.
89
+ plan(commits, runs);
90
+ expect(commits.map((c) => suites(c).culprit)).toEqual([commits[2].sha, commits[2].sha, commits[2].sha, undefined]);
91
+ });
92
+
93
+ test("names no culprit without a green below, and an every phase gets one too", () => {
94
+ const noGreen = judged([red(), red()]);
95
+ plan(noGreen.commits, noGreen.runs);
96
+ expect(noGreen.commits.map((c) => suites(c).culprit)).toEqual([undefined, undefined]);
97
+ const every = judged([[run("check", "failure")], [run("check", "failure")], [run("check", "success")]]);
98
+ plan(every.commits, every.runs);
99
+ expect(every.commits.map((c) => c.verdict!.phases[0].culprit)).toEqual([every.commits[1].sha, every.commits[1].sha, undefined]);
100
+ });
101
+ });
102
+
103
+ describe("requests (#4169)", () => {
104
+ const req = (commit: string, at: string): BuildRequest => ({ schema: 1, tag: `ci/build/${commit}`, commit, branch: "main", phases: ["suites"], reason: "requested", at });
105
+
106
+ test("a request holds its phase pending; once decided, or too old, or out of the window, it is done", () => {
107
+ const { commits, runs } = judged([[run("check", "success"), run("suites", "success")], [run("check", "success")]]);
108
+ const waiting = req(commits[1].sha, ago(60_000));
109
+ const p = plan(commits, runs, { requests: new Map([[waiting.commit, waiting]]) });
110
+ expect(suites(commits[1])).toMatchObject({ verdict: "pending", reason: "requested" });
111
+ expect(p.resolved).toEqual([]);
112
+
113
+ const old = judged([[run("check", "success"), run("suites", "success")], [run("check", "success")]]);
114
+ const stale = req(old.commits[1].sha, ago(BUILD_REQUEST_TTL_MS + 1));
115
+ expect(plan(old.commits, old.runs, { requests: new Map([[stale.commit, stale]]) }).resolved).toEqual([stale.tag]);
116
+ expect(suites(old.commits[1])).toMatchObject({ verdict: "unbuilt", reason: "request-unserved" });
117
+
118
+ const done = judged([[run("check", "success"), run("suites", "success")]]);
119
+ const served = req(done.commits[0].sha, ago(60_000));
120
+ const gone = req(sha(99), ago(60_000));
121
+ expect(plan(done.commits, done.runs, { requests: new Map([[served.commit, served], [gone.commit, gone]]) }).resolved.sort()).toEqual([served.tag, gone.tag].sort());
122
+ });
123
+ });
124
+
125
+ describe("retry (#4169)", () => {
126
+ test("a failed run with one attempt is run again and holds the commit pending; a second attempt decides", () => {
127
+ const failed = run("check", "failure");
128
+ const { commits, runs } = judged([[failed, run("suites", "success")]]);
129
+ const p = plan(commits, runs, { retryVia: "forge" });
130
+ expect(p.retries).toEqual([{ sha: commits[0].sha, run: failed }]);
131
+ expect(commits[0].verdict!.verdict).toBe("pending");
132
+ expect(commits[0].verdict!.phases[0]).toMatchObject({ verdict: "pending", reason: "retrying" });
133
+
134
+ const twice = judged([[failed, run("check", "failure"), run("suites", "success")]]);
135
+ expect(plan(twice.commits, twice.runs, { retryVia: "forge" }).retries).toEqual([]);
136
+ expect(twice.commits[0].verdict!.verdict).toBe("fail");
137
+ });
138
+
139
+ test("a retry asked for waits an hour for its attempt, then the failure counts; chant runs ask by request; a green commit is never retried", () => {
140
+ const failed = run("check", "failure");
141
+ const asked = (at: string): CiRecord => ({ schema: 1, commit: sha(1), branch: "main", state: "pending", at, phases: {}, retried: { check: { run: failed.id, at } } });
142
+ const waiting = judged([[failed, run("suites", "success")]]);
143
+ expect(plan(waiting.commits, waiting.runs, { retryVia: "forge", records: new Map([[sha(1), asked(ago(60_000))]]) }).retries).toEqual([]);
144
+ expect(waiting.commits[0].verdict!.verdict).toBe("pending");
145
+ const late = judged([[failed, run("suites", "success")]]);
146
+ plan(late.commits, late.runs, { retryVia: "forge", records: new Map([[sha(1), asked(ago(RETRY_TTL_MS + 1))]]) });
147
+ expect(late.commits[0].verdict!.verdict).toBe("fail");
148
+
149
+ const viaRequest = judged([[failed, run("suites", "success")]]);
150
+ expect(plan(viaRequest.commits, viaRequest.runs, { retryVia: "request" }).requests).toEqual([expect.objectContaining({ commit: sha(1), phases: ["check"], reason: "retry" })]);
151
+
152
+ const tagged = judged([[failed, run("suites", "success")]], ["green"]);
153
+ expect(plan(tagged.commits, tagged.runs, { retryVia: "forge" }).retries).toEqual([]);
154
+ });
155
+ });
156
+
157
+ describe("records (#4169)", () => {
158
+ test("say green, red, unbuilt or pending with each phase's verdict, reason and covering commit, and skip the time when compared", () => {
159
+ const { commits, runs } = judged([[run("check", "success"), run("suites", "success")], [run("check", "success")]]);
160
+ plan(commits, runs);
161
+ commits[0].made = { tag: `ci/green/${commits[0].sha}`, message: "" };
162
+ const top = recordOf({ green: GREEN, commit: commits[0], retries: [], requests: new Map(), now: NOW });
163
+ expect(top).toMatchObject({ state: "green", commit: commits[0].sha, phases: { check: { verdict: "pass", build: "every" }, suites: { verdict: "pass", build: "newest" } } });
164
+ const below = recordOf({ green: GREEN, commit: commits[1], retries: [], requests: new Map(), now: NOW });
165
+ expect(below).toMatchObject({ state: "unbuilt", phases: { suites: { verdict: "unbuilt", reason: "superseded", coveredBy: commits[0].sha } } });
166
+ expect(sameRecord({ ...below, at: "2020-01-01T00:00:00Z" }, below)).toBe(true);
167
+ expect(sameRecord({ ...below, state: "pending" }, below)).toBe(false);
168
+ });
169
+
170
+ test("a pending record that left the window is rewritten unbuilt, aged out", () => {
171
+ const pending: CiRecord = { schema: 1, commit: sha(1), branch: "main", state: "pending", at: ago(1), phases: { suites: { verdict: "pending", build: "newest", runs: [] } } };
172
+ expect(agedOut(pending, NOW)).toMatchObject({ state: "unbuilt", phases: { suites: { verdict: "unbuilt", reason: "aged-out" } } });
173
+ });
174
+ });