@joshuafolkken/kit 1.1784.0 → 1.2114.0

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 (690) hide show
  1. package/.claude/agents/reviewer-verification.md +19 -0
  2. package/.claude/agents/reviewer.md +19 -0
  3. package/.claude/settings.json +5 -6
  4. package/.claude/skills/dependency-update/SKILL.md +9 -10
  5. package/.claude/skills/workflow-commands/SKILL.md +6 -6
  6. package/.claude/skills/workflow-commands/background-commands.md +8 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
  13. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  14. package/.claude/skills/workflow-commands/chain-rule.md +41 -32
  15. package/.claude/skills/workflow-commands/delegation.md +6 -6
  16. package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
  17. package/.claude/skills/workflow-commands/followup.md +7 -5
  18. package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
  19. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  20. package/.claude/skills/workflow-commands/issue-comments.md +9 -4
  21. package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +2 -2
  23. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  24. package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
  25. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
  26. package/.claude/skills/workflow-commands/prerequisite.md +3 -2
  27. package/.claude/skills/workflow-commands/retrospective.md +2 -2
  28. package/.claude/skills/workflow-commands/split-assessment.md +7 -5
  29. package/.codex/hooks.json +5 -5
  30. package/.github/actions/setup-pnpm/action.yml +9 -16
  31. package/.github/dependabot.yml +16 -0
  32. package/.github/workflows/auto-tag.yml +45 -0
  33. package/.github/workflows/ci.yml +155 -15
  34. package/.github/workflows/dependabot-auto-merge.yml +63 -4
  35. package/.github/workflows/production.yml +8 -0
  36. package/.github/workflows/publish.yml +59 -45
  37. package/.vscode/settings.json +19 -0
  38. package/CLAUDE.md +5 -12
  39. package/SECURITY.md +2 -2
  40. package/dist/CLAUDE.md +5 -12
  41. package/dist/commands/document-read-cli.js +2 -1
  42. package/dist/commands/document-section-main.js +2 -1
  43. package/dist/commands/read-set-cli.js +194 -134
  44. package/dist/commands/run-prep-main.js +9694 -10173
  45. package/dist/commands/run-step-cli.js +22138 -18285
  46. package/dist/hooks/audit-provision.js +23 -5
  47. package/dist/hooks/chunk-3GKE653K.js +6204 -0
  48. package/dist/hooks/chunk-45CJJOO7.js +1243 -0
  49. package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
  50. package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
  51. package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
  52. package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
  53. package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
  54. package/dist/hooks/chunk-VVNYJWEI.js +30 -0
  55. package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
  56. package/dist/hooks/codex-hook-adapter.js +8 -8
  57. package/dist/hooks/format-edited.js +4 -5
  58. package/dist/hooks/inputs.json +415 -389
  59. package/dist/hooks/pretool-guard.js +7 -6
  60. package/dist/hooks/session-lang.js +101 -258
  61. package/dist/hooks/stop-guard.js +1744 -2576
  62. package/dist/josh.js +7133 -6368
  63. package/dist/version/index.js +161 -1
  64. package/eslint/rules/comment-why-only.js +108 -0
  65. package/package.json +15 -8
  66. package/prompts/coding-standards.md +1 -1
  67. package/prompts/collaboration-workflow/file-edits.md +14 -11
  68. package/prompts/collaboration-workflow/issue-citation.md +0 -4
  69. package/prompts/collaboration-workflow/issue-template.md +40 -21
  70. package/prompts/collaboration-workflow/lane-instructions.md +3 -1
  71. package/prompts/collaboration-workflow/measurement.md +8 -1
  72. package/prompts/collaboration-workflow/operating-rules.md +11 -2
  73. package/prompts/collaboration-workflow/output-bounds.md +6 -2
  74. package/prompts/collaboration-workflow/plan-comment.md +1 -1
  75. package/prompts/collaboration-workflow/principles.md +9 -0
  76. package/prompts/collaboration-workflow/residency.md +3 -2
  77. package/prompts/collaboration-workflow/rule-delivery.md +13 -1
  78. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  79. package/prompts/collaboration-workflow/wip-cap.md +3 -1
  80. package/prompts/review-rubric.md +8 -0
  81. package/prompts/review.md +31 -22
  82. package/prompts/testing-guide.md +1 -0
  83. package/scripts/agent/agent-argv.ts +14 -16
  84. package/scripts/agent/agent-provider.ts +138 -0
  85. package/scripts/agent/agent-role-profile.ts +108 -156
  86. package/scripts/agent/agent-safe-text.ts +20 -5
  87. package/scripts/agent/api-usage-limit.ts +65 -0
  88. package/scripts/agent/claude-agent-argv.ts +19 -9
  89. package/scripts/agent/codex-agent-argv.ts +118 -22
  90. package/scripts/agent/usage-limit-reset.ts +179 -0
  91. package/scripts/auth/claude-token-output.ts +2 -2
  92. package/scripts/auto-ok/auto-ok-author.ts +53 -0
  93. package/scripts/auto-ok/auto-ok-cli.ts +20 -9
  94. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  95. package/scripts/backlog/backlog-drive-cli.ts +6 -1
  96. package/scripts/backlog/backlog-drive-hold.ts +3 -3
  97. package/scripts/backlog/backlog-drive-idle.ts +10 -0
  98. package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
  99. package/scripts/backlog/backlog-drive-launch.ts +8 -2
  100. package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
  101. package/scripts/backlog/backlog-drive-named.ts +28 -7
  102. package/scripts/backlog/backlog-drive-paused.ts +5 -4
  103. package/scripts/backlog/backlog-drive-restore.ts +11 -3
  104. package/scripts/backlog/backlog-drive.ts +21 -60
  105. package/scripts/backlog/backlog-gate-scope.ts +61 -0
  106. package/scripts/backlog/backlog-measure-close.ts +112 -0
  107. package/scripts/backlog/backlog-next.ts +39 -49
  108. package/scripts/backlog/backlog-occupied.ts +2 -2
  109. package/scripts/backlog/backlog-overlap.ts +42 -8
  110. package/scripts/backlog/backlog-plan-cli.ts +4 -1
  111. package/scripts/backlog/backlog-plan-comment.ts +81 -0
  112. package/scripts/backlog/backlog-plan-read.ts +19 -6
  113. package/scripts/backlog/backlog-pool.ts +50 -12
  114. package/scripts/backlog/backlog-rank.ts +60 -13
  115. package/scripts/backlog/backlog-stalled-detect.ts +2 -1
  116. package/scripts/backlog/backlog-waves.ts +17 -2
  117. package/scripts/backlog/backlogrun-launch.ts +7 -4
  118. package/scripts/cost-runtime/context-cut-payback.ts +27 -25
  119. package/scripts/cost-runtime/cost-cli.ts +37 -16
  120. package/scripts/cost-runtime/cost-pricing.ts +9 -0
  121. package/scripts/cost-runtime/cost-verdict.ts +4 -1
  122. package/scripts/delegation/delegation-policy.ts +2 -2
  123. package/scripts/delegation/duplicate-read-guard.ts +1 -0
  124. package/scripts/delegation/investigation-guard.ts +14 -2
  125. package/scripts/delegation/investigation-reads.ts +45 -23
  126. package/scripts/dependabot/dependabot-cooldown.ts +3 -2
  127. package/scripts/discovery/repo-party.ts +80 -20
  128. package/scripts/discovery/session-owner-pin.ts +51 -0
  129. package/scripts/document/capped-print.ts +1 -1
  130. package/scripts/document/command-reference-line-budget.ts +42 -0
  131. package/scripts/document/document-byte-budget.ts +17 -7
  132. package/scripts/document/document-checks.ts +20 -0
  133. package/scripts/document/document-ratchet-check.ts +101 -0
  134. package/scripts/document/document-section.ts +1 -0
  135. package/scripts/document/entry-read-budget.ts +7 -3
  136. package/scripts/document/issue-citation-budget.ts +94 -0
  137. package/scripts/epic/epic-cli-argv.ts +2 -2
  138. package/scripts/epic/epic-fetch.ts +4 -2
  139. package/scripts/epic/epic-graph.ts +7 -2
  140. package/scripts/epic/epic-issue.ts +3 -1
  141. package/scripts/epic/epic-rank.ts +5 -0
  142. package/scripts/exports/unused-members-check.ts +62 -0
  143. package/scripts/exports/unused-members-cli.ts +6 -51
  144. package/scripts/followup/followup-ci-path.ts +67 -0
  145. package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
  146. package/scripts/followup/followup-coderabbit-wait.ts +13 -10
  147. package/scripts/followup/followup-failed-tests.ts +160 -0
  148. package/scripts/followup/followup-job-needs.ts +100 -0
  149. package/scripts/followup/followup-job-rerun.ts +303 -0
  150. package/scripts/followup/followup-main-move.ts +113 -0
  151. package/scripts/followup/git-followup-finish.ts +43 -11
  152. package/scripts/followup/git-followup-pending.ts +3 -3
  153. package/scripts/followup/git-followup-stages.ts +41 -11
  154. package/scripts/followup/git-pr-followup-checks.ts +55 -16
  155. package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
  156. package/scripts/followup/git-pr-followup.ts +62 -27
  157. package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
  158. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
  159. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
  160. package/scripts/gate/busy-average.ts +34 -2
  161. package/scripts/gate/check-related-pinned.ts +43 -0
  162. package/scripts/gate/check-related-reuse.ts +64 -0
  163. package/scripts/gate/check-related-section.ts +57 -0
  164. package/scripts/gate/check-related-unplanned.ts +34 -0
  165. package/scripts/gate/check-related.ts +265 -0
  166. package/scripts/gate/command-memory.ts +1 -1
  167. package/scripts/gate/core-admission.ts +24 -6
  168. package/scripts/gate/core-budget-timing.ts +20 -6
  169. package/scripts/gate/core-budget.ts +40 -11
  170. package/scripts/gate/core-wait-record.ts +3 -2
  171. package/scripts/gate/core-wait.ts +25 -2
  172. package/scripts/gate/core-weights.ts +15 -7
  173. package/scripts/gate/gate-labels.ts +46 -0
  174. package/scripts/gate/gate-ledger.ts +14 -6
  175. package/scripts/gate/gate-plan.ts +103 -58
  176. package/scripts/gate/gate-report.ts +9 -7
  177. package/scripts/gate/gate-run-steps.ts +26 -3
  178. package/scripts/gate/gate-skip.ts +45 -6
  179. package/scripts/gate/gate-static-group.ts +60 -0
  180. package/scripts/gate/gate-steps.ts +2 -1
  181. package/scripts/gate/gate-tree.ts +4 -2
  182. package/scripts/gate/hook-gate-reuse.ts +19 -2
  183. package/scripts/gate/metrics-totals.ts +59 -0
  184. package/scripts/gate/scoped-green.ts +68 -15
  185. package/scripts/gate/since-green.ts +14 -6
  186. package/scripts/gate/structural-checks.ts +96 -0
  187. package/scripts/gate/verification-gate.ts +51 -16
  188. package/scripts/gate/verification-inputs.ts +21 -6
  189. package/scripts/gate/verification-tail.ts +52 -0
  190. package/scripts/gh/gh-author-association.ts +23 -2
  191. package/scripts/gh/git-ai-review-scan.ts +1 -1
  192. package/scripts/gh/git-gh-api-path.ts +12 -3
  193. package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
  194. package/scripts/gh/git-gh-exec.ts +32 -2
  195. package/scripts/gh/git-gh-issue-read.ts +4 -2
  196. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  197. package/scripts/gh/git-gh-issue-write.ts +8 -2
  198. package/scripts/gh/git-gh-issue.ts +12 -5
  199. package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
  200. package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
  201. package/scripts/gh/git-gh-pr-read.ts +74 -13
  202. package/scripts/gh/git-gh-pr-rest.ts +3 -1
  203. package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
  204. package/scripts/gh/git-gh-pr-too-large.ts +20 -0
  205. package/scripts/gh/git-gh-pr.ts +21 -7
  206. package/scripts/gh/git-gh-retry.ts +141 -0
  207. package/scripts/gh/git-pr-ai-review.ts +15 -6
  208. package/scripts/gh/git-pr-checks-eval.ts +3 -0
  209. package/scripts/gh/git-pr-checks.ts +5 -5
  210. package/scripts/git/base-tree.ts +30 -0
  211. package/scripts/git/change-base.ts +4 -10
  212. package/scripts/git/changed-paths-sync.ts +63 -0
  213. package/scripts/git/changed-paths.ts +16 -1
  214. package/scripts/git/git-common-directory.ts +11 -1
  215. package/scripts/git/git-diff-reads.ts +66 -20
  216. package/scripts/git/git-error.ts +9 -2
  217. package/scripts/git/git-schemas.ts +13 -0
  218. package/scripts/git/git-worktree.ts +13 -0
  219. package/scripts/git/gone-branch.ts +4 -1
  220. package/scripts/git/main-merge-budget-conflict.ts +89 -0
  221. package/scripts/git/main-merge-guard.ts +55 -18
  222. package/scripts/git/main-merge-regenerate.ts +59 -25
  223. package/scripts/git/main-merge.ts +53 -5
  224. package/scripts/git/no-base-checkout.ts +127 -0
  225. package/scripts/git/stash/git-stash.ts +11 -2
  226. package/scripts/git/stash/stash-push-cli.ts +10 -0
  227. package/scripts/hooks/batch-guard.ts +40 -21
  228. package/scripts/hooks/edited-cspell.ts +37 -11
  229. package/scripts/hooks/format-edited-file.ts +22 -2
  230. package/scripts/hooks/lane-plan-guard.ts +50 -0
  231. package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
  232. package/scripts/hooks/pre-commit-lint.ts +14 -1
  233. package/scripts/hooks/pre-push-related-switch.ts +10 -0
  234. package/scripts/hooks/pre-push-unit.ts +76 -10
  235. package/scripts/hooks/pretool-guard.ts +2 -0
  236. package/scripts/hooks/reserved-run.ts +14 -1
  237. package/scripts/hooks/run-hook.sh +23 -8
  238. package/scripts/hooks/step-zero-notice.ts +6 -1
  239. package/scripts/hooks/stop-actions.ts +3 -15
  240. package/scripts/hooks/stop-guard.ts +7 -6
  241. package/scripts/init/distributed-paths.ts +1 -0
  242. package/scripts/init/hook-command-rewrite.ts +5 -1
  243. package/scripts/init/hook-launch.ts +4 -2
  244. package/scripts/issue/aggregation-split.ts +52 -0
  245. package/scripts/issue/defect-origin-line.ts +19 -1
  246. package/scripts/issue/defect-rate-cli.ts +5 -102
  247. package/scripts/issue/defect-report.ts +4 -4
  248. package/scripts/issue/effect-audit-cli.ts +178 -0
  249. package/scripts/issue/effect-audit.ts +185 -0
  250. package/scripts/issue/expected-effect-lint.ts +64 -9
  251. package/scripts/issue/git-next-issues.ts +5 -0
  252. package/scripts/issue/issue-body-or-comment.ts +38 -4
  253. package/scripts/issue/issue-cite-cli.ts +1 -3
  254. package/scripts/issue/issue-classification.ts +1 -0
  255. package/scripts/issue/issue-closing-pr.ts +8 -1
  256. package/scripts/issue/issue-evidence-exit.ts +4 -2
  257. package/scripts/issue/issue-file-cap.ts +58 -0
  258. package/scripts/issue/issue-file-cli.ts +111 -38
  259. package/scripts/issue/issue-file-deferral.ts +52 -0
  260. package/scripts/issue/issue-file-fold.ts +29 -10
  261. package/scripts/issue/issue-file-interrupt.ts +85 -0
  262. package/scripts/issue/issue-file-lane-hint.ts +30 -0
  263. package/scripts/issue/issue-file.ts +43 -6
  264. package/scripts/issue/issue-labels.ts +19 -2
  265. package/scripts/issue/issue-lint-cli.ts +27 -2
  266. package/scripts/issue/issue-measured-cli.ts +13 -35
  267. package/scripts/issue/issue-measured-close.ts +58 -0
  268. package/scripts/issue/issue-not-before-label.ts +2 -2
  269. package/scripts/issue/issue-not-before.ts +4 -1
  270. package/scripts/issue/issue-priority-cli.ts +60 -0
  271. package/scripts/issue/issue-priority-raise.ts +55 -0
  272. package/scripts/issue/issue-priority.ts +174 -43
  273. package/scripts/issue/issue-rank.ts +21 -4
  274. package/scripts/issue/issue-read-cli.ts +32 -6
  275. package/scripts/issue/issue-read.ts +37 -12
  276. package/scripts/issue/issue-sample-gate.ts +95 -74
  277. package/scripts/issue/issue-sample-prime.ts +127 -0
  278. package/scripts/issue/issue-sample-required.ts +57 -0
  279. package/scripts/issue/issue-sample-source.ts +12 -11
  280. package/scripts/issue/issue-search.ts +124 -0
  281. package/scripts/issue/issue-state.ts +13 -1
  282. package/scripts/issue/issue-template-section.ts +43 -0
  283. package/scripts/issue/issue-watch-diff.ts +31 -1
  284. package/scripts/issue/issue-watch-hook.ts +11 -2
  285. package/scripts/issue/issue-watch.ts +55 -12
  286. package/scripts/issue/markdown-section.ts +1 -0
  287. package/scripts/issue/measure-declaration.ts +121 -0
  288. package/scripts/issue/measure-rerun-cli.ts +7 -70
  289. package/scripts/issue/plan-consumers-section.ts +22 -1
  290. package/scripts/issue/reproduction-measure.ts +3 -1
  291. package/scripts/issue/size-estimate.ts +90 -0
  292. package/scripts/issue/trusted-measure.ts +84 -0
  293. package/scripts/josh/command-usage.ts +3 -2
  294. package/scripts/josh/durable-state-migrate.ts +105 -0
  295. package/scripts/josh/durable-state.ts +112 -0
  296. package/scripts/josh/josh-aliases.ts +95 -0
  297. package/scripts/josh/josh-command-map.ts +1 -82
  298. package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
  299. package/scripts/josh/josh-commands-ai.ts +5 -5
  300. package/scripts/josh/josh-commands-development.ts +59 -58
  301. package/scripts/josh/josh-commands-issue-write.ts +23 -4
  302. package/scripts/josh/josh-commands-issue.ts +32 -0
  303. package/scripts/josh/josh-commands-lane.ts +87 -4
  304. package/scripts/josh/josh-commands-lint.ts +16 -3
  305. package/scripts/josh/josh-commands-maintenance.ts +15 -0
  306. package/scripts/josh/josh-commands-review.ts +41 -6
  307. package/scripts/josh/josh-commands-ship.ts +56 -8
  308. package/scripts/josh/josh-commands-test-check.ts +80 -0
  309. package/scripts/josh/josh-commands-workflow.ts +1 -1
  310. package/scripts/josh/josh-environment-file.ts +43 -3
  311. package/scripts/josh/josh-in-process.ts +2 -2
  312. package/scripts/josh/josh-logic.ts +5 -3
  313. package/scripts/josh/josh-run.ts +19 -1
  314. package/scripts/josh/run-record-path.ts +15 -0
  315. package/scripts/josh/stamp-file.ts +11 -2
  316. package/scripts/lane/lane-adopt-cli.ts +98 -0
  317. package/scripts/lane/lane-adopt.ts +208 -0
  318. package/scripts/lane/lane-await.ts +5 -8
  319. package/scripts/lane/lane-baseline.ts +123 -0
  320. package/scripts/lane/lane-breakdown-cli.ts +64 -0
  321. package/scripts/lane/lane-breakdown-implement.ts +170 -0
  322. package/scripts/lane/lane-breakdown-laps.ts +107 -0
  323. package/scripts/lane/lane-breakdown-pinned.ts +76 -0
  324. package/scripts/lane/lane-breakdown-text.ts +145 -0
  325. package/scripts/lane/lane-breakdown.ts +239 -0
  326. package/scripts/lane/lane-check-stats.ts +29 -8
  327. package/scripts/lane/lane-child-invocation.ts +9 -2
  328. package/scripts/lane/lane-child-mark-keys.ts +12 -0
  329. package/scripts/lane/lane-child-marker.ts +20 -1
  330. package/scripts/lane/lane-ci-path-stats.ts +73 -0
  331. package/scripts/lane/lane-claim-stats.ts +84 -0
  332. package/scripts/lane/lane-close.ts +8 -3
  333. package/scripts/lane/lane-dispatch-log.ts +3 -3
  334. package/scripts/lane/lane-dispatch.ts +2 -2
  335. package/scripts/lane/lane-gap-stats.ts +7 -6
  336. package/scripts/lane/lane-guard-policy.ts +7 -1
  337. package/scripts/lane/lane-implement-stats.ts +207 -0
  338. package/scripts/lane/lane-implement-subagents.ts +90 -0
  339. package/scripts/lane/lane-investigation-switch.ts +16 -0
  340. package/scripts/lane/lane-ledger-kind.ts +31 -0
  341. package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
  342. package/scripts/lane/lane-ledger-schema.ts +119 -36
  343. package/scripts/lane/lane-ledger-write.ts +257 -0
  344. package/scripts/lane/lane-ledger.ts +31 -177
  345. package/scripts/lane/lane-load.ts +1 -1
  346. package/scripts/lane/lane-measure-cli.ts +176 -59
  347. package/scripts/lane/lane-net-read.ts +44 -0
  348. package/scripts/lane/lane-net-time.ts +212 -0
  349. package/scripts/lane/lane-open.ts +16 -7
  350. package/scripts/lane/lane-output.ts +2 -4
  351. package/scripts/lane/lane-overrun-prompt.ts +147 -0
  352. package/scripts/lane/lane-overrun.ts +274 -0
  353. package/scripts/lane/lane-paths.ts +24 -1
  354. package/scripts/lane/lane-pause-cli.ts +4 -1
  355. package/scripts/lane/lane-paused-spans.ts +97 -0
  356. package/scripts/lane/lane-plan-check.ts +207 -0
  357. package/scripts/lane/lane-plan-cli.ts +41 -0
  358. package/scripts/lane/lane-plan.ts +144 -0
  359. package/scripts/lane/lane-registry.ts +14 -1
  360. package/scripts/lane/lane-relaunch.ts +1 -1
  361. package/scripts/lane/lane-round-one-stats.ts +51 -0
  362. package/scripts/lane/lane-sampler.ts +23 -4
  363. package/scripts/lane/lane-session-action.ts +93 -18
  364. package/scripts/lane/lane-session-broker.ts +49 -21
  365. package/scripts/lane/lane-session-reread-main.ts +14 -0
  366. package/scripts/lane/lane-session-reread.ts +51 -0
  367. package/scripts/lane/lane-ship-lap-stats.ts +74 -0
  368. package/scripts/lane/lane-sleep.ts +58 -0
  369. package/scripts/lane/lane-spent.ts +1 -1
  370. package/scripts/lane/lane-split-stats.ts +28 -0
  371. package/scripts/lane/lane-stage-stats.ts +13 -1
  372. package/scripts/lane/lane-state.ts +41 -9
  373. package/scripts/lane/lane-stats-tables.ts +58 -0
  374. package/scripts/lane/lane-stats.ts +21 -5
  375. package/scripts/lane/lane-stop-stats.ts +51 -19
  376. package/scripts/lane/lane-tail-stats.ts +32 -0
  377. package/scripts/lane/lane-transcript-calls.ts +67 -10
  378. package/scripts/lane/lane-transcript-stats.ts +44 -12
  379. package/scripts/lane/lane-value-cell.ts +72 -0
  380. package/scripts/lane/lane-value.ts +118 -0
  381. package/scripts/lane/lane-withdraw-closed.ts +124 -0
  382. package/scripts/lane/lane-withdraw.ts +108 -31
  383. package/scripts/lane/lane-yield.ts +85 -0
  384. package/scripts/lane/openai-lane-ship.ts +12 -11
  385. package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
  386. package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
  387. package/scripts/lane/openai-lane-supervisor.ts +5 -4
  388. package/scripts/lib/poll.ts +38 -9
  389. package/scripts/lib/settled-together.ts +10 -0
  390. package/scripts/lib/shell-quote.ts +21 -0
  391. package/scripts/lib/token-shape.ts +111 -0
  392. package/scripts/lint/eslint-daemon-sources.ts +79 -0
  393. package/scripts/lint/eslint-daemon.ts +45 -6
  394. package/scripts/lint/eslint-warm-cli.ts +7 -0
  395. package/scripts/lint/eslint-warm.ts +66 -0
  396. package/scripts/lint/import-graph.ts +141 -11
  397. package/scripts/lint/lint-gate-scope.ts +2 -2
  398. package/scripts/lint/lint-parallel.ts +54 -6
  399. package/scripts/lint/lint-related.ts +154 -33
  400. package/scripts/lint/rename-cli.ts +179 -0
  401. package/scripts/lint/rename-identifier.ts +192 -0
  402. package/scripts/lint/type-only-imports.ts +58 -0
  403. package/scripts/notify/telegram-notify.ts +12 -3
  404. package/scripts/observations/observation-record.ts +42 -16
  405. package/scripts/observations/observations-flush.ts +32 -9
  406. package/scripts/overrides/overrides-check.ts +9 -1
  407. package/scripts/release/release-approval-prune.ts +7 -1
  408. package/scripts/repo/required-checks-logic.ts +4 -1
  409. package/scripts/repo/ruleset-check.ts +4 -3
  410. package/scripts/report/markdown-shape.ts +28 -0
  411. package/scripts/report/report-format-reference.ts +15 -1
  412. package/scripts/report/report-labels.ts +12 -1
  413. package/scripts/report/report-lint-child-summary.ts +94 -0
  414. package/scripts/report/report-lint-cli.ts +26 -7
  415. package/scripts/report/report-lint.ts +2 -20
  416. package/scripts/review/prose-stale-cli.ts +88 -0
  417. package/scripts/review/prose-stale-scan.ts +152 -0
  418. package/scripts/review/review-attest.ts +73 -23
  419. package/scripts/review/review-brief-cli.ts +68 -19
  420. package/scripts/review/review-brief-paths.ts +22 -0
  421. package/scripts/review/review-brief.ts +14 -7
  422. package/scripts/review/review-comment-carry.ts +47 -0
  423. package/scripts/review/review-comment-round.ts +85 -22
  424. package/scripts/review/review-finding-ledger.ts +109 -7
  425. package/scripts/review/review-finding-text.ts +43 -0
  426. package/scripts/review/review-interaction.ts +11 -0
  427. package/scripts/review/review-lane-handover.ts +68 -0
  428. package/scripts/review/review-lint-cli.ts +57 -0
  429. package/scripts/review/review-lint-findings.ts +92 -0
  430. package/scripts/review/review-lint-sections.ts +80 -0
  431. package/scripts/review/review-lint.ts +221 -0
  432. package/scripts/review/review-prior-findings.ts +102 -0
  433. package/scripts/review/review-readers-scan.ts +4 -2
  434. package/scripts/review/review-record-cli.ts +177 -54
  435. package/scripts/review/review-record-code-round.ts +55 -0
  436. package/scripts/review/review-record-comment.ts +60 -0
  437. package/scripts/review/review-record-interaction.ts +35 -0
  438. package/scripts/review/review-record-pinned.ts +108 -0
  439. package/scripts/review/review-record.ts +25 -1
  440. package/scripts/review/review-reviewed-tree.ts +307 -0
  441. package/scripts/review/review-round-one-closure.ts +62 -0
  442. package/scripts/review/review-round-reuse.ts +106 -0
  443. package/scripts/review/review-round2-cli.ts +91 -29
  444. package/scripts/review/review-round2-skip-comment.ts +55 -0
  445. package/scripts/review/review-round2.ts +24 -8
  446. package/scripts/review/review-stamps.ts +7 -3
  447. package/scripts/review/review-stop-log.ts +20 -6
  448. package/scripts/review/review-stops-cli.ts +2 -1
  449. package/scripts/review/review-submit-cli.ts +101 -0
  450. package/scripts/rules/agent-model.ts +1 -1
  451. package/scripts/rules/bash-triggers.ts +3 -3
  452. package/scripts/rules/concise-reply.ts +88 -0
  453. package/scripts/rules/credential-body.ts +77 -0
  454. package/scripts/rules/decision-oracle.ts +1 -1
  455. package/scripts/rules/delivered-rules.ts +36 -17
  456. package/scripts/rules/destructive-command.ts +3 -11
  457. package/scripts/rules/direct-issue-comment.ts +34 -12
  458. package/scripts/rules/direct-issue-read.ts +191 -0
  459. package/scripts/rules/direct-pr-create.ts +4 -24
  460. package/scripts/rules/early-heartbeat.ts +2 -0
  461. package/scripts/rules/edit-tool.ts +27 -0
  462. package/scripts/rules/file-edit-rows.ts +17 -0
  463. package/scripts/rules/file-rewrite.ts +146 -0
  464. package/scripts/rules/firing-point.ts +3 -2
  465. package/scripts/rules/gh-api.ts +128 -7
  466. package/scripts/rules/gh-repo-selector.ts +84 -0
  467. package/scripts/rules/git-force.ts +2 -2
  468. package/scripts/rules/guard-remedy.ts +14 -5
  469. package/scripts/rules/implementation-cut.ts +61 -28
  470. package/scripts/rules/investigator-foreground.ts +231 -0
  471. package/scripts/rules/issue-citation.ts +15 -60
  472. package/scripts/rules/issue-comments.ts +9 -4
  473. package/scripts/rules/josh-git-bare.ts +1 -1
  474. package/scripts/rules/lane-background.ts +41 -13
  475. package/scripts/rules/lane-direct-tool.ts +107 -9
  476. package/scripts/rules/lane-interactive-ask.ts +4 -1
  477. package/scripts/rules/lane-park.ts +4 -2
  478. package/scripts/rules/lane-spilled-read.ts +90 -0
  479. package/scripts/rules/lane-split-park.ts +1 -1
  480. package/scripts/rules/lane-switch-main.ts +4 -4
  481. package/scripts/rules/last-prompt.ts +20 -47
  482. package/scripts/rules/oracle-consulted.ts +1 -1
  483. package/scripts/rules/permission-guards.ts +5 -1
  484. package/scripts/rules/piped-verification-shapes.ts +110 -0
  485. package/scripts/rules/piped-verification.ts +67 -17
  486. package/scripts/rules/poll-loop.ts +72 -9
  487. package/scripts/rules/pr-merge.ts +5 -5
  488. package/scripts/rules/pre-gate-cut.ts +3 -3
  489. package/scripts/rules/protected-files.ts +88 -24
  490. package/scripts/rules/reply-language.ts +3 -17
  491. package/scripts/rules/reply-prose.ts +34 -0
  492. package/scripts/rules/repo-admin.ts +132 -0
  493. package/scripts/rules/reviewer-foreground.ts +88 -0
  494. package/scripts/rules/round-one-check-related.ts +120 -0
  495. package/scripts/rules/rule-body-guard.ts +5 -15
  496. package/scripts/rules/rule-value-cache.ts +53 -5
  497. package/scripts/rules/rule-value-candidates.ts +84 -0
  498. package/scripts/rules/rule-value-cli.ts +59 -11
  499. package/scripts/rules/rule-value.ts +17 -4
  500. package/scripts/rules/run-tail-rule.ts +1 -1
  501. package/scripts/rules/sed-in-place.ts +196 -0
  502. package/scripts/rules/shell-segments.ts +10 -2
  503. package/scripts/rules/stop-rule-rows.ts +1 -13
  504. package/scripts/rules/stop-rules.ts +33 -58
  505. package/scripts/rules/test-declared-commit.ts +1 -1
  506. package/scripts/rules/third-party-write.ts +110 -23
  507. package/scripts/rules/timed-rerun.ts +1 -1
  508. package/scripts/rules/turn-filings.ts +90 -0
  509. package/scripts/rules/worktree-guard.ts +22 -7
  510. package/scripts/run/board/run-board-ending-reason.ts +6 -2
  511. package/scripts/run/board/run-board-fit.ts +24 -1
  512. package/scripts/run/board/run-board-found.ts +134 -0
  513. package/scripts/run/board/run-board-header.ts +8 -0
  514. package/scripts/run/board/run-board-labels.ts +11 -1
  515. package/scripts/run/board/run-board-lane-time.ts +1 -0
  516. package/scripts/run/board/run-board-link.ts +15 -2
  517. package/scripts/run/board/run-board-notes.ts +7 -1
  518. package/scripts/run/board/run-board-operation.ts +4 -2
  519. package/scripts/run/board/run-board-phase.ts +2 -5
  520. package/scripts/run/board/run-board-plan.ts +1 -1
  521. package/scripts/run/board/run-board-read.ts +46 -1
  522. package/scripts/run/board/run-board-reason.ts +20 -8
  523. package/scripts/run/board/run-board-render-checks.ts +3 -2
  524. package/scripts/run/board/run-board-render-drawn.ts +11 -0
  525. package/scripts/run/board/run-board-render-found.ts +157 -0
  526. package/scripts/run/board/run-board-render-lead.ts +9 -0
  527. package/scripts/run/board/run-board-render-legend.ts +19 -0
  528. package/scripts/run/board/run-board-render-tail.ts +1 -1
  529. package/scripts/run/board/run-board-render.ts +82 -18
  530. package/scripts/run/board/run-board-spin.ts +1 -3
  531. package/scripts/run/board/run-board-status.ts +2 -0
  532. package/scripts/run/board/run-board-tick.ts +39 -10
  533. package/scripts/run/carry/run-carry-ended.ts +3 -2
  534. package/scripts/run/carry/run-carry.ts +3 -2
  535. package/scripts/run/cut/run-cut-cli.ts +33 -133
  536. package/scripts/run/cut/run-cut-digest.ts +152 -0
  537. package/scripts/run/cut/run-cut-handoff.ts +52 -8
  538. package/scripts/run/cut/run-cut-report.ts +24 -3
  539. package/scripts/run/cut/run-cut-resume.ts +129 -0
  540. package/scripts/run/cut/run-cut-review.ts +105 -0
  541. package/scripts/run/cut/run-cut.ts +18 -7
  542. package/scripts/run/detached-launch.ts +1 -1
  543. package/scripts/run/entry/run-entry-cli.ts +88 -19
  544. package/scripts/run/entry/run-entry-spill.ts +83 -0
  545. package/scripts/run/entry/run-entry-stop.ts +5 -1
  546. package/scripts/run/entry/run-entry-trust.ts +91 -0
  547. package/scripts/run/event/run-event-baseline.ts +56 -0
  548. package/scripts/run/event/run-event-filed.ts +9 -3
  549. package/scripts/run/event/run-event-overrun.ts +197 -0
  550. package/scripts/run/event/run-event-render.ts +4 -0
  551. package/scripts/run/event/run-event-stream.ts +25 -3
  552. package/scripts/run/hold/run-stop-notify.ts +3 -8
  553. package/scripts/run/merge/run-merge-cap.ts +57 -0
  554. package/scripts/run/merge/run-merge-cli.ts +66 -23
  555. package/scripts/run/merge/run-merge-outcome.ts +2 -0
  556. package/scripts/run/merge/run-merge-ship.ts +13 -3
  557. package/scripts/run/merge/run-merge-signals.ts +40 -23
  558. package/scripts/run/merge/run-merge-steps.ts +56 -36
  559. package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
  560. package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
  561. package/scripts/run/merge/run-merge.ts +41 -14
  562. package/scripts/run/review/run-review-cli.ts +306 -20
  563. package/scripts/run/review/run-review-early-push.ts +121 -0
  564. package/scripts/run/review/run-review-join-read.ts +94 -0
  565. package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
  566. package/scripts/run/review/run-review-pinned-join.ts +216 -0
  567. package/scripts/run/review/run-review-pinned-steps.ts +383 -0
  568. package/scripts/run/review/run-review-pinned-tree.ts +37 -0
  569. package/scripts/run/review/run-review-pinned.ts +53 -0
  570. package/scripts/run/review/run-review-steps.ts +70 -34
  571. package/scripts/run/review/run-review.ts +26 -6
  572. package/scripts/run/run-parent-cut-hook.ts +3 -4
  573. package/scripts/run/run-prep.ts +1 -0
  574. package/scripts/run/run-stage-read.ts +2 -6
  575. package/scripts/run/run-step-cli.ts +17 -5
  576. package/scripts/run/run-step-lane-plan.ts +27 -0
  577. package/scripts/run/run-step-section.ts +26 -7
  578. package/scripts/run/run-step.ts +5 -2
  579. package/scripts/run/ship/run-ship-args.ts +19 -0
  580. package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
  581. package/scripts/run/ship/run-ship-cli.ts +131 -39
  582. package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
  583. package/scripts/run/ship/run-ship-detach.ts +10 -4
  584. package/scripts/run/ship/run-ship-handover.ts +106 -0
  585. package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
  586. package/scripts/run/ship/run-ship-lap.ts +63 -0
  587. package/scripts/run/ship/run-ship-next.ts +27 -26
  588. package/scripts/run/ship/run-ship-overlap.ts +8 -3
  589. package/scripts/run/ship/run-ship-place.ts +9 -2
  590. package/scripts/run/ship/run-ship-planned.ts +92 -0
  591. package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
  592. package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
  593. package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
  594. package/scripts/run/ship/run-ship-preflight.ts +16 -4
  595. package/scripts/run/ship/run-ship-probe.ts +23 -9
  596. package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
  597. package/scripts/run/ship/run-ship-relaunch.ts +51 -0
  598. package/scripts/run/ship/run-ship-return.ts +103 -29
  599. package/scripts/run/ship/run-ship-review-reason.ts +24 -0
  600. package/scripts/run/ship/run-ship-review-steps.ts +230 -59
  601. package/scripts/run/ship/run-ship-review.ts +171 -51
  602. package/scripts/run/ship/run-ship-reviewer.ts +138 -42
  603. package/scripts/run/ship/run-ship-scoped.ts +39 -10
  604. package/scripts/run/ship/run-ship-stage.ts +49 -13
  605. package/scripts/run/ship/run-ship-steps.ts +24 -4
  606. package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
  607. package/scripts/run/ship/run-ship-stop-context.ts +50 -13
  608. package/scripts/run/ship/run-ship-sync.ts +171 -30
  609. package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
  610. package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
  611. package/scripts/run/ship/run-ship-waiter.ts +114 -0
  612. package/scripts/run/ship/run-ship.ts +16 -2
  613. package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
  614. package/scripts/run/wake/run-wake-cli.ts +6 -113
  615. package/scripts/run/wake/run-wake-code.ts +53 -11
  616. package/scripts/run/wake/run-wake-driver.ts +5 -3
  617. package/scripts/run/wake/run-wake-finish.ts +137 -0
  618. package/scripts/run/wake/run-wake-loop.ts +5 -3
  619. package/scripts/run/wake/run-wake-ports.ts +11 -3
  620. package/scripts/run/wake/run-wake-session.ts +2 -0
  621. package/scripts/run/wake/run-wake.ts +3 -2
  622. package/scripts/safe-chain/ci-installer-pin.ts +49 -8
  623. package/scripts/safe-chain/project-config.ts +3 -2
  624. package/scripts/security/secretlint-scan.ts +42 -9
  625. package/scripts/security/security-audit-provision-main.ts +9 -1
  626. package/scripts/security/security-audit-provision.ts +1 -1
  627. package/scripts/security/security-audit.ts +21 -1
  628. package/scripts/security/sonar-file.ts +10 -5
  629. package/scripts/split/split-assess.ts +12 -3
  630. package/scripts/split/split-early-notice.ts +116 -0
  631. package/scripts/split/split-measure.ts +20 -2
  632. package/scripts/split/split-resolution.ts +107 -0
  633. package/scripts/test/direct-importers.ts +185 -0
  634. package/scripts/test/pure-files-entries.ts +83 -0
  635. package/scripts/test/related-heavy-tests.ts +81 -0
  636. package/scripts/test/test-no-base.ts +88 -0
  637. package/scripts/test/test-pinned-cache-logic.ts +363 -0
  638. package/scripts/test/test-pinned-cache.ts +337 -0
  639. package/scripts/test/test-pinned-emit.ts +49 -0
  640. package/scripts/test/test-pinned-functions.ts +116 -0
  641. package/scripts/test/test-pinned-ledger.ts +80 -0
  642. package/scripts/test/test-pinned-logic.ts +208 -20
  643. package/scripts/test/test-pinned-main.ts +84 -18
  644. package/scripts/test/test-pinned-pool.ts +73 -0
  645. package/scripts/test/test-pinned-probe.ts +254 -0
  646. package/scripts/test/test-pinned-prune-logic.ts +118 -0
  647. package/scripts/test/test-pinned-reach.ts +194 -0
  648. package/scripts/test/test-pinned-record.ts +196 -0
  649. package/scripts/test/test-pinned-rewire.ts +148 -0
  650. package/scripts/test/test-pinned-spawn.ts +81 -0
  651. package/scripts/test/test-pinned-stage.ts +70 -0
  652. package/scripts/test/test-pinned.ts +258 -47
  653. package/scripts/test/test-reach-cli.ts +61 -0
  654. package/scripts/test/test-read-map.ts +35 -11
  655. package/scripts/test/test-read-recording.ts +17 -5
  656. package/scripts/test/test-read-trace.ts +26 -8
  657. package/scripts/test/test-red-logic.ts +46 -7
  658. package/scripts/test/test-red.ts +34 -14
  659. package/scripts/test/test-related-claim.ts +37 -0
  660. package/scripts/test/test-related-main.ts +4 -1
  661. package/scripts/test/test-related-moved.ts +91 -0
  662. package/scripts/test/test-related-record.ts +43 -23
  663. package/scripts/test/test-related-scope.ts +99 -34
  664. package/scripts/test/test-related-synced.ts +135 -0
  665. package/scripts/test/test-related-touched.ts +84 -0
  666. package/scripts/test/test-related.ts +167 -83
  667. package/scripts/test/test-unit-guard.ts +28 -5
  668. package/scripts/test/unit-durations.ts +41 -0
  669. package/scripts/test/unit-worker-share.ts +5 -4
  670. package/scripts/test/vitest-cache-directory.ts +19 -0
  671. package/scripts/test/vitest-cli-excludes.ts +14 -0
  672. package/scripts/test/vitest-failure-summary.ts +111 -0
  673. package/scripts/test/whole-suite-files.ts +12 -1
  674. package/scripts/time-runtime/time-batch-guard.ts +42 -4
  675. package/scripts/time-runtime/time-bundle-call.ts +1 -1
  676. package/scripts/time-runtime/time-bundles.ts +36 -5
  677. package/scripts/time-runtime/time-markers.ts +2 -2
  678. package/scripts/time-runtime/time-search-call.ts +38 -0
  679. package/scripts/time-runtime/time-shell.ts +1 -1
  680. package/scripts/time-runtime/time-transcript-line.ts +17 -5
  681. package/scripts/version/development-engines-verdict.ts +124 -0
  682. package/scripts/version/latest-corepack.ts +2 -0
  683. package/templates/gitignore +2 -0
  684. package/templates/workflows/ci.yml +41 -0
  685. package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
  686. package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
  687. package/dist/hooks/chunk-TUIFS5E7.js +0 -72
  688. package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
  689. package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
  690. package/scripts/rules/filing-cap.ts +0 -139
@@ -1,6 +1,6 @@
1
1
  import type { SnapshotObserver } from '#scripts/gh/git-pr-checks'
2
2
  import type { PrStateSnapshot } from '#scripts/gh/git-pr-checks-parse'
3
- import { lane_ledger } from '#scripts/lane/lane-ledger'
3
+ import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
4
4
  import type { CodeRabbitState } from '#scripts/run/event/run-event-checks'
5
5
  import { followup_checks_event } from './followup-checks-event'
6
6
 
@@ -57,26 +57,29 @@ function observer_of(wait: CodeRabbitWait): SnapshotObserver {
57
57
  }
58
58
  }
59
59
 
60
- // Both observers on every poll — the board's event line and this timer.
61
- function both(first: SnapshotObserver, second: SnapshotObserver): SnapshotObserver {
62
- return async function observe_both(snapshot: PrStateSnapshot): Promise<void> {
63
- await first(snapshot)
64
- await second(snapshot)
60
+ // Every observer on every poll, started in order — the board's event line and the wait's timers.
61
+ function all(...observers: ReadonlyArray<SnapshotObserver>): SnapshotObserver {
62
+ return async function observe_all(snapshot: PrStateSnapshot): Promise<void> {
63
+ await Promise.all(
64
+ observers.map(async (observer) => {
65
+ await observer(snapshot)
66
+ }),
67
+ )
65
68
  }
66
69
  }
67
70
 
68
71
  async function record_part(stage: string, elapsed_ms: number): Promise<void> {
69
- if (elapsed_ms > NO_TIME) await lane_ledger.record_stage({ stage, elapsed_ms })
72
+ if (elapsed_ms > NO_TIME) await lane_ledger_write.record_stage({ stage, elapsed_ms })
70
73
  }
71
74
 
72
75
  // **A run that never saw CodeRabbit waiting records nothing**, so a row's run count is how many
73
76
  // followups CodeRabbit held up, and its median is over those.
74
77
  async function record(wait: CodeRabbitWait): Promise<void> {
75
- await record_part(lane_ledger.CODERABBIT_WAIT_STAGE, wait.wait_ms)
76
- await record_part(lane_ledger.CODERABBIT_RATE_LIMIT_STAGE, wait.rate_limited_ms)
78
+ await record_part(lane_ledger_write.CODERABBIT_WAIT_STAGE, wait.wait_ms)
79
+ await record_part(lane_ledger_write.CODERABBIT_RATE_LIMIT_STAGE, wait.rate_limited_ms)
77
80
  }
78
81
 
79
- const followup_coderabbit_wait = { both, new_wait, observe, observer_of, record }
82
+ const followup_coderabbit_wait = { all, new_wait, observe, observer_of, record }
80
83
 
81
84
  export type { CodeRabbitWait }
82
85
  export { followup_coderabbit_wait }
@@ -0,0 +1,160 @@
1
+ import path from 'node:path'
2
+ import { git_gh_issue_write } from '#scripts/gh/git-gh-issue-write'
3
+ import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
4
+ import {
5
+ observation_record,
6
+ REFUSED_VERDICT,
7
+ type ObservationEntry,
8
+ } from '#scripts/observations/observation-record'
9
+ import { review_comment_round } from '#scripts/review/review-comment-round'
10
+ import { vitest_failure_summary } from '#scripts/test/vitest-failure-summary'
11
+
12
+ // A CI job that ended `failure` because a unit test went red can still owe nothing to the change: a test
13
+ // the diff never touched fails under runner load (joshuafolkken/kit#5152, a frame-budget test on a busy
14
+ // runner). `followup` reruns such a job once, as it does a stalled one (`followup-job-rerun.ts`), and
15
+ // these are the readings that decide it: the failed tests a job's log names, and whether any of them
16
+ // belongs to the change.
17
+ //
18
+ // **A test belongs to the change when its own file or its subject module moved** — `foo.claim.test.ts`
19
+ // tests `foo.claim.ts` and `foo.ts`. A test reaching a moved module only through an import is missed,
20
+ // and costs one rerun: the second red still stops `followup`, since a rerun attempt is never rerun.
21
+
22
+ // GitHub Actions stamps every log line `<ISO timestamp> `, which vitest's ` FAIL ` line does not begin with.
23
+ const LOG_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\S+Z /u
24
+ const TEST_SUFFIX = /\.test(?<extension>\.[^./]+)$/u
25
+ const SEGMENT_SEPARATOR = '.'
26
+ const OBSERVATION_SLUG = 'followup-unrelated-test-rerun'
27
+ const OBSERVATION_DEPTH = 'd1'
28
+ const NAME_SEPARATOR = '; '
29
+ const UNNAMED_ISSUE =
30
+ 'Did not record the failed tests outside the change: the branch names no issue to post them on.'
31
+
32
+ const RERUN_VERDICT = 'rerun'
33
+ const KEPT_VERDICT = 'kept'
34
+ const UNNAMED_VERDICT = 'unnamed'
35
+
36
+ type TestVerdict = typeof RERUN_VERDICT | typeof KEPT_VERDICT | typeof UNNAMED_VERDICT
37
+
38
+ interface FailedTests {
39
+ files: ReadonlyArray<string>
40
+ names: ReadonlyArray<string>
41
+ }
42
+
43
+ interface UnrelatedFailure {
44
+ job: string
45
+ tests: FailedTests
46
+ }
47
+
48
+ // The failed tests vitest named in a job's log — empty when the job failed on anything but a test.
49
+ function read_failed_tests(log: string): FailedTests {
50
+ const lines = log.split('\n').map((line) => line.replace(LOG_TIMESTAMP, ''))
51
+ const names = vitest_failure_summary.failed_tests(lines.join('\n'))
52
+
53
+ return { files: vitest_failure_summary.failed_files(names), names }
54
+ }
55
+
56
+ // The test file itself, then the module each leading run of its name's segments names.
57
+ function subjects_of(test_file: string): ReadonlyArray<string> {
58
+ const extension = TEST_SUFFIX.exec(test_file)?.groups?.['extension']
59
+
60
+ if (extension === undefined) return [test_file]
61
+
62
+ const directory = path.posix.dirname(test_file)
63
+ const segments = path.posix.basename(test_file, `.test${extension}`).split(SEGMENT_SEPARATOR)
64
+ const modules = segments.map((_, index) =>
65
+ path.posix.join(
66
+ directory,
67
+ `${segments.slice(0, index + 1).join(SEGMENT_SEPARATOR)}${extension}`,
68
+ ),
69
+ )
70
+
71
+ return [test_file, ...modules]
72
+ }
73
+
74
+ function is_in_change(test_file: string, changed: ReadonlySet<string>): boolean {
75
+ return subjects_of(test_file).some((subject) => changed.has(subject))
76
+ }
77
+
78
+ // **`rerun` only when the log named a failed test and none of them belongs to the change**, `kept`
79
+ // when one does. A job red with no test named — a lint, a build, a job gathering others' results — is
80
+ // `unnamed`: rerun only as one waiting on a job red on tests outside the change (`followup-job-needs.ts`).
81
+ function verdict_of(tests: FailedTests, changed: ReadonlySet<string>): TestVerdict {
82
+ if (tests.files.length === 0) return UNNAMED_VERDICT
83
+
84
+ return tests.files.every((file) => !is_in_change(file, changed)) ? RERUN_VERDICT : KEPT_VERDICT
85
+ }
86
+
87
+ // The ledger line a rerun leaves, so a test that keeps failing beside unrelated changes is counted
88
+ // rather than retried into silence. A ledger line's fields are split on ` | `, so the `|<project>|`
89
+ // prefix a workspace test name carries is rewritten.
90
+ function observation_of(failures: ReadonlyArray<UnrelatedFailure>): ObservationEntry {
91
+ const files = new Set(failures.flatMap(({ tests }) => tests.files))
92
+ const names = new Set(failures.flatMap(({ tests }) => tests.names))
93
+ const listed = [...names].map((name) => name.replaceAll('|', '/'))
94
+ const jobs = failures.map(({ job }) => job)
95
+
96
+ return {
97
+ slug: OBSERVATION_SLUG,
98
+ depth: OBSERVATION_DEPTH,
99
+ where: [...files].join(', '),
100
+ what: `followup reran ${jobs.join(', ')}; failed outside the change: ${listed.join(NAME_SEPARATOR)}`,
101
+ }
102
+ }
103
+
104
+ // **The line is posted on the issue, never appended to the tree** (joshuafolkken/kit#5197): the rerun
105
+ // happens during the CI wait, after `prepare_merge` committed the ledger, so an appended line would
106
+ // either be lost with the lane or need a push of its own that restarts CI. `review-comment-round.ts`
107
+ // carries it into the ledger once the issue closes; the count is still read off this tree's ledger.
108
+ async function post(entry: ObservationEntry, issue: number): Promise<string> {
109
+ const assessment = await observation_record.assess({
110
+ entry,
111
+ checkout: observation_ledger_home.ledger_root(),
112
+ now: new Date(),
113
+ })
114
+
115
+ if (assessment.verdict === REFUSED_VERDICT) return `${REFUSED_VERDICT}: ${assessment.reason}`
116
+
117
+ await git_gh_issue_write.issue_comment(
118
+ String(issue),
119
+ review_comment_round.observation_body_of([assessment.line]),
120
+ )
121
+
122
+ return assessment.verdict
123
+ }
124
+
125
+ // A post that fails is reported and does not stop the step it describes; a branch naming no issue has
126
+ // nowhere to post.
127
+ async function record_entry(entry: ObservationEntry, issue: number | undefined): Promise<void> {
128
+ if (issue === undefined) {
129
+ console.info(`⚠️ ${UNNAMED_ISSUE}`)
130
+
131
+ return
132
+ }
133
+
134
+ try {
135
+ console.info(`📝 Recorded the ${entry.slug} observation: ${await post(entry, issue)}`)
136
+ } catch (error) {
137
+ console.info(`⚠️ Could not record the ${entry.slug} observation: ${String(error)}`)
138
+ }
139
+ }
140
+
141
+ async function record(
142
+ failures: ReadonlyArray<UnrelatedFailure>,
143
+ issue: number | undefined,
144
+ ): Promise<void> {
145
+ if (failures.length === 0) return
146
+
147
+ await record_entry(observation_of(failures), issue)
148
+ }
149
+
150
+ const followup_failed_tests = {
151
+ observation_of,
152
+ read_failed_tests,
153
+ record,
154
+ record_entry,
155
+ subjects_of,
156
+ verdict_of,
157
+ }
158
+
159
+ export type { FailedTests, TestVerdict, UnrelatedFailure }
160
+ export { followup_failed_tests, KEPT_VERDICT, OBSERVATION_SLUG, RERUN_VERDICT, UNNAMED_VERDICT }
@@ -0,0 +1,100 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import { yaml_document } from '#scripts/lib/yaml-document'
3
+ import { z } from 'zod'
4
+
5
+ // A job that only gathers others' results — kit's `Checks`, which `needs` the unit shards — ends
6
+ // `failure` beside a shard red on a test the change never touched, and its log names no test
7
+ // (joshuafolkken/kit#5171). It fails because of that shard alone, so it is rerun with it; the rerun
8
+ // endpoint reruns a failed job's dependents in any case. **The workflow's own `needs` says which job
9
+ // waits on which**, read from the checkout the pull request's head is.
10
+ //
11
+ // A job is known by the name a check run carries, which the workflow writes as a template: an
12
+ // expression in it matches anything, and a name with none gains the ` (<matrix values>)` suffix GitHub
13
+ // appends to a matrix job. A job no pattern matches — a reusable workflow's, a name built otherwise —
14
+ // is waited on by nothing here, so its failure is kept.
15
+
16
+ const EXPRESSION = /\$\{\{[^}]*\}\}/u
17
+ const EXPRESSION_MARK = '${{'
18
+ const ANY_TEXT = '.+'
19
+ const MATRIX_SUFFIX = String.raw`(?: \(.+\))?`
20
+ const REGEX_SPECIAL = /[$()*+.?[\\\]^{|}]/gu
21
+
22
+ const needs_schema = z.array(z.string())
23
+
24
+ const job_schema = z.looseObject({
25
+ name: z.string().optional(),
26
+ needs: z.union([z.string(), needs_schema]).optional(),
27
+ })
28
+
29
+ const workflow_schema = z.looseObject({ jobs: z.record(z.string(), job_schema) })
30
+
31
+ type WorkflowJobs = z.infer<typeof workflow_schema>['jobs']
32
+
33
+ interface JobNeeds {
34
+ pattern: RegExp
35
+ needs: ReadonlyArray<RegExp>
36
+ }
37
+
38
+ type NeedsGraph = ReadonlyArray<JobNeeds>
39
+
40
+ function escape(text: string): string {
41
+ return text.replaceAll(REGEX_SPECIAL, String.raw`\$&`)
42
+ }
43
+
44
+ function name_pattern(id: string, name: string = id): RegExp {
45
+ const source = name.includes(EXPRESSION_MARK)
46
+ ? name
47
+ .split(EXPRESSION)
48
+ .map((part) => escape(part))
49
+ .join(ANY_TEXT)
50
+ : `${escape(name)}${MATRIX_SUFFIX}`
51
+
52
+ return new RegExp(`^${source}$`, 'u')
53
+ }
54
+
55
+ function needs_list(needs: string | ReadonlyArray<string> = []): ReadonlyArray<string> {
56
+ return typeof needs === 'string' ? [needs] : needs
57
+ }
58
+
59
+ function pattern_of(jobs: WorkflowJobs, id: string): RegExp {
60
+ return name_pattern(id, jobs[id]?.name)
61
+ }
62
+
63
+ // The jobs of a workflow, each with the names of the jobs it needs; a workflow of another shape has
64
+ // none. YAML that does not parse throws, which `read_graph` answers as none.
65
+ function parse_graph(workflow: string): NeedsGraph {
66
+ const parsed = workflow_schema.safeParse(yaml_document.parse_yaml(workflow))
67
+
68
+ if (!parsed.success) return []
69
+
70
+ const { jobs } = parsed.data
71
+
72
+ return Object.entries(jobs).map(([id, job]) => ({
73
+ pattern: pattern_of(jobs, id),
74
+ needs: needs_list(job.needs).map((need) => pattern_of(jobs, need)),
75
+ }))
76
+ }
77
+
78
+ // **Whether the job named `job_name` needs one of the jobs named in `upstream`.**
79
+ function is_waiting_on(
80
+ graph: NeedsGraph,
81
+ job_name: string,
82
+ upstream: ReadonlyArray<string>,
83
+ ): boolean {
84
+ const needs = graph.filter(({ pattern }) => pattern.test(job_name)).flatMap((job) => job.needs)
85
+
86
+ return upstream.some((name) => needs.some((pattern) => pattern.test(name)))
87
+ }
88
+
89
+ async function read_graph(workflow_path: string): Promise<NeedsGraph> {
90
+ try {
91
+ return parse_graph(await readFile(workflow_path, 'utf8'))
92
+ } catch {
93
+ return []
94
+ }
95
+ }
96
+
97
+ const followup_job_needs = { is_waiting_on, parse_graph, read_graph }
98
+
99
+ export type { NeedsGraph }
100
+ export { followup_job_needs }
@@ -0,0 +1,303 @@
1
+ import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
2
+ import { git_gh_command } from '#scripts/gh/git-gh-command'
3
+ import { git_gh_exec } from '#scripts/gh/git-gh-exec'
4
+ import { git_gh_helpers } from '#scripts/gh/git-gh-helpers'
5
+ import { changed_paths } from '#scripts/git/changed-paths'
6
+ import { git_branch } from '#scripts/git/git-branch'
7
+ import { poll } from '#scripts/lib/poll'
8
+ import { z } from 'zod'
9
+ import {
10
+ followup_failed_tests,
11
+ KEPT_VERDICT,
12
+ RERUN_VERDICT,
13
+ UNNAMED_VERDICT,
14
+ type FailedTests,
15
+ type TestVerdict,
16
+ } from './followup-failed-tests'
17
+ import { followup_job_needs, type NeedsGraph } from './followup-job-needs'
18
+ import { followup_main_move } from './followup-main-move'
19
+
20
+ // A GitHub Actions job whose every step succeeded can still end `cancelled` or `timed_out`: the runner
21
+ // stops reporting after the last step and the job's `timeout-minutes` cuts it off (joshuafolkken/kit#4813,
22
+ // two 3-second jobs cut at 2m0s). Nothing in the change caused it, so `followup` reruns such a job once
23
+ // — as `git-gh-retry.ts` retries a GitHub 5xx — rather than failing a pull request that is green.
24
+ //
25
+ // **A job red only on tests outside the change is rerun the same way** (joshuafolkken/kit#5171): a test
26
+ // the diff never touched failing under runner load stopped `followup` and waited for a relaunched
27
+ // session to type the rerun by hand. Only the run's first attempt qualifies, so a test that fails again
28
+ // stops `followup` as before, and each such rerun leaves an observation (`followup-failed-tests.ts`).
29
+ // A job that failed only for waiting on such a job is rerun with it (`followup-job-needs.ts`).
30
+ //
31
+ // The rerun endpoint reruns **every** failed job of a run, and GitHub refuses it while the run is still
32
+ // going. So each run is waited out first, and it is rerun only when every job that failed in it
33
+ // qualifies: a run holding a job that failed on the change itself is never rerun, or a test the change
34
+ // broke could be retried into a merge.
35
+
36
+ const FAILURE_CONCLUSION = 'failure'
37
+ const FAILED_CONCLUSIONS = [FAILURE_CONCLUSION, 'cancelled', 'timed_out'] as const
38
+ const FAILED_JOB_CONCLUSIONS: ReadonlySet<string> = new Set(FAILED_CONCLUSIONS)
39
+ const ACTIONS_APP_SLUG = 'github-actions'
40
+ const PASSING_STEP_CONCLUSIONS: ReadonlySet<string> = new Set(['success', 'skipped'])
41
+ const RUN_COMPLETED = 'completed'
42
+ const FIRST_ATTEMPT = 1
43
+
44
+ // The longest `timeout-minutes` in `ci.yml` is 10; a run still going past that is not one a rerun helps.
45
+ const COMPLETE_ATTEMPTS = 40
46
+ const COMPLETE_INTERVAL_MS = 15_000
47
+
48
+ // The rerun answers once the new attempt exists, but its check runs can trail it; a re-judge that read
49
+ // the failed run again would fail on the very job just rerun.
50
+ const REGISTER_ATTEMPTS = 10
51
+ const REGISTER_INTERVAL_MS = 3000
52
+
53
+ const failed_check_schema = z.looseObject({
54
+ id: z.number(),
55
+ conclusion: z.enum(FAILED_CONCLUSIONS),
56
+ app: z.looseObject({ slug: z.literal(ACTIONS_APP_SLUG) }),
57
+ })
58
+
59
+ const rollup_schema = z.looseObject({ statusCheckRollup: z.array(z.unknown()) })
60
+
61
+ // REST answers `null` for a step that never finished; a test fixture may leave the field out.
62
+ const step_schema = z.looseObject({ conclusion: z.string().nullish() })
63
+
64
+ const job_schema = z.looseObject({
65
+ id: z.number(),
66
+ name: z.string(),
67
+ run_id: z.number(),
68
+ run_attempt: z.number().nullish(),
69
+ conclusion: z.string().nullish(),
70
+ steps: z.array(step_schema),
71
+ })
72
+
73
+ // `path` is the workflow file's, relative to the repository root.
74
+ const run_schema = z.looseObject({ status: z.string(), path: z.string().optional() })
75
+
76
+ const jobs_page_schema = z.looseObject({ jobs: z.array(job_schema) })
77
+
78
+ type Job = z.infer<typeof job_schema>
79
+
80
+ // A failed job and what its reading says, with the failed tests when its log named some.
81
+ interface Judged {
82
+ job: Job
83
+ verdict: TestVerdict
84
+ tests?: FailedTests
85
+ }
86
+
87
+ function actions_api_path(rest: string): string {
88
+ return `${git_gh_api_path.repo_api_path()}/actions/${rest}`
89
+ }
90
+
91
+ async function read_actions<T>(rest: string, schema: z.ZodType<T>): Promise<T> {
92
+ const json = await git_gh_exec.exec_gh_api({ path: actions_api_path(rest) })
93
+
94
+ return schema.parse(JSON.parse(json))
95
+ }
96
+
97
+ async function read_failed_check_ids(branch_name: string): Promise<Array<number>> {
98
+ const { snapshot_json } = await git_gh_command.pr_get_checks_snapshot(branch_name)
99
+ const rollup = rollup_schema.parse(JSON.parse(snapshot_json)).statusCheckRollup
100
+
101
+ return rollup.flatMap((item) => {
102
+ const parsed = failed_check_schema.safeParse(item)
103
+
104
+ return parsed.success ? parsed.data.id : []
105
+ })
106
+ }
107
+
108
+ // A check run of GitHub Actions has its job's id, which is how the job — and so its run — is found.
109
+ async function read_failed_run_ids(branch_name: string): Promise<Array<number>> {
110
+ const ids = await read_failed_check_ids(branch_name)
111
+ const jobs = await Promise.all(
112
+ ids.map(async (id) => await read_actions(`jobs/${String(id)}`, job_schema)),
113
+ )
114
+
115
+ return [...new Set(jobs.map((job) => job.run_id))]
116
+ }
117
+
118
+ async function is_run_completed(run_id: number): Promise<boolean> {
119
+ const run = await read_actions(`runs/${String(run_id)}`, run_schema)
120
+
121
+ return run.status === RUN_COMPLETED
122
+ }
123
+
124
+ async function await_run_completed(run_id: number): Promise<boolean> {
125
+ return await poll.poll_until(async () => await is_run_completed(run_id), {
126
+ attempts: COMPLETE_ATTEMPTS,
127
+ interval_ms: COMPLETE_INTERVAL_MS,
128
+ })
129
+ }
130
+
131
+ function has_every_step_passed(job: Job): boolean {
132
+ if (job.steps.length === 0) return false
133
+
134
+ return job.steps.every((step) => PASSING_STEP_CONCLUSIONS.has(step.conclusion ?? ''))
135
+ }
136
+
137
+ function is_first_failure(job: Job): boolean {
138
+ return (
139
+ job.conclusion === FAILURE_CONCLUSION && (job.run_attempt ?? FIRST_ATTEMPT) <= FIRST_ATTEMPT
140
+ )
141
+ }
142
+
143
+ // A job whose every step passed stopped on the runner; one red on its first attempt is judged by the
144
+ // failed tests its log names. The log endpoint redirects to the plain text, which `gh api` follows; a
145
+ // vitest log is colored.
146
+ async function judge_job(job: Job, changed: ReadonlySet<string>): Promise<Judged> {
147
+ if (has_every_step_passed(job)) return { job, verdict: RERUN_VERDICT }
148
+ if (!is_first_failure(job)) return { job, verdict: KEPT_VERDICT }
149
+
150
+ const log = await git_gh_exec.exec_gh_api({
151
+ path: actions_api_path(`jobs/${String(job.id)}/logs`),
152
+ should_allow_escapes: true,
153
+ })
154
+ const tests = followup_failed_tests.read_failed_tests(log)
155
+
156
+ return { job, verdict: followup_failed_tests.verdict_of(tests, changed), tests }
157
+ }
158
+
159
+ function is_red_on_tests({ verdict, tests }: Judged): boolean {
160
+ return verdict === RERUN_VERDICT && tests !== undefined
161
+ }
162
+
163
+ // A job red with no test named qualifies only as one waiting on a job red on tests outside the change.
164
+ function settle_unnamed(judged: ReadonlyArray<Judged>, graph: NeedsGraph): Array<Judged> {
165
+ const red_on_tests = judged.filter((entry) => is_red_on_tests(entry)).map(({ job }) => job.name)
166
+
167
+ return judged.map((entry) => {
168
+ if (entry.verdict !== UNNAMED_VERDICT) return entry
169
+
170
+ const is_waiting = followup_job_needs.is_waiting_on(graph, entry.job.name, red_on_tests)
171
+
172
+ return is_waiting ? { job: entry.job, verdict: RERUN_VERDICT } : entry
173
+ })
174
+ }
175
+
176
+ // The workflow is read only when a job named no test; a run without its `path` waits on nothing.
177
+ async function read_needs_graph(
178
+ run_id: number,
179
+ judged: ReadonlyArray<Judged>,
180
+ ): Promise<NeedsGraph> {
181
+ if (judged.every(({ verdict }) => verdict !== UNNAMED_VERDICT)) return []
182
+
183
+ const run = await read_actions(`runs/${String(run_id)}`, run_schema)
184
+
185
+ return run.path === undefined ? [] : await followup_job_needs.read_graph(run.path)
186
+ }
187
+
188
+ // **The jobs the rerun would retry**, or `undefined` when the run must not be rerun: still going, or
189
+ // holding a failed job that does not qualify.
190
+ async function read_rerun_jobs(
191
+ run_id: number,
192
+ changed: ReadonlySet<string>,
193
+ ): Promise<Array<Judged> | undefined> {
194
+ if (!(await await_run_completed(run_id))) return undefined
195
+ const { jobs } = await read_actions(
196
+ `runs/${String(run_id)}/jobs?filter=latest&per_page=100`,
197
+ jobs_page_schema,
198
+ )
199
+ const failed = jobs.filter((job) => FAILED_JOB_CONCLUSIONS.has(job.conclusion ?? ''))
200
+ const judged = await Promise.all(failed.map(async (job) => await judge_job(job, changed)))
201
+ const settled = settle_unnamed(judged, await read_needs_graph(run_id, judged))
202
+
203
+ return settled.every(({ verdict }) => verdict === RERUN_VERDICT) ? settled : undefined
204
+ }
205
+
206
+ // One request per run, not retried: a rerun that landed before a transient failure would be refused
207
+ // the second time.
208
+ async function rerun_runs(run_ids: ReadonlyArray<number>): Promise<void> {
209
+ await Promise.all(
210
+ run_ids.map(
211
+ async (run_id) =>
212
+ await git_gh_exec.exec_gh_api({
213
+ path: actions_api_path(`runs/${String(run_id)}/rerun-failed-jobs`),
214
+ method: 'POST',
215
+ should_retry: false,
216
+ }),
217
+ ),
218
+ )
219
+ }
220
+
221
+ async function await_rerun_registered(
222
+ branch_name: string,
223
+ jobs: ReadonlyArray<Job>,
224
+ ): Promise<void> {
225
+ const rerun_ids = new Set(jobs.map((job) => job.id))
226
+
227
+ await poll.poll_until(
228
+ async () => {
229
+ const failed = await read_failed_check_ids(branch_name)
230
+
231
+ return failed.every((id) => !rerun_ids.has(id))
232
+ },
233
+ { attempts: REGISTER_ATTEMPTS, interval_ms: REGISTER_INTERVAL_MS },
234
+ )
235
+ }
236
+
237
+ // Every run must qualify: rerunning some would still leave the pull request red on the others. The
238
+ // change is read once, and only when a run failed.
239
+ async function find_rerun_jobs(branch_name: string): Promise<Array<Judged>> {
240
+ const run_ids = await read_failed_run_ids(branch_name)
241
+
242
+ if (run_ids.length === 0) return []
243
+
244
+ const changed = new Set(await changed_paths.read_changed_paths(false))
245
+ const per_run = await Promise.all(
246
+ run_ids.map(async (run_id) => await read_rerun_jobs(run_id, changed)),
247
+ )
248
+
249
+ return per_run.every((jobs) => jobs !== undefined) ? per_run.flat() : []
250
+ }
251
+
252
+ async function rerun(branch_name: string, entries: ReadonlyArray<Judged>): Promise<Array<string>> {
253
+ const jobs = entries.map(({ job }) => job)
254
+ const names = jobs.map((job) => job.name)
255
+
256
+ console.info(
257
+ `🔁 Rerunning CI jobs that stopped after every step passed or failed only outside the change: ${names.join(', ')}`,
258
+ )
259
+ await followup_failed_tests.record(
260
+ entries.flatMap(({ job, tests }) => (tests === undefined ? [] : { job: job.name, tests })),
261
+ git_branch.issue_from_branch(branch_name),
262
+ )
263
+ await rerun_runs([...new Set(jobs.map((job) => job.run_id))])
264
+ await await_rerun_registered(branch_name, jobs)
265
+
266
+ return names
267
+ }
268
+
269
+ // A failed test main has since changed is merged before any rerun (`followup-main-move.ts`).
270
+ async function should_merge_first(
271
+ branch_name: string,
272
+ entries: ReadonlyArray<Judged>,
273
+ ): Promise<boolean> {
274
+ const files = [...new Set(entries.flatMap(({ tests }) => tests?.files ?? []))]
275
+
276
+ return await followup_main_move.should_merge_first(
277
+ files,
278
+ git_branch.issue_from_branch(branch_name),
279
+ )
280
+ }
281
+
282
+ // **Returns the names of the jobs it reran**, empty when there was none to rerun or main has to be
283
+ // merged first. A read or a rerun that fails is reported and answers empty: the rerun is a recovery,
284
+ // so the original failure stands.
285
+ async function rerun_failed_jobs(branch_name: string): Promise<Array<string>> {
286
+ try {
287
+ const entries = await find_rerun_jobs(branch_name)
288
+
289
+ if (entries.length === 0 || (await should_merge_first(branch_name, entries))) return []
290
+
291
+ return await rerun(branch_name, entries)
292
+ } catch (error) {
293
+ console.info(`⚠️ CI job rerun failed: ${git_gh_helpers.get_error_message_with_stderr(error)}`)
294
+
295
+ return []
296
+ }
297
+ }
298
+
299
+ const followup_job_rerun = {
300
+ rerun_failed_jobs,
301
+ }
302
+
303
+ export { followup_job_rerun, COMPLETE_ATTEMPTS, REGISTER_ATTEMPTS }