@joshuafolkken/kit 1.1784.0 → 1.2116.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 +60 -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 +386 -360
  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 +16 -9
  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,7 +1,10 @@
1
+ import { error_text } from '#scripts/lib/error-message'
1
2
  import { FULL_PAGE_QUERY, git_gh_api_path } from './git-gh-api-path'
3
+ import { note_unreadable_comments } from './git-gh-comments-unreadable'
2
4
  import { git_gh_exec } from './git-gh-exec'
3
5
  import { git_gh_helpers } from './git-gh-helpers'
4
6
  import { git_gh_pr_rest, type RestPull } from './git-gh-pr-rest'
7
+ import { git_gh_pr_too_large } from './git-gh-pr-too-large'
5
8
  import { to_gh_state } from './git-gh-rest-state'
6
9
 
7
10
  // Reading pull requests through REST, in the answers `gh pr view` gives.
@@ -61,10 +64,16 @@ function lookup_path(branch_name: string): string {
61
64
  return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`
62
65
  }
63
66
 
64
- async function fetch_pr_number(branch_name: string): Promise<number | undefined> {
67
+ async function fetch_branch_pull(branch_name: string): Promise<RestPull | undefined> {
65
68
  const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) })
66
69
 
67
- return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number
70
+ return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))
71
+ }
72
+
73
+ async function fetch_pr_number(branch_name: string): Promise<number | undefined> {
74
+ const pull = await fetch_branch_pull(branch_name)
75
+
76
+ return pull?.number
68
77
  }
69
78
 
70
79
  // Why the resolution produced no number. `missing` is the lookup answering: the listing came back and
@@ -80,7 +89,7 @@ interface PullMergeState {
80
89
  is_merged: boolean
81
90
  merged_at: string | undefined
82
91
  head_sha: string | undefined
83
- // `josh ship` reads `DIRTY` here to re-merge a conflicting pull request.
92
+ // GitHub's merge state (`DIRTY` on a conflict).
84
93
  merge_state_status: string | undefined
85
94
  }
86
95
 
@@ -100,8 +109,8 @@ const pending_pr_number_by_branch = new Map<string, Promise<PullNumberRead>>()
100
109
  // same `GET /pulls/{N}` each time. The promise is stored, so readers in one tick share the request as
101
110
  // well as its answer; a read that fails is dropped and re-tried, the rule the number memo holds.
102
111
  // **Every write to a pull request clears it** through `forget_pr_numbers`, and the two state readers
103
- // never read it: `pr_get_merge_state` answers where the pull request stands *now*, which `josh ship`
104
- // asks again after every merge it pushes, and `pr_view` decides whether `josh git -y` writes onto the
112
+ // never read it: `pr_get_merge_state` answers where the pull request stands *now*, which `josh followup`
113
+ // asks again after every merge `josh ship` pushes, and `pr_view` decides whether `josh git -y` writes onto the
105
114
  // pull request — after a commit and a push its preflight read would otherwise outlast.
106
115
  const pull_by_number = new Map<number, Promise<RestPull>>()
107
116
 
@@ -168,9 +177,12 @@ async function resolve_pr_number(branch_name: string): Promise<number | undefine
168
177
  }
169
178
 
170
179
  // One definition, because both callers that refuse to fold an unreadable lookup raise it —
171
- // `require_pr_number` and `pr_exists`.
180
+ // `require_pr_number` and `pr_exists`. The cause's first line rides in the message: a reader that
181
+ // prints only the message — the `josh git -y` preflight, a `josh ship` log — still says why.
172
182
  function to_unreadable_error(branch_name: string, cause: unknown): Error {
173
- return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause })
183
+ const [gist = ''] = error_text.message_of(cause).split('\n', 1)
184
+
185
+ return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name} (${gist})`, { cause })
174
186
  }
175
187
 
176
188
  // The number for a caller that has nothing to fold an absence into. The reads above answer their own
@@ -213,6 +225,36 @@ async function read_remembered_pull(pr_number: number): Promise<RestPull> {
213
225
  }
214
226
  }
215
227
 
228
+ // The listing row for a detail read GitHub refused as too large (`git-gh-pr-too-large.ts`). The row
229
+ // carries neither `mergeable` nor `mergeable_state`, which every reader of them already takes as
230
+ // "not known yet". Any other failure — and a listing that no longer names the number — stays the
231
+ // detail read's own `unreadable`.
232
+ async function read_listed_pull(
233
+ detail_error: unknown,
234
+ branch_name: string,
235
+ pr_number: number,
236
+ ): Promise<RestPull | undefined> {
237
+ if (!git_gh_pr_too_large.is_too_many_files(detail_error)) return undefined
238
+ const pull = await fetch_branch_pull(branch_name)
239
+
240
+ return pull?.number === pr_number ? pull : undefined
241
+ }
242
+
243
+ async function read_pull_or_listed(
244
+ branch_name: string,
245
+ pr_number: number,
246
+ read: (pr_number: number) => Promise<RestPull>,
247
+ ): Promise<RestPull> {
248
+ try {
249
+ return await read(pr_number)
250
+ } catch (error) {
251
+ const listed = await read_listed_pull(error, branch_name, pr_number)
252
+ if (listed === undefined) throw error
253
+
254
+ return listed
255
+ }
256
+ }
257
+
216
258
  // One pull request named by its branch: the resolution, then the detail read that carries
217
259
  // `mergeable` and `mergeable_state`, which the lookup listing does not.
218
260
  async function read_pull_of_branch(
@@ -223,7 +265,10 @@ async function read_pull_of_branch(
223
265
  if (number_read.kind !== 'read') return number_read
224
266
 
225
267
  try {
226
- return { kind: 'read', pull: await read(number_read.pr_number) }
268
+ return {
269
+ kind: 'read',
270
+ pull: await read_pull_or_listed(branch_name, number_read.pr_number, read),
271
+ }
227
272
  } catch (error) {
228
273
  return { kind: 'unreadable', cause: error }
229
274
  }
@@ -301,25 +346,39 @@ async function pr_view(branch_name: string): Promise<string> {
301
346
  return JSON.stringify(git_gh_pr_rest.to_pr_info(read.pull))
302
347
  }
303
348
 
349
+ // gh's own reason for a lookup that never answered, or that the branch has no pull request at all.
350
+ function unresolved_reason(read: Exclude<PullNumberRead, { kind: 'read' }>): string {
351
+ return read.kind === 'unreadable' ? error_text.message_of(read.cause) : NO_PULL_REQUEST_MESSAGE
352
+ }
353
+
304
354
  // `undefined` when the listing could not be read — a failed request, or a PR whose number could not
305
355
  // be resolved. Not `'[]'`.
306
356
  //
307
357
  // The two are the same string to a caller, and the callers are the merge gate: a rate limit would
308
358
  // arrive as "no reviewer left a finding" and the PR merge with the gate never actually read.
309
359
  // The direction is what makes it worse than the epic auto-close's version of
310
- // the same misread — that one only failed to close something.
360
+ // the same misread — that one only failed to close something. Either failure warns its reason
361
+ // (`git-gh-comments-unreadable.ts`).
311
362
  async function read_comments(
312
363
  branch_name: string,
313
364
  to_path: (pr_number: string) => string,
314
365
  to_json: (raw: string) => string,
315
366
  ): Promise<string | undefined> {
316
- const pr_number = await resolve_pr_number(branch_name)
317
- if (pr_number === undefined) return undefined
318
- const path = `${to_path(String(pr_number))}${COMMENTS_QUERY}`
367
+ const read = await read_pr_number(branch_name)
368
+
369
+ if (read.kind !== 'read') {
370
+ note_unreadable_comments(branch_name, unresolved_reason(read))
371
+
372
+ return undefined
373
+ }
374
+
375
+ const path = `${to_path(String(read.pr_number))}${COMMENTS_QUERY}`
319
376
 
320
377
  try {
321
378
  return to_json(await git_gh_exec.exec_gh_api({ path, should_paginate: true }))
322
- } catch {
379
+ } catch (error) {
380
+ note_unreadable_comments(branch_name, error_text.message_of(error))
381
+
323
382
  return undefined
324
383
  }
325
384
  }
@@ -362,7 +421,9 @@ export type { PullMergeState }
362
421
  export {
363
422
  git_gh_pr_read,
364
423
  forget_pr_numbers,
424
+ read_listed_pull,
365
425
  read_pull,
426
+ read_pull_or_listed,
366
427
  require_pr_number,
367
428
  resolve_pr_number,
368
429
  NO_PULL_REQUEST_MESSAGE,
@@ -145,7 +145,8 @@ function to_pr_info(pull: RestPull): Record<string, unknown> {
145
145
 
146
146
  // The conversation comments in the shape `gh pr view --json comments` answered with. A listing that
147
147
  // will not parse throws rather than answering an empty one — `git-pr-ai-review.ts` reads an empty
148
- // listing as "no reviewer left a finding" and merges.
148
+ // listing as "no reviewer left a finding" and merges. `updatedAt` is when CodeRabbit last rewrote its
149
+ // rate-limit notice, the moment its stated window counts from (joshuafolkken/kit#5289).
149
150
  function to_pr_comments(rest_json: string): Array<Record<string, unknown>> {
150
151
  const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema)
151
152
  if (parsed === undefined) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE)
@@ -154,6 +155,7 @@ function to_pr_comments(rest_json: string): Array<Record<string, unknown>> {
154
155
  body: comment.body ?? EMPTY_BODY,
155
156
  url: comment.html_url,
156
157
  author: { login: comment.user?.login },
158
+ updatedAt: comment.updated_at,
157
159
  }))
158
160
  }
159
161
 
@@ -2,13 +2,15 @@ import { FULL_PAGE_QUERY, git_gh_api_path } from './git-gh-api-path'
2
2
  import { git_gh_branch_rules } from './git-gh-branch-rules'
3
3
  import { git_gh_exec } from './git-gh-exec'
4
4
  import { git_gh_poll_read } from './git-gh-poll-read'
5
- import { require_pr_number } from './git-gh-pr-read'
6
- import { git_gh_pr_rest, type RestPull } from './git-gh-pr-rest'
5
+ import { git_gh_pr_graphql_state } from './git-gh-pr-graphql-state'
6
+ import { read_listed_pull, read_pull, require_pr_number } from './git-gh-pr-read'
7
+ import type { RestPull } from './git-gh-pr-rest'
7
8
  import { git_gh_pr_review } from './git-gh-pr-review'
8
9
  import { git_gh_pr_rollup } from './git-gh-pr-rollup'
9
10
  import { to_gh_state } from './git-gh-rest-state'
10
11
 
11
- // The merge gate's pull request state snapshot, rebuilt from REST.
12
+ // The merge gate's pull request state snapshot, rebuilt from REST — GraphQL only for the merge state
13
+ // of a pull request whose REST detail GitHub refuses as too large (`resolve_pull`).
12
14
  //
13
15
  // `gh pr view --json mergeStateStatus,reviewDecision,statusCheckRollup` is answered 403 in a cloud
14
16
  // session, and all three fields are GraphQL-only: one has no REST
@@ -93,13 +95,26 @@ interface ResolvedPull {
93
95
  // **The detail is read without an ETag.** GitHub computes `mergeable_state` lazily and can change it
94
96
  // under an unchanged ETag, so a 304 answered from the poll cache kept the gate reading a stale
95
97
  // `blocked` on a pull request that had turned `clean` (joshuafolkken/kit#4589).
98
+ //
99
+ // **A detail GitHub refuses as too many files changed** is stood in for by the listing row, whose
100
+ // missing `mergeable_state` is read from GraphQL (`git-gh-pr-graphql-state.ts`) — or the snapshot
101
+ // throws with the reason, since an unknown state would hold the gate pending until its budget ran out.
102
+ async function read_detail_or_listed(branch_name: string, pr_number: number): Promise<RestPull> {
103
+ try {
104
+ return await read_pull(pr_number)
105
+ } catch (error) {
106
+ const listed = await read_listed_pull(error, branch_name, pr_number)
107
+ if (listed === undefined) throw error
108
+ const state = await git_gh_pr_graphql_state.read_merge_state_status(listed, error)
109
+
110
+ return { ...listed, mergeable_state: state }
111
+ }
112
+ }
113
+
96
114
  async function resolve_pull(branch_name: string): Promise<ResolvedPull> {
97
115
  const pr_number = await require_pr_number(branch_name)
98
- const json = await git_gh_exec.exec_gh_api({
99
- path: git_gh_api_path.pull_api_path(String(pr_number)),
100
- })
101
116
 
102
- return { pr_number, pull: git_gh_pr_rest.parse_rest_pull(json) }
117
+ return { pr_number, pull: await read_detail_or_listed(branch_name, pr_number) }
103
118
  }
104
119
 
105
120
  // Everything the snapshot carries except the review decision: three requests rather than four.
@@ -0,0 +1,20 @@
1
+ import { error_text } from '#scripts/lib/error-message'
2
+ import { gh_failure } from './git-gh-failure'
3
+
4
+ // GitHub refuses the single-pull JSON (`GET /pulls/{N}`) of some pull requests with
5
+ // `HTTP 422: … too many files changed`, every time, however few files the pull request changes — while
6
+ // the listing, the files, the diff and GraphQL all answer for the same one (joshuafolkken/kit#5226).
7
+ // The refusal is about that one response, so the branch-keyed reads fall back to the listing row
8
+ // rather than reporting the pull request unreadable.
9
+ const UNPROCESSABLE_STATUS = 422
10
+ const TOO_MANY_FILES_TEXT = 'too many files changed'
11
+
12
+ function is_too_many_files(error: unknown): boolean {
13
+ if (gh_failure.failure_of(error)?.status !== UNPROCESSABLE_STATUS) return false
14
+
15
+ return error_text.message_of(error).includes(TOO_MANY_FILES_TEXT)
16
+ }
17
+
18
+ const git_gh_pr_too_large = { is_too_many_files }
19
+
20
+ export { git_gh_pr_too_large }
@@ -6,7 +6,13 @@ import { git_gh_exec } from './git-gh-exec'
6
6
  import { gh_failure } from './git-gh-failure'
7
7
  import { git_gh_helpers } from './git-gh-helpers'
8
8
  import { git_gh_issue_write } from './git-gh-issue-write'
9
- import { forget_pr_numbers, git_gh_pr_read, read_pull, require_pr_number } from './git-gh-pr-read'
9
+ import {
10
+ forget_pr_numbers,
11
+ git_gh_pr_read,
12
+ read_pull,
13
+ read_pull_or_listed,
14
+ require_pr_number,
15
+ } from './git-gh-pr-read'
10
16
  import { git_gh_pr_rest } from './git-gh-pr-rest'
11
17
  import { git_gh_pr_snapshot } from './git-gh-pr-snapshot'
12
18
 
@@ -188,10 +194,14 @@ function is_head_moved(error: unknown): boolean {
188
194
  }
189
195
 
190
196
  // `undefined` is "nobody read it", kept apart from the `false` that means "the pull request is not
191
- // merged". Folding the two would report an unread state as a settled answer.
192
- async function read_merge_state(pr_number: number): Promise<boolean | undefined> {
197
+ // merged". Folding the two would report an unread state as a settled answer. The listing row, which
198
+ // carries `merged_at` too, stands in for a detail GitHub refuses as too many files changed.
199
+ async function read_merge_state(
200
+ branch_name: string,
201
+ pr_number: number,
202
+ ): Promise<boolean | undefined> {
193
203
  try {
194
- return git_gh_pr_rest.is_merged(await read_pull(pr_number))
204
+ return git_gh_pr_rest.is_merged(await read_pull_or_listed(branch_name, pr_number, read_pull))
195
205
  } catch (error) {
196
206
  // The reason goes in the line, for the same reason the poll loop's warning carries one: the
197
207
  // failure this throws below names the *merge* request as its cause, so without this an operator
@@ -220,10 +230,14 @@ async function sleep_between_read_backs(attempt: number): Promise<void> {
220
230
  // **A read that failed is still not an answer.** Out of attempts it throws rather than reporting
221
231
  // `false`, carrying the merge failure as its `cause`, so no run ever concludes "the merge did not
222
232
  // happen" from a read nobody got.
223
- async function has_merge_landed(pr_number: number, merge_error: unknown): Promise<boolean> {
233
+ async function has_merge_landed(
234
+ branch_name: string,
235
+ pr_number: number,
236
+ merge_error: unknown,
237
+ ): Promise<boolean> {
224
238
  for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
225
239
  // eslint-disable-next-line no-await-in-loop -- a retry runs only after the previous attempt has answered
226
- const is_merged = await read_merge_state(pr_number)
240
+ const is_merged = await read_merge_state(branch_name, pr_number)
227
241
  if (is_merged !== undefined) return is_merged
228
242
 
229
243
  // eslint-disable-next-line no-await-in-loop -- a retry runs only after the previous attempt has answered
@@ -257,7 +271,7 @@ async function pr_merge(branch_name: string, head_sha: string): Promise<void> {
257
271
  try {
258
272
  await put_merge(pr_number, head_sha)
259
273
  } catch (error) {
260
- if (!(await has_merge_landed(pr_number, error))) throw error
274
+ if (!(await has_merge_landed(branch_name, pr_number, error))) throw error
261
275
  }
262
276
  }
263
277
 
@@ -0,0 +1,141 @@
1
+ import { setTimeout as delay } from 'node:timers/promises'
2
+ import { get_exit_code, has_timed_out } from '#scripts/git/git-execa-error'
3
+ import { error_text } from '#scripts/lib/error-message'
4
+ import { gh_failure } from './git-gh-failure'
5
+
6
+ // One REST request asked again, a bounded number of times, when GitHub failed it transiently
7
+ // (joshuafolkken/kit#4717). A single 502 on `review:record --comment` used to stop a whole ship stage
8
+ // and cost a relaunched session that changed nothing; the same request seconds later succeeds.
9
+ //
10
+ // **Transient is a 5xx or a request that got no answer at all** — a dropped connection writes no
11
+ // error document, so its status is `undefined`. A 4xx is the request's own fault and answers the same
12
+ // the next time. Three failures without a status are still not retried: a timeout (it has already
13
+ // spent a minute, and a write it killed may have landed — `GH_REQUEST_TIMEOUT_MS`'s note), an error
14
+ // `to_gh_error` never built (gh missing, a refused body), which no wait can change, and one that wrote
15
+ // a body anyway — a GraphQL `errors` document is served with a 200, so it names no status and is
16
+ // still GitHub's answer.
17
+ //
18
+ // **A retried write can land twice** when the first attempt reached GitHub before it failed. The
19
+ // writes on this path tolerate it: a duplicate pull request is refused with the 422 the creator reads
20
+ // (`handle_pr_create_error`), a repeated merge is read back by `has_merge_landed`, and at worst a
21
+ // review-round comment is posted twice — cheaper than the stopped stage it replaces. Issue creation
22
+ // has no such guard — a second issue opens and nothing reads the first back — so
23
+ // `issue_create_request` opts out with `should_retry: false`.
24
+
25
+ const FIRST_RETRY_DELAY_MS = 2000
26
+ const SECOND_RETRY_DELAY_MS = 5000
27
+ const RETRY_DELAYS_MS = [FIRST_RETRY_DELAY_MS, SECOND_RETRY_DELAY_MS]
28
+ const MAX_ATTEMPTS = RETRY_DELAYS_MS.length + 1
29
+ const SERVER_ERROR_FLOOR = 500
30
+ // gh's own exit code for "authentication required" (`gh auth login`, no `GH_TOKEN`). It writes no
31
+ // error document either, so without this it would read as a request that got no answer — and no
32
+ // wait logs anyone in.
33
+ const GH_AUTH_REQUIRED_EXIT_CODE = 4
34
+
35
+ const SPENT_NOTE = 'gh: request failed transiently on every attempt'
36
+
37
+ // gh's own summary line for a failed request, e.g. `gh: Server Error (HTTP 502)`. It is the only
38
+ // place the status is written when the body is an HTML error page rather than GitHub's JSON document.
39
+ const SUMMARY_STATUS_PATTERN = /\(HTTP (?<status>\d{3})\)/u
40
+
41
+ interface RetryHooks {
42
+ wait: (milliseconds: number) => Promise<void>
43
+ note: (text: string) => void
44
+ }
45
+
46
+ const DEFAULT_HOOKS: RetryHooks = {
47
+ wait: async (milliseconds) => {
48
+ await delay(milliseconds)
49
+ },
50
+ note: (text) => {
51
+ process.stderr.write(`${text}\n`)
52
+ },
53
+ }
54
+
55
+ function summary_status(error: Error): number | undefined {
56
+ const { status } = SUMMARY_STATUS_PATTERN.exec(error.message)?.groups ?? {}
57
+
58
+ return status === undefined ? undefined : Number(status)
59
+ }
60
+
61
+ // Whether the failed spawn wrote a response body: a request that got no answer at all wrote none.
62
+ function has_answer(spawn_error: unknown): boolean {
63
+ if (!(spawn_error instanceof Error) || !('stdout' in spawn_error)) return false
64
+
65
+ return typeof spawn_error.stdout === 'string' && spawn_error.stdout.trim().length > 0
66
+ }
67
+
68
+ function is_transient_status(status: number | undefined, spawn_error: unknown): boolean {
69
+ if (status === undefined) return !has_answer(spawn_error)
70
+
71
+ return status >= SERVER_ERROR_FLOOR
72
+ }
73
+
74
+ // A spawn no wait can change: one it killed on its budget, or one gh refused before asking.
75
+ function is_final_spawn(spawn_error: unknown): boolean {
76
+ return has_timed_out(spawn_error) || get_exit_code(spawn_error) === GH_AUTH_REQUIRED_EXIT_CODE
77
+ }
78
+
79
+ function is_transient(error: unknown): boolean {
80
+ const failure = gh_failure.failure_of(error)
81
+
82
+ if (failure === undefined || !(error instanceof Error)) return false
83
+
84
+ const status = failure.status ?? summary_status(error)
85
+
86
+ return !is_final_spawn(error.cause) && is_transient_status(status, error.cause)
87
+ }
88
+
89
+ function first_line(error: unknown): string {
90
+ return error_text.message_of(error).split('\n', 1)[0] ?? ''
91
+ }
92
+
93
+ function retry_note(error: unknown, attempt: number, wait_ms: number): string {
94
+ const ordinal = `${String(attempt)}/${String(MAX_ATTEMPTS)}`
95
+
96
+ return `gh: request failed transiently (attempt ${ordinal}: ${first_line(error)}) — retrying in ${String(wait_ms)} ms`
97
+ }
98
+
99
+ // The wait before asking again, or `undefined` when this failure is final — not transient, or the
100
+ // last attempt spent.
101
+ function retry_delay(error: unknown, attempt: number): number | undefined {
102
+ return is_transient(error) ? RETRY_DELAYS_MS[attempt - 1] : undefined
103
+ }
104
+
105
+ // The line a request transient to its last attempt leaves on stderr, naming the failure by the first
106
+ // line of its message: a stopped `josh ship` stage that ended on that failure reads it as the
107
+ // infrastructure (joshuafolkken/kit#5052). A recovered retry writes none.
108
+ function spent_note(message: string): string {
109
+ return `${SPENT_NOTE} (${message})`
110
+ }
111
+
112
+ function note_spent(error: unknown, attempt: number, hooks: RetryHooks): void {
113
+ if (attempt === MAX_ATTEMPTS && is_transient(error)) hooks.note(spent_note(first_line(error)))
114
+ }
115
+
116
+ async function with_retry<T>(
117
+ request: () => Promise<T>,
118
+ hooks: RetryHooks = DEFAULT_HOOKS,
119
+ attempt = 1,
120
+ ): Promise<T> {
121
+ try {
122
+ return await request()
123
+ } catch (error) {
124
+ const wait_ms = retry_delay(error, attempt)
125
+
126
+ if (wait_ms === undefined) {
127
+ note_spent(error, attempt, hooks)
128
+ throw error
129
+ }
130
+
131
+ hooks.note(retry_note(error, attempt, wait_ms))
132
+ await hooks.wait(wait_ms)
133
+
134
+ return await with_retry(request, hooks, attempt + 1)
135
+ }
136
+ }
137
+
138
+ const gh_retry = { MAX_ATTEMPTS, RETRY_DELAYS_MS, is_transient, spent_note, with_retry }
139
+
140
+ export type { RetryHooks }
141
+ export { gh_retry }
@@ -7,6 +7,7 @@ import {
7
7
  type ReviewComment,
8
8
  } from './git-ai-review-scan'
9
9
  import { git_gh_command } from './git-gh-command'
10
+ import { is_retry_pending, note_unreadable_stop } from './git-gh-comments-unreadable'
10
11
  import { git_pr_confirmation, has_ignore_reason, type TelegramContext } from './git-pr-confirmation'
11
12
 
12
13
  interface AiReviewPullComment {
@@ -132,6 +133,19 @@ const UNREADABLE_COMMENTS_BODY =
132
133
  // never read the gate reports exactly like one that read it and found nothing.
133
134
  const UNREADABLE_COMMENTS_NOTE = 'AI reviewer comments could not be read; the scan was bypassed.'
134
135
 
136
+ // An attempt the ship will run again sends no confirmation; the final one does (is_retry_pending).
137
+ async function confirm_unreadable_stop(context: TelegramContext): Promise<void> {
138
+ note_unreadable_stop()
139
+ if (is_retry_pending()) return
140
+
141
+ await git_pr_confirmation.notify_confirmation({
142
+ context,
143
+ body: UNREADABLE_COMMENTS_BODY,
144
+ recommended: 'Re-run `pnpm josh followup` once the comments can be read',
145
+ alternatives: [IGNORE_ALTERNATIVE],
146
+ })
147
+ }
148
+
135
149
  async function handle_unreadable_comments(input: {
136
150
  branch_name: string
137
151
  ignore_reason: string | undefined
@@ -146,12 +160,7 @@ async function handle_unreadable_comments(input: {
146
160
  return [UNREADABLE_COMMENTS_NOTE]
147
161
  }
148
162
 
149
- await git_pr_confirmation.notify_confirmation({
150
- context: input.context,
151
- body: UNREADABLE_COMMENTS_BODY,
152
- recommended: 'Re-run `pnpm josh followup` once the comments can be read',
153
- alternatives: [IGNORE_ALTERNATIVE],
154
- })
163
+ await confirm_unreadable_stop(input.context)
155
164
 
156
165
  throw new Error(UNREADABLE_COMMENTS_BODY)
157
166
  }
@@ -287,6 +287,9 @@ class PrConflictError extends Error {
287
287
  }
288
288
 
289
289
  export {
290
+ FAILURE_MESSAGE_PREFIX,
291
+ FAILURE_REASON_CHECKS,
292
+ FAILURE_REASON_CONFLICT,
290
293
  PrConflictError,
291
294
  evaluate_pr_state,
292
295
  is_auto_merge_blocked,
@@ -48,10 +48,10 @@ function get_configured_max_attempts(): number {
48
48
  const CHECK_MAX_ATTEMPTS = get_configured_max_attempts()
49
49
  const DEFAULT_STABLE_READS = 2
50
50
 
51
- // One authoritative gate poll is enough once `pr_checks_watch` has already seen every check finish:
52
- // the watch is the first confirmation, so the poll that follows need only agree once. The two reads
53
- // exist for the pending→settled window a bare poll opens on, and the watch has itself already spanned
54
- // that window — so waiting a second interval after it merely re-proves what the watch established.
51
+ // One authoritative gate poll is enough once `pr_checks_watch` has returned — every check finished, or
52
+ // its budget ran out on a pending read. The two reads exist for the pending→settled window a bare poll
53
+ // opens on, and a returned watch has either spanned that window or seen its pending side, which the
54
+ // poll's first settled read then closes — so a second interval merely re-proves it (#5080).
55
55
  const WATCH_CONFIRMED_STABLE_READS = 1
56
56
 
57
57
  // **A read that failed is not a verdict.** The loop reads every ten seconds, so a dropped connection,
@@ -314,7 +314,7 @@ async function default_fetch_pr_state(
314
314
  }
315
315
 
316
316
  // **`required_stable_reads` is a parameter, not a constant, so the caller can lower it** — `run_checks`
317
- // passes `WATCH_CONFIRMED_STABLE_READS` when the watch already confirmed completion. It defaults to
317
+ // passes `WATCH_CONFIRMED_STABLE_READS` when the watch returned. It defaults to
318
318
  // `DEFAULT_STABLE_READS`, so every caller that names only the branch is exactly where it was.
319
319
  // What a caller is shown of each poll it waits on — `followup` reports the checks to the run's event
320
320
  // stream from it (joshuafolkken/kit#3770). It decides nothing: the wait reads the same snapshot.
@@ -1,3 +1,4 @@
1
+ import { rmSync } from 'node:fs'
1
2
  import { copyFile, mkdir, mkdtemp, rm, symlink } from 'node:fs/promises'
2
3
  import { tmpdir } from 'node:os'
3
4
  import path from 'node:path'
@@ -13,6 +14,12 @@ import { git_worktree } from './git-worktree'
13
14
 
14
15
  const TREE_NAME = 'tree'
15
16
  const NODE_MODULES = 'node_modules'
17
+ const TERMINATE_SIGNAL = 'SIGTERM'
18
+ // 128 + SIGTERM's 15: the code the process would have exited with had no listener caught the signal.
19
+ const TERMINATED_EXIT_CODE = 143
20
+
21
+ // The temp directories of the trees this process holds now.
22
+ const live_parents = new Set<string>()
16
23
 
17
24
  interface BaseTree {
18
25
  // The temp directory holding the tree — a file written here sits beside the tree, never in it.
@@ -43,6 +50,26 @@ async function remove(base_tree: BaseTree): Promise<void> {
43
50
  }
44
51
  }
45
52
 
53
+ // **A terminated run removes its trees before it exits** (joshuafolkken/kit#4794): `josh run:review
54
+ // --join` stops a `test:pinned` that fell behind, and a `finally` never runs past a signal, so each
55
+ // tree would stay in the temp directory and in `git worktree list`. The removal is synchronous because
56
+ // the process exits right after it; the next `with_tree`'s prune drops the registrations.
57
+ function remove_live_trees(): void {
58
+ for (const parent of live_parents) rmSync(parent, { force: true, recursive: true })
59
+
60
+ process.exit(TERMINATED_EXIT_CODE)
61
+ }
62
+
63
+ function track(parent: string): void {
64
+ if (live_parents.size === 0) process.on(TERMINATE_SIGNAL, remove_live_trees)
65
+ live_parents.add(parent)
66
+ }
67
+
68
+ function untrack(parent: string): void {
69
+ live_parents.delete(parent)
70
+ if (live_parents.size === 0) process.off(TERMINATE_SIGNAL, remove_live_trees)
71
+ }
72
+
46
73
  async function with_tree<T>(
47
74
  prefix: string,
48
75
  commit: string,
@@ -51,12 +78,15 @@ async function with_tree<T>(
51
78
  const parent = await mkdtemp(path.join(tmpdir(), prefix))
52
79
  const base_tree = { parent, tree: path.join(parent, TREE_NAME) }
53
80
 
81
+ track(parent)
82
+
54
83
  try {
55
84
  await prepare(base_tree.tree, commit)
56
85
 
57
86
  return await use(base_tree)
58
87
  } finally {
59
88
  await remove(base_tree)
89
+ untrack(parent)
60
90
  }
61
91
  }
62
92
 
@@ -1,4 +1,5 @@
1
1
  import { git_command } from './git-command'
2
+ import { git_diff_reads } from './git-diff-reads'
2
3
  import { git_spawn } from './git-spawn'
3
4
 
4
5
  // **The change base as a commit, or nothing — never a ref name**.
@@ -22,29 +23,22 @@ async function resolved(): Promise<string | undefined> {
22
23
  }
23
24
  }
24
25
 
25
- const MERGE_BASE_COMMAND = 'merge-base'
26
- const MERGE_HEAD = 'MERGE_HEAD'
27
- const LINE_BREAK = '\n'
28
-
29
26
  // Where the side a merge in progress brings in left the default branch — `undefined` where no merge
30
27
  // is in progress, which is every ordinary run.
31
28
  async function incoming_base(): Promise<string | undefined> {
32
29
  try {
33
30
  const reference = await git_command.default_branch_reference()
34
31
 
35
- return await git_spawn.read([MERGE_BASE_COMMAND, reference, MERGE_HEAD])
32
+ return await git_spawn.read([...git_diff_reads.incoming_base_arguments(reference)])
36
33
  } catch {
37
34
  return undefined
38
35
  }
39
36
  }
40
37
 
41
- // `--independent` drops every commit another one listed reaches, so of two commits on the default
42
- // branch's history it keeps the later.
43
38
  async function later_of(base: string, incoming: string): Promise<string> {
44
- const kept = await git_spawn.read([MERGE_BASE_COMMAND, '--independent', base, incoming])
45
- const [later = base] = kept.split(LINE_BREAK)
39
+ const kept = await git_spawn.read([...git_diff_reads.later_base_arguments(base, incoming)])
46
40
 
47
- return later
41
+ return git_diff_reads.later_base_of(kept, base)
48
42
  }
49
43
 
50
44
  // **The base of the tree, not of `HEAD`, while a merge is in progress.** A merge of the default