@joshuafolkken/kit 1.1166.0 → 1.1229.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 (437) hide show
  1. package/.claude/.claude-plugin/plugin.json +1 -1
  2. package/.claude/settings.json +2 -2
  3. package/.claude/skills/dependency-update/SKILL.md +4 -8
  4. package/.claude/skills/epic-commands/SKILL.md +2 -2
  5. package/.claude/skills/epic-commands/epic-bundle.md +1 -1
  6. package/.claude/skills/workflow-commands/SKILL.md +2 -4
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +9 -5
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +29 -9
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +7 -2
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +24 -15
  13. package/.claude/skills/workflow-commands/chain-rule.md +1 -1
  14. package/.claude/skills/workflow-commands/delegation.md +2 -2
  15. package/.claude/skills/workflow-commands/followup.md +5 -0
  16. package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
  17. package/.claude/skills/workflow-commands/halfrun.md +1 -1
  18. package/.claude/skills/workflow-commands/issue-scout.md +1 -1
  19. package/.claude/skills/workflow-commands/kickoff.md +1 -1
  20. package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
  21. package/.claude/skills/workflow-commands/pre-gate-cut.md +10 -5
  22. package/.claude/skills/workflow-commands/prerequisite.md +2 -2
  23. package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
  24. package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
  25. package/.codex/hooks.json +2 -2
  26. package/.cursorrules +6 -41
  27. package/.github/actions/setup-node/action.yml +1 -1
  28. package/.github/workflows/auto-tag.yml +3 -4
  29. package/.github/workflows/ci.yml +47 -33
  30. package/.github/workflows/dependabot-auto-merge.yml +13 -6
  31. package/.github/workflows/publish.yml +48 -4
  32. package/.github/workflows/sonar-qube.yml +3 -0
  33. package/.pnpmfile.mjs +4 -3
  34. package/.prettierignore +8 -2
  35. package/CLAUDE.md +18 -20
  36. package/README.md +26 -19
  37. package/SECURITY.md +15 -1
  38. package/dist/CLAUDE.md +18 -20
  39. package/dist/commands/document-section-cli.js +734 -0
  40. package/dist/hooks/audit-provision.js +346 -0
  41. package/dist/hooks/batch-guard.js +5 -4
  42. package/dist/hooks/{chunk-DYAVAM6S.js → chunk-55HHG5PX.js} +1405 -631
  43. package/dist/hooks/{chunk-A6P7PN7P.js → chunk-5VMKCCC4.js} +25 -10
  44. package/dist/hooks/chunk-5VNDSNG3.js +12 -0
  45. package/dist/hooks/{chunk-5KTCMCOT.js → chunk-6HJJXRBX.js} +4 -2
  46. package/dist/hooks/chunk-CDY4NFQG.js +15 -0
  47. package/dist/hooks/chunk-ELCN5LCH.js +135 -0
  48. package/dist/hooks/{chunk-ENA7P5FB.js → chunk-F56MFS4B.js} +3 -3
  49. package/dist/hooks/{chunk-ANJ2WGFD.js → chunk-FIVDHQGT.js} +7 -6
  50. package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
  51. package/dist/hooks/{chunk-GU5MATVE.js → chunk-JUDKDCDP.js} +499 -389
  52. package/dist/hooks/{chunk-ZDZ555YH.js → chunk-NV2JRZZO.js} +743 -909
  53. package/dist/hooks/{chunk-V2BTOZHT.js → chunk-RNWHN5BP.js} +3 -3
  54. package/dist/hooks/{chunk-URJA6XY4.js → chunk-TQNPDZNH.js} +4 -4
  55. package/dist/hooks/{chunk-GYNGP7HI.js → chunk-VC3PZPZO.js} +1 -1
  56. package/dist/hooks/codex-hook-adapter.js +14 -12
  57. package/dist/hooks/duplicate-read-guard.js +6 -5
  58. package/dist/hooks/format-edited.js +4 -3
  59. package/dist/hooks/inputs.json +232 -210
  60. package/dist/hooks/investigation-guard.js +6 -5
  61. package/dist/hooks/pretool-guard.js +12 -10
  62. package/dist/hooks/session-lang.js +2 -4
  63. package/dist/hooks/stop-guard.js +81 -59
  64. package/dist/josh.js +872 -75
  65. package/dist/version/index.d.ts +7 -2
  66. package/dist/version/index.js +152 -75
  67. package/eslint/base.js +23 -1
  68. package/eslint/rules/code-quality.js +3 -0
  69. package/eslint/rules/early-return-one-liner.js +98 -0
  70. package/eslint/rules/unicorn.js +16 -1
  71. package/package.json +7 -6
  72. package/pnpm-workspace.yaml +9 -4
  73. package/prettier/basic.d.ts +3 -1
  74. package/prettier/index.d.ts +3 -1
  75. package/prettier/static.d.ts +3 -1
  76. package/prompts/coding-standards.md +1 -1
  77. package/prompts/collaboration-workflow/file-edits.md +4 -4
  78. package/prompts/collaboration-workflow/glossary.md +1 -1
  79. package/prompts/collaboration-workflow/operating-rules.md +17 -9
  80. package/prompts/collaboration-workflow/overview.md +2 -2
  81. package/prompts/collaboration-workflow/principles.md +14 -12
  82. package/prompts/collaboration-workflow/report-format.md +1 -1
  83. package/prompts/collaboration-workflow/residency.md +1 -1
  84. package/prompts/collaboration-workflow/rule-delivery.md +6 -6
  85. package/prompts/collaboration-workflow/shell-body.md +6 -6
  86. package/prompts/collaboration-workflow/turn-batching.md +1 -1
  87. package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
  88. package/prompts/collaboration-workflow/wip-cap.md +10 -10
  89. package/prompts/review-rubric.md +1 -2
  90. package/prompts/review.md +1 -1
  91. package/scripts/agent/agent-headless.ts +29 -0
  92. package/scripts/agent/agent-launch-environment.ts +6 -2
  93. package/scripts/agent/codex-agent-argv.ts +2 -1
  94. package/scripts/auto-ok/auto-ok-cli.ts +1 -1
  95. package/scripts/backlog/backlog-arrival.ts +3 -3
  96. package/scripts/backlog/backlog-budget.ts +1 -1
  97. package/scripts/backlog/backlog-drive-args.ts +135 -0
  98. package/scripts/backlog/backlog-drive-cli.ts +11 -137
  99. package/scripts/backlog/backlog-drive-launch.ts +39 -0
  100. package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
  101. package/scripts/backlog/backlog-drive-named.ts +3 -3
  102. package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
  103. package/scripts/backlog/backlog-drive-owner.ts +1 -1
  104. package/scripts/backlog/backlog-drive-restore.ts +1 -1
  105. package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
  106. package/scripts/backlog/backlog-drive.ts +1 -1
  107. package/scripts/backlog/backlog-next.ts +31 -9
  108. package/scripts/backlog/backlog-offer-cli.ts +6 -6
  109. package/scripts/backlog/backlog-offer.ts +1 -1
  110. package/scripts/backlog/backlog-plan-cli.ts +7 -6
  111. package/scripts/backlog/backlog-pool.ts +57 -11
  112. package/scripts/backlog/backlog-rank.ts +18 -9
  113. package/scripts/backlog/backlog-ready.ts +1 -1
  114. package/scripts/backlog/backlog-restructure.ts +152 -0
  115. package/scripts/backlog/backlog-scope.ts +1 -1
  116. package/scripts/backlog/backlog-stalled-detect.ts +2 -2
  117. package/scripts/backlog/backlog-stalled.ts +1 -1
  118. package/scripts/backlog/backlog-waves.ts +15 -17
  119. package/scripts/build/build-codex-hooks.ts +3 -1
  120. package/scripts/build/build-commands.ts +66 -0
  121. package/scripts/build/build-hooks.ts +2 -1
  122. package/scripts/build/build-library.ts +12 -3
  123. package/scripts/build/build.ts +70 -0
  124. package/scripts/build/local-bin.ts +3 -13
  125. package/scripts/cost-runtime/context-cut-threshold.ts +1 -9
  126. package/scripts/cost-runtime/cost-verdict.ts +15 -28
  127. package/scripts/delegation/investigation-reads.ts +3 -1
  128. package/scripts/discovery/repo-origin.ts +5 -5
  129. package/scripts/doctor/doctor-consumer.ts +2 -2
  130. package/scripts/doctor/doctor-io.ts +2 -3
  131. package/scripts/document/capped-print-part.ts +30 -0
  132. package/scripts/document/capped-print.ts +3 -4
  133. package/scripts/document/document-byte-budget.ts +11 -2
  134. package/scripts/document/document-scan.ts +117 -0
  135. package/scripts/document/document-section.ts +143 -16
  136. package/scripts/document/entry-read-budget.ts +5 -3
  137. package/scripts/document/entry-read-set.ts +10 -89
  138. package/scripts/document/read-set-cost.ts +98 -0
  139. package/scripts/epic/epic-add-body.ts +1 -0
  140. package/scripts/epic/epic-audit-checks.ts +1 -1
  141. package/scripts/epic/epic-audit-rationale.ts +4 -3
  142. package/scripts/epic/epic-bundle-cli.ts +14 -3
  143. package/scripts/epic/epic-bundle-poll.ts +41 -0
  144. package/scripts/epic/epic-busy.ts +1 -1
  145. package/scripts/epic/epic-candidate-confirm.ts +3 -2
  146. package/scripts/epic/epic-classify.ts +1 -1
  147. package/scripts/epic/epic-cli-argv.ts +78 -46
  148. package/scripts/epic/epic-cli-remove.ts +11 -19
  149. package/scripts/epic/epic-cli.ts +74 -73
  150. package/scripts/epic/epic-close-comment.ts +1 -1
  151. package/scripts/epic/epic-close.ts +1 -1
  152. package/scripts/epic/epic-cross-repo.ts +5 -5
  153. package/scripts/epic/epic-graph.ts +1 -1
  154. package/scripts/epic/epic-index.ts +1 -1
  155. package/scripts/epic/epic-issue.ts +2 -6
  156. package/scripts/epic/epic-lane-offer.ts +2 -2
  157. package/scripts/epic/epic-next.ts +29 -42
  158. package/scripts/epic/epic-solo-stale.ts +1 -1
  159. package/scripts/epic/epic-triage.ts +8 -4
  160. package/scripts/epic/epic-validate.ts +1 -1
  161. package/scripts/followup/git-followup-finish.ts +2 -2
  162. package/scripts/followup/git-followup-label.ts +2 -2
  163. package/scripts/followup/git-followup-merged.ts +2 -2
  164. package/scripts/followup/git-followup-stages.ts +9 -0
  165. package/scripts/followup/git-pr-followup-checks.ts +142 -0
  166. package/scripts/followup/git-pr-followup.ts +5 -130
  167. package/scripts/gate/core-budget.ts +69 -1
  168. package/scripts/gate/gate-plan.ts +47 -7
  169. package/scripts/gate/gate-skip.ts +38 -7
  170. package/scripts/gate/hook-gate-reuse.ts +12 -10
  171. package/scripts/gate/pre-commit-type-check.ts +4 -1
  172. package/scripts/gate/project-checks.ts +6 -9
  173. package/scripts/gate/type-check-step.ts +2 -3
  174. package/scripts/gate/verification-gate.ts +27 -8
  175. package/scripts/gh/git-gh-api-path.ts +2 -2
  176. package/scripts/gh/git-gh-issue-rest.ts +3 -3
  177. package/scripts/gh/git-gh-issue-write.ts +53 -7
  178. package/scripts/gh/git-gh-issue.ts +5 -3
  179. package/scripts/gh/git-gh-pr-read.ts +68 -19
  180. package/scripts/gh/git-gh-pr-rest.ts +1 -1
  181. package/scripts/gh/git-gh-pr.ts +5 -1
  182. package/scripts/gh/git-pr-ai-review.ts +1 -1
  183. package/scripts/gh/git-pr-checks-eval.ts +9 -3
  184. package/scripts/gh/git-pr-checks-parse.ts +1 -1
  185. package/scripts/gh/git-pr-checks-watch.ts +6 -5
  186. package/scripts/gh/git-pr-checks.ts +18 -6
  187. package/scripts/gh/git-pr-coderabbit.ts +1 -1
  188. package/scripts/gh/git-pr-error.ts +1 -3
  189. package/scripts/gh/git-pr.ts +9 -13
  190. package/scripts/gh/github-issue-url.ts +2 -2
  191. package/scripts/git/constants.ts +12 -6
  192. package/scripts/git/git-command.ts +20 -218
  193. package/scripts/git/git-diff-reads.ts +225 -0
  194. package/scripts/git/git-error.ts +4 -12
  195. package/scripts/git/git-pre-push-hook.ts +139 -0
  196. package/scripts/git/git-prompt.ts +2 -6
  197. package/scripts/git/{schemas.ts → git-schemas.ts} +2 -0
  198. package/scripts/git/git-spawn-sync.ts +3 -1
  199. package/scripts/git/git-spawn.ts +1 -1
  200. package/scripts/git/git-staging.ts +4 -6
  201. package/scripts/git/git-status.ts +3 -9
  202. package/scripts/git/main-merge.ts +57 -21
  203. package/scripts/hooks/codex-hook-adapter.ts +1 -1
  204. package/scripts/hooks/edited-cspell.ts +2 -4
  205. package/scripts/hooks/format-edited-file.ts +3 -12
  206. package/scripts/hooks/pre-push-unit.ts +31 -9
  207. package/scripts/hooks/reserved-run.ts +2 -2
  208. package/scripts/hooks/run-hook.sh +5 -4
  209. package/scripts/hooks/stop-guard.ts +5 -3
  210. package/scripts/init/init-actions.ts +0 -5
  211. package/scripts/init/init-ai-copy.ts +13 -2
  212. package/scripts/init/init-logic-json-merge.ts +19 -0
  213. package/scripts/init/init-logic-secretlint.ts +7 -7
  214. package/scripts/init/init-logic-workspace.ts +41 -2
  215. package/scripts/init/init-logic.ts +16 -26
  216. package/scripts/init/init.ts +5 -15
  217. package/scripts/init/initial-commit.ts +4 -4
  218. package/scripts/init/kit-development-versions.ts +40 -0
  219. package/scripts/init/kit-setup-state.ts +2 -2
  220. package/scripts/init/start-exec.ts +68 -19
  221. package/scripts/init/start-setup-pr.ts +9 -9
  222. package/scripts/init/start-steps.ts +6 -10
  223. package/scripts/issue/defect-rate-cli.ts +1 -1
  224. package/scripts/issue/git-next-issues.ts +1 -1
  225. package/scripts/issue/issue-auto-ok.ts +1 -1
  226. package/scripts/issue/issue-backlinks-cli.ts +2 -2
  227. package/scripts/issue/issue-backlinks.ts +9 -9
  228. package/scripts/issue/issue-comment-cli.ts +19 -13
  229. package/scripts/issue/issue-file-cli.ts +45 -18
  230. package/scripts/issue/issue-file.ts +24 -1
  231. package/scripts/issue/issue-scout-closed.ts +1 -1
  232. package/scripts/issue/issue-state-cli.ts +30 -74
  233. package/scripts/josh/josh-command-map.ts +2 -0
  234. package/scripts/josh/josh-command-types.ts +11 -0
  235. package/scripts/josh/josh-commands-ai.ts +4 -4
  236. package/scripts/josh/josh-commands-backlog.ts +4 -1
  237. package/scripts/josh/josh-commands-development.ts +11 -1
  238. package/scripts/josh/josh-commands-document.ts +1 -0
  239. package/scripts/josh/josh-commands-lane.ts +22 -0
  240. package/scripts/josh/josh-commands-maintenance.ts +4 -4
  241. package/scripts/josh/josh-commands-run.ts +12 -7
  242. package/scripts/josh/josh-in-process.ts +28 -9
  243. package/scripts/josh/josh-logic.ts +33 -15
  244. package/scripts/josh/process-identity.ts +1 -3
  245. package/scripts/josh/session-language-cli.ts +3 -4
  246. package/scripts/lane/lane-await.ts +5 -1
  247. package/scripts/lane/lane-child-invocation.ts +2 -2
  248. package/scripts/lane/lane-cli.ts +3 -3
  249. package/scripts/lane/lane-dispatch.ts +14 -10
  250. package/scripts/lane/lane-environment.ts +5 -1
  251. package/scripts/lane/lane-handoff.ts +1 -1
  252. package/scripts/lane/lane-install.ts +2 -13
  253. package/scripts/lane/lane-launch-cli.ts +35 -3
  254. package/scripts/lane/lane-ledger.ts +145 -0
  255. package/scripts/lane/lane-leftover.ts +1 -1
  256. package/scripts/lane/lane-load.ts +108 -0
  257. package/scripts/lane/lane-measure-cli.ts +149 -0
  258. package/scripts/lane/lane-occupancy.ts +3 -0
  259. package/scripts/lane/lane-reap.ts +1 -3
  260. package/scripts/lane/lane-relaunch.ts +1 -1
  261. package/scripts/lane/lane-stats.ts +224 -0
  262. package/scripts/lane/lane-vacant.ts +36 -0
  263. package/scripts/lane/openai-lane-ship.ts +2 -2
  264. package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
  265. package/scripts/lane/openai-lane-supervisor.ts +7 -165
  266. package/scripts/lane/openai-review-broker.ts +2 -4
  267. package/scripts/lib/ancestor-directories.ts +35 -0
  268. package/scripts/lib/animation-helpers.ts +2 -2
  269. package/scripts/lib/buffered-process.ts +3 -7
  270. package/scripts/lib/cli-flags.ts +71 -1
  271. package/scripts/lib/spawn-exit.ts +2 -2
  272. package/scripts/lib/timeouts.ts +35 -2
  273. package/scripts/lines/line-budget.ts +4 -6
  274. package/scripts/notify/telegram-notify.ts +26 -1
  275. package/scripts/notify/telegram-test.ts +6 -0
  276. package/scripts/observations/observation-ledger-line.ts +6 -6
  277. package/scripts/observations/observation-ledger-migrate.ts +55 -4
  278. package/scripts/observations/observation-ledger.ts +26 -7
  279. package/scripts/overrides/lockfile-overrides.ts +1 -1
  280. package/scripts/overrides/overrides-logic.ts +1 -1
  281. package/scripts/propagate/propagate-git.ts +1 -1
  282. package/scripts/propagate/propagate-publish.ts +2 -2
  283. package/scripts/propagate/propagate-steps.ts +20 -15
  284. package/scripts/propagate/propagate.ts +36 -27
  285. package/scripts/refactor/import-graph.ts +18 -1
  286. package/scripts/refactor/refactor-lint.ts +4 -4
  287. package/scripts/repo/repository-labels.ts +2 -2
  288. package/scripts/repo/required-checks-logic.ts +1 -3
  289. package/scripts/review/live-evidence.ts +2 -4
  290. package/scripts/review/review-brief-cli.ts +1 -1
  291. package/scripts/review/review-finding-ledger.ts +1 -1
  292. package/scripts/review/review-findings-cli.ts +1 -3
  293. package/scripts/rules/decision-oracle-batch.ts +128 -0
  294. package/scripts/rules/decision-oracle-tokens.ts +21 -0
  295. package/scripts/rules/decision-oracle.ts +8 -120
  296. package/scripts/rules/delivered-rules.ts +26 -182
  297. package/scripts/rules/direct-filing.ts +1 -1
  298. package/scripts/rules/early-heartbeat.ts +3 -3
  299. package/scripts/rules/git-argv.ts +1 -1
  300. package/scripts/rules/git-force.ts +1 -1
  301. package/scripts/rules/implementation-cut.ts +2 -2
  302. package/scripts/rules/index-guard.ts +1 -1
  303. package/scripts/rules/issue-comments.ts +118 -0
  304. package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
  305. package/scripts/rules/lane-background.ts +2 -2
  306. package/scripts/rules/lane-park.ts +18 -0
  307. package/scripts/rules/lane-split-park.ts +128 -0
  308. package/scripts/rules/oracle-firing.ts +1 -1
  309. package/scripts/rules/poll-loop.ts +1 -1
  310. package/scripts/rules/pre-gate-cut.ts +3 -3
  311. package/scripts/rules/protected-files.ts +59 -4
  312. package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
  313. package/scripts/rules/shell-body-trigger.ts +1 -1
  314. package/scripts/rules/stop-rules.ts +18 -7
  315. package/scripts/rules/test-declared-commit.ts +4 -4
  316. package/scripts/rules/wip-cap.ts +63 -0
  317. package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
  318. package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
  319. package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
  320. package/scripts/run/{run-carry.ts → carry/run-carry.ts} +12 -6
  321. package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
  322. package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +3 -3
  323. package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +2 -2
  324. package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
  325. package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
  326. package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
  327. package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
  328. package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
  329. package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +16 -2
  330. package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
  331. package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
  332. package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
  333. package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
  334. package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
  335. package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
  336. package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
  337. package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
  338. package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
  339. package/scripts/run/run-ending.ts +2 -2
  340. package/scripts/run/run-halfrun-resume.ts +6 -1
  341. package/scripts/run/run-headless.ts +1 -1
  342. package/scripts/run/run-invocation.ts +85 -84
  343. package/scripts/run/run-liveness.ts +1 -1
  344. package/scripts/run/run-parent-cut-hook.ts +1 -1
  345. package/scripts/run/run-preflight.ts +6 -6
  346. package/scripts/run/run-prep-cli.ts +1 -1
  347. package/scripts/run/run-prrun-resume.ts +1 -1
  348. package/scripts/run/run-report-cli.ts +4 -4
  349. package/scripts/run/run-report.ts +2 -2
  350. package/scripts/run/run-status-cli.ts +1 -1
  351. package/scripts/run/run-step-cli.ts +3 -3
  352. package/scripts/run/run-step.ts +2 -2
  353. package/scripts/run/run-stop-notify.ts +1 -1
  354. package/scripts/run/run-stranded-detect.ts +4 -4
  355. package/scripts/run/run-stranded.ts +2 -2
  356. package/scripts/run/run-watcher-guard-cli.ts +1 -1
  357. package/scripts/run/run-watcher-guard.ts +5 -3
  358. package/scripts/run/run-watcher-hook.ts +1 -1
  359. package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +29 -8
  360. package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
  361. package/scripts/run/ship/run-ship-lock.ts +50 -0
  362. package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +13 -0
  363. package/scripts/run/ship/run-ship-pre-detach.ts +66 -0
  364. package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
  365. package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
  366. package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +10 -6
  367. package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
  368. package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
  369. package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +13 -0
  370. package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +15 -8
  371. package/scripts/run/ship/run-ship-stop-text.ts +13 -0
  372. package/scripts/run/ship/run-ship-sync.ts +128 -0
  373. package/scripts/run/{run-ship.ts → ship/run-ship.ts} +6 -0
  374. package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
  375. package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +31 -9
  376. package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +3 -3
  377. package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +99 -17
  378. package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
  379. package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +8 -2
  380. package/scripts/run/wake/run-wake-restart.ts +58 -0
  381. package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
  382. package/scripts/run/{run-wake.ts → wake/run-wake.ts} +1 -1
  383. package/scripts/safe-chain/preinstall-command.ts +20 -21
  384. package/scripts/safe-chain/project-config.ts +19 -15
  385. package/scripts/sync/sync-ai-files.ts +1 -1
  386. package/scripts/sync/sync-configs.ts +2 -1
  387. package/scripts/sync/sync.ts +15 -2
  388. package/scripts/test/test-red-logic.ts +17 -2
  389. package/scripts/test/test-red.ts +8 -7
  390. package/scripts/test/unit-worker-share.ts +1 -0
  391. package/scripts/time-runtime/time-background.ts +5 -1
  392. package/scripts/time-runtime/time-instant.ts +3 -1
  393. package/scripts/time-runtime/time-spans.ts +6 -139
  394. package/scripts/time-runtime/time-tool-call.ts +160 -0
  395. package/scripts/time-runtime/time-transcript-line.ts +27 -1
  396. package/scripts/version/effective-upstream.ts +2 -20
  397. package/scripts/version/latest-corepack.ts +17 -17
  398. package/scripts/version/latest-regression.ts +1 -1
  399. package/scripts/version/latest-update.ts +4 -4
  400. package/scripts/version/npm-registry.ts +20 -5
  401. package/scripts/version/publishable-range-check.ts +2 -2
  402. package/scripts/version/publishable-range.ts +1 -1
  403. package/scripts/version/release-age.ts +48 -38
  404. package/scripts/version/release-hold.ts +1 -1
  405. package/scripts/version/safe-chain-version-update.ts +40 -0
  406. package/scripts/version/upgrade-shell-command.ts +48 -8
  407. package/scripts/version/version-check-logic.ts +23 -14
  408. package/scripts/version/version-commands.ts +23 -11
  409. package/scripts/version/version-remote.ts +28 -13
  410. package/templates/pnpm-workspace.basic.yaml +4 -0
  411. package/templates/workflows/dependabot-auto-merge.yml +5 -5
  412. package/templates/workflows/github-release.yml +2 -0
  413. package/scripts/build/build-config-merge.ts +0 -27
  414. package/scripts/build/build-managed-marker.ts +0 -27
  415. package/scripts/build/build-self-sync-guard.ts +0 -27
  416. package/scripts/build/build-version.ts +0 -26
  417. package/scripts/version/preinstall-version-update.ts +0 -86
  418. /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
  419. /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
  420. /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
  421. /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
  422. /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
  423. /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
  424. /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
  425. /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
  426. /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
  427. /package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +0 -0
  428. /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
  429. /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
  430. /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
  431. /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
  432. /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
  433. /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
  434. /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
  435. /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
  436. /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
  437. /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
@@ -2,6 +2,7 @@ import { FULL_PAGE_QUERY, git_gh_api_path } from './git-gh-api-path'
2
2
  import { git_gh_exec } from './git-gh-exec'
3
3
  import { git_gh_helpers } from './git-gh-helpers'
4
4
  import { git_gh_pr_rest, type RestPull } from './git-gh-pr-rest'
5
+ import { to_gh_state } from './git-gh-rest-state'
5
6
 
6
7
  // Reading pull requests through REST, in the answers `gh pr view` used to give.
7
8
  //
@@ -81,14 +82,31 @@ interface PullMergeState {
81
82
  is_merged: boolean
82
83
  merged_at: string | undefined
83
84
  head_sha: string | undefined
85
+ // `josh ship` reads `DIRTY` here to re-merge a conflicting pull request (joshuafolkken/kit#3221).
86
+ merge_state_status: string | undefined
84
87
  }
85
88
 
86
89
  type PullNumberRead =
87
90
  { kind: 'read'; pr_number: number } | { kind: 'missing' } | { kind: 'unreadable'; cause: unknown }
88
91
 
92
+ // The branch-keyed detail read, in the same three answers. `missing` and `unreadable` are the
93
+ // resolution's own; `unreadable` also covers a resolved number whose detail read failed, which every
94
+ // branch-keyed reader used to fold into "no pull request" (joshuafolkken/kit#3263).
95
+ type PullRead = Exclude<PullNumberRead, { kind: 'read' }> | { kind: 'read'; pull: RestPull }
96
+
89
97
  // The lookups still in flight, keyed the same way the resolved numbers are.
90
98
  const pending_pr_number_by_branch = new Map<string, Promise<PullNumberRead>>()
91
99
 
100
+ // The detail reads, keyed by number and remembered for the life of the command
101
+ // (joshuafolkken/kit#3263). `josh followup` asks `pr_get_body` and `pr_get_url` in one tick — the
102
+ // same `GET /pulls/{N}` each time. The promise is stored, so readers in one tick share the request as
103
+ // well as its answer; a read that fails is dropped and re-tried, the rule the number memo holds.
104
+ // **Every write to a pull request clears it** through `forget_pr_numbers`, and the two state readers
105
+ // never read it: `pr_get_merge_state` answers where the pull request stands *now*, which `josh ship`
106
+ // asks again after every merge it pushes, and `pr_view` decides whether `josh git -y` writes onto the
107
+ // pull request — after a commit and a push its preflight read would otherwise outlast.
108
+ const pull_by_number = new Map<number, Promise<RestPull>>()
109
+
92
110
  async function fetch_pr_number_read(branch_name: string): Promise<PullNumberRead> {
93
111
  try {
94
112
  const pr_number = await fetch_pr_number(branch_name)
@@ -127,10 +145,11 @@ async function start_pr_number_read(branch_name: string): Promise<PullNumberRead
127
145
  }
128
146
  }
129
147
 
130
- // Defined after the map it clears, so the two memos are declared before anything reaches them.
148
+ // Defined after the maps it clears, so the memos are declared before anything reaches them.
131
149
  function forget_pr_numbers(): void {
132
150
  pr_number_by_branch.clear()
133
151
  pending_pr_number_by_branch.clear()
152
+ pull_by_number.clear()
134
153
  }
135
154
 
136
155
  async function read_pr_number(branch_name: string): Promise<PullNumberRead> {
@@ -141,9 +160,9 @@ async function read_pr_number(branch_name: string): Promise<PullNumberRead> {
141
160
  }
142
161
 
143
162
  // `undefined` covers both "this branch has no pull request" and "the lookup failed", which is the
144
- // distinction `gh pr view` never made either — every *read* here folds them together on purpose:
145
- // `pr_view` answers the empty string either way, the comment readers `undefined`. The two callers
146
- // that act on the absence rather than displaying it — `require_pr_number` and `pr_exists` — opt out.
163
+ // distinction `gh pr view` never made either — the *display* reads here fold them together on
164
+ // purpose: the comment readers answer `undefined` either way. The callers that act on the absence
165
+ // rather than displaying it — `require_pr_number`, `pr_exists` and `pr_view` — opt out.
147
166
  async function resolve_pr_number(branch_name: string): Promise<number | undefined> {
148
167
  const read = await read_pr_number(branch_name)
149
168
 
@@ -183,19 +202,43 @@ async function read_pull(pr_number: number): Promise<RestPull> {
183
202
  return git_gh_pr_rest.parse_rest_pull(json)
184
203
  }
185
204
 
205
+ // The identity check keeps a failure from evicting a read that `forget_pr_numbers` already replaced.
206
+ async function read_remembered_pull(pr_number: number): Promise<RestPull> {
207
+ const pending = pull_by_number.get(pr_number) ?? read_pull(pr_number)
208
+
209
+ pull_by_number.set(pr_number, pending)
210
+
211
+ try {
212
+ return await pending
213
+ } catch (error) {
214
+ if (pull_by_number.get(pr_number) === pending) pull_by_number.delete(pr_number)
215
+ throw error
216
+ }
217
+ }
218
+
186
219
  // One pull request named by its branch: the resolution, then the detail read that carries
187
220
  // `mergeable` and `mergeable_state`, which the lookup listing does not.
188
- async function read_pull_of_branch(branch_name: string): Promise<RestPull | undefined> {
189
- const pr_number = await resolve_pr_number(branch_name)
190
- if (pr_number === undefined) return undefined
221
+ async function read_pull_of_branch(
222
+ branch_name: string,
223
+ read: (pr_number: number) => Promise<RestPull> = read_remembered_pull,
224
+ ): Promise<PullRead> {
225
+ const number_read = await read_pr_number(branch_name)
226
+ if (number_read.kind !== 'read') return number_read
191
227
 
192
228
  try {
193
- return await read_pull(pr_number)
194
- } catch {
195
- return undefined
229
+ return { kind: 'read', pull: await read(number_read.pr_number) }
230
+ } catch (error) {
231
+ return { kind: 'unreadable', cause: error }
196
232
  }
197
233
  }
198
234
 
235
+ // The display readers' fold: "no pull request" and "nothing could be read" are one empty answer.
236
+ async function read_pull_or_undefined(branch_name: string): Promise<RestPull | undefined> {
237
+ const read = await read_pull_of_branch(branch_name)
238
+
239
+ return read.kind === 'read' ? read.pull : undefined
240
+ }
241
+
199
242
  // **`false` means the branch has no pull request, and nothing else.** This is the second half of
200
243
  // joshuafolkken/kit#1048's distinction, on the caller that needs it most: `pr_exists` is not read for
201
244
  // display, it decides whether `git-pr.ts` **opens** a pull request. Folding an unreadable lookup into
@@ -215,7 +258,7 @@ async function pr_get_number(branch_name: string): Promise<number | undefined> {
215
258
  }
216
259
 
217
260
  async function pr_get_url(branch_name: string): Promise<string | undefined> {
218
- const pull = await read_pull_of_branch(branch_name)
261
+ const pull = await read_pull_or_undefined(branch_name)
219
262
  if (pull?.html_url === undefined) return undefined
220
263
 
221
264
  return git_gh_helpers.parse_pr_state_string(pull.html_url)
@@ -226,33 +269,39 @@ async function pr_get_url(branch_name: string): Promise<string | undefined> {
226
269
  // made on GitHub moves without touching the local branch. `undefined` covers both "no pull request"
227
270
  // and "nothing could be read": `followup` then takes its ordinary path, whose merge step settles a
228
271
  // pull request that did merge after all (`pr_merge`), so an unread answer costs a wait, not a wrong tail.
272
+ // Read past the detail memo: `josh ship` asks it again after each merge it pushes (joshuafolkken/kit#3263).
229
273
  async function pr_get_merge_state(branch_name: string): Promise<PullMergeState | undefined> {
230
- const pull = await read_pull_of_branch(branch_name)
231
- if (pull === undefined) return undefined
274
+ const read = await read_pull_of_branch(branch_name, read_pull)
275
+ if (read.kind !== 'read') return undefined
276
+ const { pull } = read
232
277
 
233
278
  return {
234
279
  is_merged: git_gh_pr_rest.is_merged(pull),
235
280
  merged_at: pull.merged_at ?? undefined,
236
281
  head_sha: pull.head?.sha,
282
+ merge_state_status: to_gh_state(pull.mergeable_state),
237
283
  }
238
284
  }
239
285
 
240
286
  // REST answers JSON null for a pull request with no body where `gh --json body` answered an empty
241
287
  // string, and both used to arrive here as the empty answer this folds to `undefined`.
242
288
  async function pr_get_body(branch_name: string): Promise<string | undefined> {
243
- const pull = await read_pull_of_branch(branch_name)
289
+ const pull = await read_pull_or_undefined(branch_name)
244
290
  const body = pull?.body
245
291
 
246
292
  return typeof body === 'string' && body.length > 0 ? body : undefined
247
293
  }
248
294
 
249
- // The empty string means "there is nothing to read" — a branch with no pull request, or a read that
250
- // failed. `git-pr.ts` checks the length before parsing.
295
+ // The empty string means "this branch has no pull request", and nothing else. A read that failed
296
+ // throws, carrying gh's reason as the `cause`: `git-pr.ts` decides from this state whether the
297
+ // branch's pull request merged, and a rate limit answered as "no state" sent a merged pull request
298
+ // down the open one's path (joshuafolkken/kit#3263). Read past the detail memo, like the merge state.
251
299
  async function pr_view(branch_name: string): Promise<string> {
252
- const pull = await read_pull_of_branch(branch_name)
253
- if (pull === undefined) return ''
300
+ const read = await read_pull_of_branch(branch_name, read_pull)
301
+ if (read.kind === 'unreadable') throw to_unreadable_error(branch_name, read.cause)
302
+ if (read.kind === 'missing') return ''
254
303
 
255
- return JSON.stringify(git_gh_pr_rest.to_pr_info(pull))
304
+ return JSON.stringify(git_gh_pr_rest.to_pr_info(read.pull))
256
305
  }
257
306
 
258
307
  // `undefined` when the listing could not be read — a failed request, or a PR whose number could not
@@ -1,8 +1,8 @@
1
+ import { rest_comment_schema } from '#scripts/git/git-schemas'
1
2
  import {
2
3
  parse_json_array_or_undefined,
3
4
  parse_json_object_safe,
4
5
  } from '#scripts/git/parse-json-array'
5
- import { rest_comment_schema } from '#scripts/git/schemas'
6
6
  import { z } from 'zod'
7
7
  import { MERGED_STATE, to_gh_state } from './git-gh-rest-state'
8
8
 
@@ -152,10 +152,12 @@ async function pr_comment(branch_name: string, body: string): Promise<string> {
152
152
 
153
153
  // Replaces the body of the branch's open pull request. `git -y` opens a pull request once, so a
154
154
  // body supplied on a later run — the live-execution evidence `followup` gates the merge on
155
- // (joshuafolkken/kit#2446) — reaches an already-open one only through this write.
155
+ // (joshuafolkken/kit#2446) — reaches an already-open one only through this write. The remembered
156
+ // detail read holds the old body, so it is cleared first (joshuafolkken/kit#3263).
156
157
  async function pr_update_body(branch_name: string, body: string): Promise<void> {
157
158
  const pr_number = await require_pr_number(branch_name)
158
159
 
160
+ forget_pr_numbers()
159
161
  await git_gh_exec.exec_gh_api({
160
162
  path: git_gh_api_path.pull_api_path(String(pr_number)),
161
163
  method: PATCH_METHOD,
@@ -163,7 +165,9 @@ async function pr_update_body(branch_name: string, body: string): Promise<void>
163
165
  })
164
166
  }
165
167
 
168
+ // A merge moves the remembered detail read's state, so it is cleared first (joshuafolkken/kit#3263).
166
169
  async function put_merge(pr_number: number): Promise<void> {
170
+ forget_pr_numbers()
167
171
  await git_gh_exec.exec_gh_api({
168
172
  path: git_gh_api_path.pull_merge_api_path(String(pr_number)),
169
173
  method: PUT_METHOD,
@@ -1,6 +1,6 @@
1
1
  // cspell:words coderabbit coderabbitai
2
+ import { ai_review_pull_comment_schema } from '#scripts/git/git-schemas'
2
3
  import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
3
- import { ai_review_pull_comment_schema } from '#scripts/git/schemas'
4
4
  import {
5
5
  classify_ai_review_comments,
6
6
  type ClassifiedFinding,
@@ -191,9 +191,7 @@ function evaluate_pr_state(snapshot: PrStateSnapshot): PrEvaluation {
191
191
 
192
192
  if (failure !== undefined) return failure
193
193
 
194
- if (is_mergeable_state(snapshot) && is_every_required_passing(statuses)) {
195
- return 'success'
196
- }
194
+ if (is_mergeable_state(snapshot) && is_every_required_passing(statuses)) return 'success'
197
195
 
198
196
  return 'pending'
199
197
  }
@@ -240,7 +238,14 @@ function is_auto_merge_blocked(snapshot: PrStateSnapshot): boolean {
240
238
  )
241
239
  }
242
240
 
241
+ // A failed wait whose cause is a merge conflict (joshuafolkken/kit#3221), so `followup` names its last
242
+ // lap `conflict` rather than `interrupted` and the cause is read from the stage block, not looked up.
243
+ class PrConflictError extends Error {
244
+ override name = 'PrConflictError'
245
+ }
246
+
243
247
  export {
248
+ PrConflictError,
244
249
  evaluate_pr_state,
245
250
  is_auto_merge_blocked,
246
251
  is_review_decision_decisive,
@@ -251,5 +256,6 @@ export {
251
256
  REQUIRED_CHECKS,
252
257
  CODERABBIT_CHECK_NAME,
253
258
  is_required_check,
259
+ is_merge_conflict,
254
260
  }
255
261
  export type { PrEvaluation }
@@ -1,4 +1,4 @@
1
- import { pr_raw_schema, type RollupItemData } from '#scripts/git/schemas'
1
+ import { pr_raw_schema, type RollupItemData } from '#scripts/git/git-schemas'
2
2
  import { json_value } from '#scripts/lib/json-value'
3
3
 
4
4
  const CHECK_STATUS_PASS = 'pass'
@@ -7,7 +7,6 @@ import {
7
7
  SECONDS_TO_MS,
8
8
  wait_for_pr_success,
9
9
  type PrStateEvaluator,
10
- type ReviewDecisionPredicate,
11
10
  } from './git-pr-checks'
12
11
  import { is_coderabbit_check, is_required_check, type PrEvaluation } from './git-pr-checks-eval'
13
12
  import {
@@ -116,12 +115,14 @@ function describe_checks_failure(snapshot: PrStateSnapshot): string {
116
115
  // **The watch never reads `review_decision`**, so the poll must not spend a request fetching it. It
117
116
  // is the same difference `evaluate_checks_settled` is written for, said to the fetcher instead of to
118
117
  // the verdict: three requests a poll rather than four (joshuafolkken/kit#1043).
119
- const SHOULD_NEVER_READ_REVIEW_DECISION: ReviewDecisionPredicate = () => false
118
+ function should_never_read_review_decision(): boolean {
119
+ return false
120
+ }
120
121
 
121
122
  const CHECKS_SETTLED_EVALUATOR: PrStateEvaluator = {
122
123
  evaluate: evaluate_checks_settled,
123
124
  describe: describe_checks_failure,
124
- should_read_review_decision: SHOULD_NEVER_READ_REVIEW_DECISION,
125
+ should_read_review_decision: should_never_read_review_decision,
125
126
  }
126
127
 
127
128
  async function watch_until_settled(branch_name: string): Promise<void> {
@@ -145,7 +146,7 @@ async function watch_until_settled(branch_name: string): Promise<void> {
145
146
  // from failing on the first read: two minutes is the grace, and an empty rollup at the end of them
146
147
  // is the answer `gh` gave immediately.
147
148
  async function fail_when_no_checks(branch_name: string): Promise<void> {
148
- const snapshot = await default_fetch_pr_state(branch_name, SHOULD_NEVER_READ_REVIEW_DECISION)
149
+ const snapshot = await default_fetch_pr_state(branch_name, should_never_read_review_decision)
149
150
  if (snapshot.rollup.length === 0) throw new Error(NO_CHECKS_MESSAGE)
150
151
  }
151
152
 
@@ -173,7 +174,7 @@ export {
173
174
  git_pr_checks_watch,
174
175
  fail_when_no_checks,
175
176
  CHECKS_SETTLED_EVALUATOR,
176
- SHOULD_NEVER_READ_REVIEW_DECISION,
177
+ should_never_read_review_decision,
177
178
  evaluate_checks_settled,
178
179
  is_awaited_check,
179
180
  describe_checks_failure,
@@ -1,4 +1,4 @@
1
- import { package_name_schema } from '#scripts/git/schemas'
1
+ import { package_name_schema } from '#scripts/git/git-schemas'
2
2
  import { poll } from '#scripts/lib/poll'
3
3
  import { git_gh_pr_auto_merge } from './git-gh-pr-auto-merge'
4
4
  import { git_gh_pr_snapshot } from './git-gh-pr-snapshot'
@@ -6,7 +6,9 @@ import {
6
6
  describe_pr_failure,
7
7
  evaluate_pr_state,
8
8
  is_auto_merge_blocked,
9
+ is_merge_conflict,
9
10
  is_review_decision_decisive,
11
+ PrConflictError,
10
12
  type PrEvaluation,
11
13
  } from './git-pr-checks-eval'
12
14
  import { parse_pr_state_snapshot, read_string, type PrStateSnapshot } from './git-pr-checks-parse'
@@ -85,7 +87,9 @@ type ReviewDecisionPredicate = (snapshot: PrStateSnapshot) => boolean
85
87
 
86
88
  // The conservative answer, and the one every evaluator that does not say gets: read it every poll,
87
89
  // which is what all of them did before the predicate existed.
88
- const SHOULD_ALWAYS_READ_REVIEW_DECISION: ReviewDecisionPredicate = () => true
90
+ function should_always_read_review_decision(): boolean {
91
+ return true
92
+ }
89
93
 
90
94
  type PrStateFetcher = (
91
95
  branch_name: string,
@@ -142,6 +146,12 @@ function advance_stable_count(previous: number, state: PrEvaluation): number {
142
146
  return state === 'success' ? previous + 1 : 0
143
147
  }
144
148
 
149
+ function failure_error(message: string, snapshot: PrStateSnapshot): Error {
150
+ return is_merge_conflict(snapshot.merge_state_status)
151
+ ? new PrConflictError(message)
152
+ : new Error(message)
153
+ }
154
+
145
155
  function classify_poll_result(input: {
146
156
  snapshot: PrStateSnapshot
147
157
  stable_count: number
@@ -150,7 +160,9 @@ function classify_poll_result(input: {
150
160
  }): { is_done: boolean; next_stable_count: number } {
151
161
  const state = input.evaluator.evaluate(input.snapshot)
152
162
 
153
- if (state === 'failure') throw new Error(input.evaluator.describe(input.snapshot))
163
+ if (state === 'failure') {
164
+ throw failure_error(input.evaluator.describe(input.snapshot), input.snapshot)
165
+ }
154
166
 
155
167
  const next_stable_count = advance_stable_count(input.stable_count, state)
156
168
 
@@ -189,7 +201,7 @@ async function read_pr_state(
189
201
  try {
190
202
  const snapshot = await input.options.fetcher(
191
203
  input.options.branch_name,
192
- evaluator.should_read_review_decision ?? SHOULD_ALWAYS_READ_REVIEW_DECISION,
204
+ evaluator.should_read_review_decision ?? should_always_read_review_decision,
193
205
  )
194
206
 
195
207
  return { kind: 'read', snapshot }
@@ -287,7 +299,7 @@ async function wait_for_pr_success(options: WaitForPrSuccessOptions): Promise<Pr
287
299
  // (joshuafolkken/kit#1043).
288
300
  async function default_fetch_pr_state(
289
301
  branch_name: string,
290
- should_read_review_decision: ReviewDecisionPredicate = SHOULD_ALWAYS_READ_REVIEW_DECISION,
302
+ should_read_review_decision: ReviewDecisionPredicate = should_always_read_review_decision,
291
303
  ): Promise<PrStateSnapshot> {
292
304
  const checks = await git_gh_pr_snapshot.pr_get_checks_snapshot(branch_name)
293
305
  const snapshot = parse_pr_state_snapshot(checks.snapshot_json)
@@ -375,7 +387,7 @@ export {
375
387
  is_pr_checks_timeout,
376
388
  default_fetch_pr_state,
377
389
  MERGE_GATE_EVALUATOR,
378
- SHOULD_ALWAYS_READ_REVIEW_DECISION,
390
+ should_always_read_review_decision,
379
391
  }
380
392
  export type { MergeProgress, PrStateFetcher, PrStateEvaluator, ReviewDecisionPredicate }
381
393
  export {
@@ -1,5 +1,5 @@
1
+ import { pull_comment_schema } from '#scripts/git/git-schemas'
1
2
  import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
2
- import { pull_comment_schema } from '#scripts/git/schemas'
3
3
  import { git_gh_command } from './git-gh-command'
4
4
  import { has_ignore_reason } from './git-pr-ai-review'
5
5
 
@@ -8,9 +8,7 @@ function has_error_cause(error: Error): boolean {
8
8
 
9
9
  function is_pr_already_exists_error(error: unknown): boolean {
10
10
  if (error instanceof Error) {
11
- if (is_pr_already_exists_message(error.message)) {
12
- return true
13
- }
11
+ if (is_pr_already_exists_message(error.message)) return true
14
12
 
15
13
  return has_error_cause(error)
16
14
  }
@@ -1,6 +1,6 @@
1
1
  import { pr_classification, type ReleaseClassification } from '#scripts/ci/pr-classification'
2
2
  import type { IssueInfo } from '#scripts/git/git-issue'
3
- import { pr_info_schema } from '#scripts/git/schemas'
3
+ import { pr_info_schema } from '#scripts/git/git-schemas'
4
4
  import { animation_helpers, type AnimationOptions } from '#scripts/lib/animation-helpers'
5
5
  import { git_gh_command } from './git-gh-command'
6
6
  import { git_pr_error } from './git-pr-error'
@@ -98,18 +98,14 @@ function parse_pr_state(pr_info_json: string): string | undefined {
98
98
  }
99
99
  }
100
100
 
101
- async function get_pr_state_safe(branch_name: string): Promise<string | undefined> {
102
- try {
103
- const pr_info_json = await git_gh_command.pr_view(branch_name)
101
+ // A read that failed throws rather than answering `undefined`: "no state" is read as "not merged",
102
+ // which would write onto a pull request that already merged (joshuafolkken/kit#3263).
103
+ async function get_pr_state(branch_name: string): Promise<string | undefined> {
104
+ const pr_info_json = await git_gh_command.pr_view(branch_name)
104
105
 
105
- if (pr_info_json.length === 0) {
106
- return undefined
107
- }
106
+ if (pr_info_json.length === 0) return undefined
108
107
 
109
- return parse_pr_state(pr_info_json)
110
- } catch {
111
- return undefined
112
- }
108
+ return parse_pr_state(pr_info_json)
113
109
  }
114
110
 
115
111
  function is_pr_state_merged(pr_state: string | undefined): boolean {
@@ -137,7 +133,7 @@ async function handle_existing_pr(
137
133
  branch_name: string,
138
134
  options: { label: ReleaseClassification; should_replace_body: boolean },
139
135
  ): Promise<void> {
140
- const pr_state_result = await get_pr_state_safe(branch_name)
136
+ const pr_state_result = await get_pr_state(branch_name)
141
137
 
142
138
  if (is_pr_state_merged(pr_state_result)) {
143
139
  git_pr_messages.display_merged_pr_message()
@@ -181,7 +177,7 @@ function build_body(issue_info: IssueInfo, extra_body?: string): string {
181
177
 
182
178
  async function existing_pr_label(branch_name: string): Promise<ReleaseClassification | undefined> {
183
179
  if (!(await git_gh_command.pr_exists(branch_name))) return undefined
184
- if (is_pr_state_merged(await get_pr_state_safe(branch_name))) return undefined
180
+ if (is_pr_state_merged(await get_pr_state(branch_name))) return undefined
185
181
 
186
182
  return await git_gh_command.pr_get_classification(branch_name)
187
183
  }
@@ -75,8 +75,8 @@ function build_target(match: RegExpExecArray): ParsedUrl {
75
75
  }
76
76
  }
77
77
 
78
- function parse_with(pattern: RegExp, url: string | undefined): ParsedUrl | undefined {
79
- const match = pattern.exec(url ?? '')
78
+ function parse_with(pattern: RegExp, url: string | undefined = ''): ParsedUrl | undefined {
79
+ const match = pattern.exec(url)
80
80
  if (match === null) return undefined
81
81
 
82
82
  return build_target(match)
@@ -12,19 +12,24 @@ const PORCELAIN_FLAG = '--porcelain'
12
12
  // The untracked-files mode every porcelain status reading passes (`git-command.ts` carries why it is
13
13
  // passed and why it is `all`), shared so another module's own reading cannot drift from it.
14
14
  const UNTRACKED_FILES_FLAG = '--untracked-files=all'
15
+ // The git binary a spawn runs in place of the platform's own (joshuafolkken/kit#3234). The spawn path
16
+ // is absolute, so a `git` placed in front on `PATH` never stands in for it: the unit suite's network
17
+ // guard shimmed `git` that way, and `josh ship`'s tests fetched and merged the default branch into the
18
+ // checkout they ran in without the guard ever seeing the call. The guard names its shim here instead.
19
+ const GIT_BINARY_KEY = 'JOSH_GIT_BINARY'
15
20
 
16
21
  function get_git_command(): string {
17
- if (platform() === 'win32') {
18
- return String.raw`"C:\Program Files\Git\cmd\git.exe"`
19
- }
22
+ if (platform() === 'win32') return String.raw`"C:\Program Files\Git\cmd\git.exe"`
20
23
 
21
24
  return GIT_COMMAND_UNIX
22
25
  }
23
26
 
24
27
  function get_git_command_for_spawn(): string {
25
- if (platform() === 'win32') {
26
- return String.raw`C:\Program Files\Git\cmd\git.exe`
27
- }
28
+ const binary = process.env[GIT_BINARY_KEY]
29
+
30
+ if (binary !== undefined && binary !== '') return binary
31
+
32
+ if (platform() === 'win32') return String.raw`C:\Program Files\Git\cmd\git.exe`
28
33
 
29
34
  return GIT_COMMAND_UNIX
30
35
  }
@@ -35,6 +40,7 @@ const git_utilities = {
35
40
  }
36
41
 
37
42
  export {
43
+ GIT_BINARY_KEY,
38
44
  REQUIRED_STATUS_LENGTH,
39
45
  STAGED_STATUS_INDEX,
40
46
  UNTRACKED_FILE_PREFIX,