@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,22 +1,31 @@
1
1
  import { git_gh_pr_read } from '#scripts/gh/git-gh-pr-read'
2
- import { is_merge_conflict } from '#scripts/gh/git-pr-checks-eval'
2
+ import {
3
+ FAILURE_MESSAGE_PREFIX,
4
+ FAILURE_REASON_CHECKS,
5
+ FAILURE_REASON_CONFLICT,
6
+ } from '#scripts/gh/git-pr-checks-eval'
3
7
  import { git_command } from '#scripts/git/git-command'
4
8
  import { main_merge, type MergeOutcome } from '#scripts/git/main-merge'
9
+ import { pre_commit_lint_switch } from '#scripts/hooks/pre-commit-lint-switch'
10
+ import { pre_push_related_switch } from '#scripts/hooks/pre-push-related-switch'
5
11
  import { josh_command, type JoshResult } from '#scripts/josh/josh-run'
6
12
  import { error_text } from '#scripts/lib/error-message'
13
+ import { run_ship_lap } from './run-ship-lap'
7
14
  import { run_ship_overlap } from './run-ship-overlap'
8
15
  import { run_ship_scoped } from './run-ship-scoped'
9
- import type { Cause } from './run-ship-stop-cause'
16
+ import { run_ship_stop_cause, type Cause } from './run-ship-stop-cause'
10
17
 
11
18
  // `josh ship` keeps the branch current with the default branch itself: a default branch that moves
12
19
  // while the pull request opens and CI runs would otherwise stop the followup as `interrupted`, though
13
20
  // merging the default branch again usually resolves it.
14
21
  //
15
- // So the merge happens mechanically: once before the commit (`sync_stage`), and once when
16
- // the followup fails on a pull request GitHub reports `DIRTY` (`followup_stage`). A conflict only on
17
- // generated files is regenerated by the merge itself (`main-merge-regenerate.ts`); any other conflict
22
+ // So the merge happens mechanically: before the gate (`sync_stage`), again before the push when the
23
+ // default branch moved since (`catch_up`), and when the followup fails after it moved again
24
+ // (`followup_stage`). A conflict only on
25
+ // the files `main-merge-regenerate.ts` lists is resolved by the merge itself; any other conflict
18
26
  // stops the ship as `conflict`, naming the unmerged paths so the resumed session goes straight to them.
19
27
 
28
+ const { STEP, timed } = run_ship_lap
20
29
  const should_forward_stderr = true
21
30
  const SUCCESS_EXIT_CODE = 0
22
31
  const FAILURE_EXIT_CODE = 1
@@ -27,19 +36,32 @@ const CONFLICT_NOTE = 'merging the default branch left unmerged paths:'
27
36
  const DEFERRED_PREFIX = 'merge deferred to the followup: '
28
37
  const CURRENT_SUFFIX = ' brings nothing in'
29
38
  const MERGED_PREFIX = 'merged '
39
+ const MERGED_GATE_NOTE =
40
+ 'the default branch moved since the sync; the tree merged with it fails the gate:'
41
+ const PUSHED_NOTE = 'pushed the merge ahead of the gate, so CI runs beside it'
42
+ const PUSH_FAILED_NOTE =
43
+ 'the push ahead of the gate failed; the commit stage pushes after the gate:'
30
44
 
31
45
  // A stage's result, plus the paths a stopped merge left unmerged — carried to the stop prompt.
32
46
  // `is_skipped` marks a stage that found nothing to do — a review round with nothing to review, a gate
33
47
  // that reused a green record — so its few seconds are not timed as a run of it. `preconditions` names
34
48
  // the kinds of the preconditions a stopped preflight found unmet (`run-ship-preflight.ts`). `cause` is the
35
- // one a stopped review round tagged its stop with (`run-ship-stop-cause.ts`).
49
+ // one a stopped stage tagged its stop with — a review round, or a red gate over a merged tree
50
+ // (`run-ship-stop-cause.ts`). `reason` says why the stage ran or was skipped, written on its event line
51
+ // (`run-ship-review-reason.ts`).
36
52
  interface StepResult extends JoshResult {
37
53
  is_skipped?: boolean
54
+ reason?: string
38
55
  conflicts?: ReadonlyArray<string>
39
56
  preconditions?: ReadonlyArray<string>
40
57
  cause?: Cause
41
58
  }
42
59
 
60
+ // The push of a tree already committed, with no pull request touched.
61
+ function push_argv(title: string): ReadonlyArray<string> {
62
+ return ['git', '-y', '--skip-commit', '--skip-pr', title]
63
+ }
64
+
43
65
  async function josh(argv: ReadonlyArray<string>): Promise<JoshResult> {
44
66
  return await josh_command.josh_run(argv, should_forward_stderr)
45
67
  }
@@ -48,7 +70,7 @@ async function josh(argv: ReadonlyArray<string>): Promise<JoshResult> {
48
70
  // changed, and the followup still finds a real conflict on GitHub's side.
49
71
  async function merge_outcome(): Promise<MergeOutcome> {
50
72
  try {
51
- return await main_merge.merge()
73
+ return await timed(STEP.MERGE, async () => await main_merge.merge())
52
74
  } catch (error) {
53
75
  return { kind: 'refused', message: error_text.message_of(error) }
54
76
  }
@@ -68,51 +90,161 @@ function sync_note(outcome: Exclude<MergeOutcome, { kind: 'conflict' }>): string
68
90
  : `${MERGED_PREFIX}${outcome.branch}`
69
91
  }
70
92
 
71
- // A refusal does not stop the commit: the guard refuses only uncommitted work the default branch also
72
- // touches, and once that work is committed the followup merges again on a clean tree. A merge that changed
73
- // the tree is checked by the gate stage that follows, so the commit never carries
74
- // an unchecked merge and the gate's record names the merge base the push carries.
75
- //
93
+ // Uncommitted work is committed locally first, never stashed: the merge guard refuses uncommitted work
94
+ // the default branch also touches, and a merge deferred past it met its conflict only after the gate,
95
+ // both review rounds and CI (joshuafolkken/kit#4708). The commit is not pushed here: a tree the sync
96
+ // leaves unmerged goes out at the commit stage once the gate is green (`push_ahead` pushes a merged
97
+ // one). A refusal left — a fetch that failed — does not stop the ship: the commit stage and the
98
+ // followup merge again.
99
+ // The pre-commit prettier, cspell and eslint are switched off for it: the gate or the scoped pair runs
100
+ // the same checks over this tree before anything is pushed, and the hooks only waited for the core
101
+ // budget to repeat them (joshuafolkken/kit#4985).
102
+ async function commit_locally(title: string, is_dirty: boolean): Promise<JoshResult | undefined> {
103
+ if (!is_dirty) return undefined
104
+
105
+ const commit = await timed(
106
+ STEP.LOCAL_COMMIT,
107
+ async () =>
108
+ await josh_command.josh_run(
109
+ ['git', '-y', '--skip-push', '--skip-pr', title],
110
+ should_forward_stderr,
111
+ undefined,
112
+ { [pre_commit_lint_switch.SWITCH_ENV_KEY]: 'off' },
113
+ ),
114
+ )
115
+
116
+ return commit.code === SUCCESS_EXIT_CODE ? undefined : commit
117
+ }
118
+
119
+ // **A merge the sync made is pushed at once onto an open pull request, ahead of the gate**
120
+ // (joshuafolkken/kit#5067): CI and SonarQube start from this push, and pushed after the gate they ran
121
+ // behind it, leaving the followup minutes of waiting. Only the pull request's own branch moves; the
122
+ // merge still needs the gate, the review attest and a green CI, and a red gate stops the ship before
123
+ // the followup as before, the next push replacing the commit. Its pre-push hook runs the related tests
124
+ // (`pre-push-related-switch.ts`), since the gate over this same tree runs the whole suite next. A
125
+ // branch with no open pull request pushes nothing here: CI runs on pull requests only. An unmoved
126
+ // default branch pushes nothing either, so the commit stage stays the one push; a merged one makes it
127
+ // two, and CI and the review bots run again on the second. A failed push stops nothing: the commit
128
+ // stage pushes the gated tree.
129
+ async function push_ahead(title: string): Promise<string> {
130
+ const push = await timed(
131
+ STEP.PUSH,
132
+ async () =>
133
+ await josh_command.josh_run(push_argv(title), should_forward_stderr, undefined, {
134
+ [pre_push_related_switch.SWITCH_ENV_KEY]: pre_push_related_switch.SWITCH_ON,
135
+ }),
136
+ )
137
+
138
+ return push.code === SUCCESS_EXIT_CODE ? PUSHED_NOTE : [PUSH_FAILED_NOTE, push.out].join('\n')
139
+ }
140
+
141
+ // An unreadable pull request reads as none: the commit stage pushes after the gate either way.
142
+ async function has_open_pull(): Promise<boolean> {
143
+ try {
144
+ return (await git_gh_pr_read.pr_get_number(await git_command.branch())) !== undefined
145
+ } catch {
146
+ return false
147
+ }
148
+ }
149
+
150
+ async function pushed_note(outcome: MergeOutcome, title: string): Promise<string> {
151
+ if (outcome.kind !== 'merged' || !(await has_open_pull())) return ''
152
+
153
+ return await push_ahead(title)
154
+ }
155
+
156
+ async function waited_note(issue: string): Promise<string> {
157
+ const overlap = await timed(
158
+ STEP.OVERLAP_WAIT,
159
+ async () => await run_ship_overlap.await_overlap(issue),
160
+ )
161
+
162
+ return run_ship_overlap.note(overlap)
163
+ }
164
+
76
165
  // The merge waits first for any lane whose open pull request changes the same paths
77
166
  // (`run-ship-overlap.ts`), so it brings that pull request in before the gate rather than meeting it
78
167
  // as `DIRTY` after CI.
79
- async function sync_stage(issue: string): Promise<StepResult> {
80
- const waited = run_ship_overlap.note(await run_ship_overlap.await_overlap(issue))
168
+ async function sync_stage(issue: string, title: string, is_dirty: boolean): Promise<StepResult> {
169
+ const waited = await waited_note(issue)
170
+ const failed_commit = await commit_locally(title, is_dirty)
171
+
172
+ if (failed_commit !== undefined) {
173
+ const out = [waited, failed_commit.out].filter((line) => line !== '').join('\n')
174
+
175
+ return { ...failed_commit, out }
176
+ }
177
+
81
178
  const outcome = await merge_outcome()
82
179
 
83
180
  if (outcome.kind === 'conflict') return conflict_result(outcome.files, waited)
84
181
 
85
- const out = [waited, sync_note(outcome)].filter((line) => line !== '').join('\n')
182
+ const pushed = await pushed_note(outcome, title)
183
+ const out = [waited, sync_note(outcome), pushed].filter((line) => line !== '').join('\n')
86
184
 
87
185
  return { code: SUCCESS_EXIT_CODE, out }
88
186
  }
89
187
 
90
- // Any read that fails answers "not conflicting": the followup failure then stands as it was.
91
- async function is_conflicting(): Promise<boolean> {
92
- try {
93
- const state = await git_gh_pr_read.pr_get_merge_state(await git_command.branch())
188
+ // The tree merged with a default branch that moved is gated before it is pushed: a tree only the
189
+ // combined change breaks — two lanes each growing one document inside its cap — stops here, reproduced
190
+ // locally rather than met on CI's merge ref (joshuafolkken/kit#4825). The push then carries the tree
191
+ // the gate's record describes, so the pre-push hook reuses it rather than re-running the unit suite.
192
+ // Its red is tagged a check, so the stage's infrastructure retry never reads test output as an outage.
193
+ async function gate_merged(): Promise<StepResult | undefined> {
194
+ const gate = await run_ship_scoped.scoped_gate()
94
195
 
95
- return is_merge_conflict(state?.merge_state_status)
96
- } catch {
97
- return false
98
- }
196
+ if (gate.code === SUCCESS_EXIT_CODE) return undefined
197
+
198
+ const out = [MERGED_GATE_NOTE, gate.out].filter((line) => line !== '').join('\n')
199
+
200
+ return { ...gate, out, cause: run_ship_stop_cause.CAUSE.CHECK }
201
+ }
202
+
203
+ // Before the commit stage pushes: a default branch that moved since the sync stage is merged again and
204
+ // the merged tree gated, so the stop lands while the lane still waits on the ship. An unmoved one
205
+ // merges nothing and re-runs no gate; a refusal leaves the merge to the followup, as the sync does.
206
+ // A tree with no green gate record is gated whatever the merge answered: a re-run commit stage finds
207
+ // the merge its red gate stopped on already made, and must not push it without a gate.
208
+ async function catch_up(): Promise<StepResult | undefined> {
209
+ const outcome = await merge_outcome()
210
+
211
+ if (outcome.kind === 'conflict') return conflict_result(outcome.files, '')
212
+ if (outcome.kind !== 'merged' && (await run_ship_scoped.is_gate_green())) return undefined
213
+
214
+ return await gate_merged()
99
215
  }
100
216
 
101
- // The merged tree is gated before it is pushed: the push then carries the tree
102
- // the gate's record describes, so the pre-push hook reuses it rather than re-running the unit suite.
103
217
  async function push_merged(title: string): Promise<StepResult | undefined> {
104
- const gate = await run_ship_scoped.scoped_gate()
218
+ const gate = await gate_merged()
105
219
 
106
- if (gate.code !== SUCCESS_EXIT_CODE) return gate
220
+ if (gate !== undefined) return gate
107
221
 
108
- const push = await josh(['git', '-y', '--skip-commit', '--skip-pr', title])
222
+ const push = await josh(push_argv(title))
109
223
 
110
224
  return push.code === SUCCESS_EXIT_CODE ? undefined : push
111
225
  }
112
226
 
227
+ // The followup's reasons a newer base can change: a red check or a conflict.
228
+ const MERGE_FIXABLE_REASONS = [FAILURE_REASON_CHECKS, FAILURE_REASON_CONFLICT]
229
+
230
+ function names_merge_fixable(text: string | undefined): boolean {
231
+ if (text?.includes(FAILURE_MESSAGE_PREFIX) !== true) return false
232
+
233
+ return MERGE_FIXABLE_REASONS.some((reason) => text.includes(reason))
234
+ }
235
+
236
+ // Whether the followup failed on the pull request's checks — a red check or a conflict. A refusal
237
+ // before the wait, or a review blocker alone, is not one: its text carries neither reason.
238
+ function is_checks_failure(failed: JoshResult): boolean {
239
+ return [failed.out, failed.err].some((text) => names_merge_fixable(text))
240
+ }
241
+
113
242
  // The result to stop on, or `undefined` once a clean merge is pushed and the followup can wait again.
243
+ // A followup that failed on the checks merges again whenever the default branch moved since the push,
244
+ // conflicting or not: a red CI on the merge ref is then reproduced by the local gate or cleared by the
245
+ // new push. An unmoved default branch, or any other failure, stands.
114
246
  async function merge_again(title: string, failed: JoshResult): Promise<StepResult | undefined> {
115
- if (!(await is_conflicting())) return failed
247
+ if (!is_checks_failure(failed)) return failed
116
248
 
117
249
  const outcome = await merge_outcome()
118
250
 
@@ -168,7 +300,16 @@ async function followup_stage(
168
300
  return await followup_attempt(title, notify, mark, 0)
169
301
  }
170
302
 
171
- const run_ship_sync = { CONFLICT_NOTE, MAX_MERGE_RETRIES, followup_stage, sync_stage }
303
+ const run_ship_sync = {
304
+ CONFLICT_NOTE,
305
+ MAX_MERGE_RETRIES,
306
+ MERGED_GATE_NOTE,
307
+ PUSHED_NOTE,
308
+ PUSH_FAILED_NOTE,
309
+ catch_up,
310
+ followup_stage,
311
+ sync_stage,
312
+ }
172
313
 
173
314
  export type { StepResult, WaitMark }
174
315
  export { run_ship_sync }
@@ -0,0 +1,178 @@
1
+ import { gate_labels } from '#scripts/gate/gate-labels'
2
+ import type { GateTree } from '#scripts/gate/gate-tree'
3
+ import type { MovedParts } from '#scripts/gate/moved-paths'
4
+ import { scoped_green } from '#scripts/gate/scoped-green'
5
+ import { since_green } from '#scripts/gate/since-green'
6
+ import { changed_file_scope } from '#scripts/git/changed-file-scope'
7
+ import { changed_paths } from '#scripts/git/changed-paths'
8
+ import type { FileMapStampAccess } from '#scripts/josh/file-map-stamp'
9
+ import { run_record_path } from '#scripts/josh/run-record-path'
10
+ import { review_stamps } from '#scripts/review/review-stamps'
11
+ import { test_read_map, type ReadMap } from '#scripts/test/test-read-map'
12
+ import { related_scope } from '#scripts/test/test-related-scope'
13
+ import { test_related_synced, type SyncedSplit } from '#scripts/test/test-related-synced'
14
+ import { whole_suite_files } from '#scripts/test/whole-suite-files'
15
+
16
+ // **A scoped pair whose records went stale only because a merge with the default branch moved the base
17
+ // re-runs nothing when that merge shares nothing with this branch's change** (joshuafolkken/kit#4985).
18
+ // The sync, the commit stage's catch-up and a lane's own conflict merge each moved the base under a
19
+ // pair green on the branch side, and the pair re-ran over the branch's related set — minutes of core
20
+ // budget for main's files, which main's CI had already verified.
21
+ //
22
+ // **Where the two can meet still runs.** A file both sides changed, a test reaching both a synced module
23
+ // and this branch's change (`test-related-synced.ts`), or a synced module the import graph cannot
24
+ // split keeps the pair running as before, narrowed by the checks themselves (`since-green.ts`). So
25
+ // does, for `lint:related`, any file whose imports reach both sides, and for `test:related` a synced
26
+ // file every test depends on or a non-code file a test reads: `josh test:related` leaves those to the
27
+ // branch's own tests, which a settled record would skip. The
28
+ // gate still runs over the merged tree wherever it ran before (joshuafolkken/kit#4825), and CI runs
29
+ // the whole suite.
30
+ //
31
+ // **Whatever cannot be read settles nothing**: a record that is absent or cannot be compared, a branch
32
+ // side that moved since its record, a merge that changed a check's configuration, or a branch change
33
+ // git cannot read.
34
+
35
+ interface SyncedCheck {
36
+ script: string
37
+ label: string
38
+ stamp: FileMapStampAccess
39
+ }
40
+
41
+ const CHECKS: ReadonlyArray<SyncedCheck> = [
42
+ {
43
+ script: scoped_green.LINT_SCRIPT,
44
+ label: gate_labels.LINT_LABEL,
45
+ stamp: review_stamps.lint_related_stamp,
46
+ },
47
+ {
48
+ script: scoped_green.TEST_SCRIPT,
49
+ label: gate_labels.UNIT_LABEL,
50
+ stamp: review_stamps.test_related_stamp,
51
+ },
52
+ ]
53
+
54
+ function is_sync_only(parts: MovedParts): boolean {
55
+ return (
56
+ parts.synced.length > 0 &&
57
+ parts.changed.every((relative) => run_record_path.is_run_record(relative))
58
+ )
59
+ }
60
+
61
+ async function synced_of(
62
+ check: SyncedCheck,
63
+ tree: GateTree,
64
+ ): Promise<ReadonlyArray<string> | undefined> {
65
+ const parts = await since_green.read_moved_parts(check.stamp, check.label, undefined, tree)
66
+
67
+ return parts !== undefined && is_sync_only(parts) ? parts.synced : undefined
68
+ }
69
+
70
+ // What the merge brought in, when every stale record moved by nothing else.
71
+ async function synced_files(
72
+ checks: ReadonlyArray<SyncedCheck>,
73
+ tree: GateTree,
74
+ ): Promise<ReadonlyArray<string> | undefined> {
75
+ const parts = await Promise.all(checks.map(async (check) => await synced_of(check, tree)))
76
+ const known = parts.filter((synced) => synced !== undefined)
77
+
78
+ return known.length > 0 && known.length === parts.length ? [...new Set(known.flat())] : undefined
79
+ }
80
+
81
+ async function branch_change(): Promise<ReadonlyArray<string> | undefined> {
82
+ try {
83
+ return await changed_paths.read_changed_paths(false)
84
+ } catch {
85
+ return undefined
86
+ }
87
+ }
88
+
89
+ function is_whole_suite(file: string): boolean {
90
+ return whole_suite_files.WHOLE_SUITE_FILES.has(file)
91
+ }
92
+
93
+ // A synced file a test can reach with no import the graph splits: one every test depends on, or a
94
+ // non-code file a recorded test reads — every one of them when no map was recorded.
95
+ function is_read_by_tests(map: ReadMap | undefined, file: string): boolean {
96
+ if (is_whole_suite(file)) return true
97
+ if (related_scope.is_relatable(file)) return false
98
+
99
+ return map === undefined || test_read_map.readers_of(map, file).length > 0
100
+ }
101
+
102
+ function has_check(checks: ReadonlyArray<SyncedCheck>, script: string): boolean {
103
+ return checks.some((check) => check.script === script)
104
+ }
105
+
106
+ // What only one check reads where the two sides meet. `lint:related`'s type-aware rules read every
107
+ // file whose imports reach both, code between included; `test:related` leaves a file the graph
108
+ // cannot split to the branch's own tests (`test-related.ts`), which a settled record would skip.
109
+ function check_overlap(
110
+ checks: ReadonlyArray<SyncedCheck>,
111
+ synced: ReadonlyArray<string>,
112
+ split: SyncedSplit,
113
+ map: ReadMap | undefined,
114
+ ): ReadonlyArray<string> {
115
+ const meeting = has_check(checks, scoped_green.LINT_SCRIPT) ? split.meeting : []
116
+ const read = has_check(checks, scoped_green.TEST_SCRIPT)
117
+ ? synced.filter((file) => is_read_by_tests(map, file))
118
+ : []
119
+
120
+ return [...meeting, ...read]
121
+ }
122
+
123
+ // The synced files and tests where the merge and this branch's change meet.
124
+ async function overlap_of(
125
+ checks: ReadonlyArray<SyncedCheck>,
126
+ synced: ReadonlyArray<string>,
127
+ branch: ReadonlyArray<string>,
128
+ ): Promise<ReadonlyArray<string>> {
129
+ const root = await changed_file_scope.repository_root_or_cwd()
130
+ const map = test_read_map.read_map(root)
131
+ const split = await test_related_synced.split(root, synced, branch, map)
132
+ const both = synced.filter((file) => branch.includes(file))
133
+ // Code the graph left unsplit — whole-suite modules included, which it never splits.
134
+ const unsplit = synced.filter(
135
+ (file) =>
136
+ related_scope.is_relatable(file) && !branch.includes(file) && !split.left.includes(file),
137
+ )
138
+
139
+ return [...both, ...split.shared, ...unsplit, ...check_overlap(checks, synced, split, map)]
140
+ }
141
+
142
+ async function unshared_sync(
143
+ checks: ReadonlyArray<SyncedCheck>,
144
+ tree: GateTree,
145
+ ): Promise<ReadonlyArray<string> | undefined> {
146
+ const synced = await synced_files(checks, tree)
147
+ const branch = synced === undefined ? undefined : await branch_change()
148
+
149
+ if (synced === undefined || branch === undefined) return undefined
150
+
151
+ const overlap = await overlap_of(checks, synced, branch)
152
+
153
+ return overlap.length === 0 ? synced : undefined
154
+ }
155
+
156
+ function reason(scripts: ReadonlyArray<string>, synced: ReadonlyArray<string>): string {
157
+ return (
158
+ `scoped pair: ${scripts.join(' and ')} not re-run — the ${String(synced.length)} file(s) ` +
159
+ "the merge with the default branch brought in share no file or test with this branch's change."
160
+ )
161
+ }
162
+
163
+ // The line a settled pair prints, after re-recording each stale check green on this tree so the gate
164
+ // and the brief read it as checked — or `undefined`, and the pair runs as before.
165
+ async function settle(tree: GateTree, scripts: ReadonlyArray<string>): Promise<string | undefined> {
166
+ const checks = CHECKS.filter((check) => scripts.includes(check.script))
167
+ const synced = await unshared_sync(checks, tree)
168
+
169
+ if (synced === undefined) return undefined
170
+
171
+ for (const check of checks) scoped_green.record_green(check.stamp, tree)
172
+
173
+ return reason(scripts, synced)
174
+ }
175
+
176
+ const run_ship_synced_scope = { settle }
177
+
178
+ export { run_ship_synced_scope }
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env tsx
2
+ import { setTimeout as sleep } from 'node:timers/promises'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { issue_number_shape } from '#scripts/issue/issue-number-shape'
5
+ import { run_ship_detach } from './run-ship-detach'
6
+ import { run_ship_probe } from './run-ship-probe'
7
+ import { run_ship_stage } from './run-ship-stage'
8
+ import { run_ship_waiter } from './run-ship-waiter'
9
+
10
+ // `josh ship:wait <N>` — the lane session that detached a ship waits here for the gate and review
11
+ // round 1, so a stop there comes back to it (`run-ship-waiter.ts`) rather than to a fresh session
12
+ // (joshuafolkken/kit#4711). One verdict line, then the stop note on `returned`:
13
+ //
14
+ // - `returned` — the supervisor stopped and handed its note to this session: fix it and ship again.
15
+ // - `passed` — the ship committed, or finished: what follows is the supervisor's alone.
16
+ // - `ended` — the supervisor ended with no note for this session: nothing is left to wait for.
17
+ // - `pending` — the chunk ran out with the ship still before its commit: issue the wait again.
18
+ //
19
+ // **A chunk ends below the five-minute prompt-cache TTL a lane child runs on** (`lane-child-marker.ts`),
20
+ // so re-issuing it is the keep-alive: the next request reads the cached context back (66k tokens on
21
+ // Opus 5.5, about $0.013) instead of writing it whole again after the wait (about $0.33) —
22
+ // joshuafolkken/kit#5344. The heartbeat outlives the chunk by the session gap.
23
+
24
+ const VERDICT = {
25
+ RETURNED: 'returned',
26
+ PASSED: 'passed',
27
+ ENDED: 'ended',
28
+ PENDING: 'pending',
29
+ } as const
30
+
31
+ type Verdict = (typeof VERDICT)[keyof typeof VERDICT]
32
+
33
+ interface Waited {
34
+ verdict: Verdict
35
+ note?: string
36
+ }
37
+
38
+ const CHUNK_MS = 270_000
39
+ const POLL_MS = 2000
40
+ const ARGV_OFFSET = 2
41
+ const SUCCESS_EXIT_CODE = 0
42
+ const FAILURE_EXIT_CODE = 1
43
+ const USAGE = 'Usage: josh ship:wait <N>'
44
+ const NO_REPOSITORY_NOTE = 'not inside a git repository'
45
+
46
+ const { COMMIT, ROUND_TWO, FOLLOWUP, REPORT } = run_ship_stage.STAGE
47
+ const PAST_COMMIT: ReadonlySet<string> = new Set([ROUND_TWO, FOLLOWUP, REPORT])
48
+
49
+ // Past the commit in the current attempt: the record's `done` outlives a stop after the commit, so a
50
+ // re-ship after a followup stop would read as committed before its own gate and review ran. `started`
51
+ // is cleared at every stop, so it says what this attempt reached.
52
+ function is_committed(repository: string, issue: string): boolean {
53
+ const target = run_ship_stage.record_path(repository, issue)
54
+ const started = run_ship_stage.read_started(target)
55
+
56
+ if (started.some((stage) => PAST_COMMIT.has(stage))) return true
57
+
58
+ return started.includes(COMMIT) && run_ship_stage.read_done(target).has(COMMIT)
59
+ }
60
+
61
+ // The supervisor's own end, where it left no note: `undefined` while it still runs.
62
+ function ended(repository: string, issue: string): Waited | undefined {
63
+ const result = run_ship_detach.read_result(repository, issue)?.result
64
+
65
+ if (result === 'running') return undefined
66
+
67
+ return { verdict: result === 'success' ? VERDICT.PASSED : VERDICT.ENDED }
68
+ }
69
+
70
+ function settled(repository: string, issue: string): Waited | undefined {
71
+ const note = run_ship_waiter.take(repository, issue)
72
+
73
+ if (note !== undefined) return { verdict: VERDICT.RETURNED, note }
74
+ if (is_committed(repository, issue)) return { verdict: VERDICT.PASSED }
75
+
76
+ return ended(repository, issue)
77
+ }
78
+
79
+ async function poll(repository: string, issue: string, deadline: number): Promise<Waited> {
80
+ while (Date.now() < deadline) {
81
+ run_ship_waiter.beat(repository, issue)
82
+ const waited = settled(repository, issue)
83
+
84
+ if (waited !== undefined) return waited
85
+
86
+ // eslint-disable-next-line no-await-in-loop -- polling: each read waits on the supervisor's progress
87
+ await sleep(POLL_MS)
88
+ }
89
+
90
+ return { verdict: VERDICT.PENDING }
91
+ }
92
+
93
+ // A `pending` chunk keeps the heartbeat for the next one; any other verdict ends the wait for good.
94
+ async function wait_for(repository: string, issue: string, chunk_ms = CHUNK_MS): Promise<Waited> {
95
+ const waited = await poll(repository, issue, Date.now() + chunk_ms)
96
+
97
+ if (waited.verdict !== VERDICT.PENDING) run_ship_waiter.leave(repository, issue)
98
+
99
+ return waited
100
+ }
101
+
102
+ // Each verdict carries what the session does next, so the instruction lives with the command.
103
+ function next_action(issue: string): Record<Verdict, string> {
104
+ return {
105
+ [VERDICT.RETURNED]: 'the ship stopped: fix what the note below says, then ship again',
106
+ [VERDICT.PASSED]: 'the supervisor ships the rest: end the turn',
107
+ [VERDICT.ENDED]: 'nothing is left to wait for: end the turn',
108
+ [VERDICT.PENDING]: `still before the commit: on \`pnpm josh cost --cut\` \`under\`, run \`pnpm josh ship:wait ${issue}\` again; else end the turn`,
109
+ }
110
+ }
111
+
112
+ function report(issue: string, waited: Waited): void {
113
+ console.info(`${waited.verdict} — ${next_action(issue)[waited.verdict]}`)
114
+ if (waited.note !== undefined) console.info(waited.note)
115
+ }
116
+
117
+ async function run(argv: ReadonlyArray<string>): Promise<number> {
118
+ const issue = issue_number_shape.single_of(argv)
119
+
120
+ if (issue === undefined) {
121
+ console.error(USAGE)
122
+
123
+ return FAILURE_EXIT_CODE
124
+ }
125
+
126
+ const repository = await run_ship_probe.repository_directory()
127
+
128
+ if (repository === undefined) {
129
+ console.error(NO_REPOSITORY_NOTE)
130
+
131
+ return FAILURE_EXIT_CODE
132
+ }
133
+
134
+ report(issue, await wait_for(repository, issue))
135
+
136
+ return SUCCESS_EXIT_CODE
137
+ }
138
+
139
+ const run_ship_wait_cli = { CHUNK_MS, VERDICT, run, wait_for }
140
+
141
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
142
+ process.exitCode = await run(process.argv.slice(ARGV_OFFSET))
143
+ }
144
+
145
+ export { run_ship_wait_cli }