@joshuafolkken/kit 1.1784.0 → 1.2116.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/.claude/agents/reviewer-verification.md +19 -0
  2. package/.claude/agents/reviewer.md +19 -0
  3. package/.claude/settings.json +5 -6
  4. package/.claude/skills/dependency-update/SKILL.md +9 -10
  5. package/.claude/skills/workflow-commands/SKILL.md +6 -6
  6. package/.claude/skills/workflow-commands/background-commands.md +8 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
  13. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  14. package/.claude/skills/workflow-commands/chain-rule.md +41 -32
  15. package/.claude/skills/workflow-commands/delegation.md +6 -6
  16. package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
  17. package/.claude/skills/workflow-commands/followup.md +7 -5
  18. package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
  19. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  20. package/.claude/skills/workflow-commands/issue-comments.md +9 -4
  21. package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +2 -2
  23. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  24. package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
  25. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
  26. package/.claude/skills/workflow-commands/prerequisite.md +3 -2
  27. package/.claude/skills/workflow-commands/retrospective.md +2 -2
  28. package/.claude/skills/workflow-commands/split-assessment.md +7 -5
  29. package/.codex/hooks.json +5 -5
  30. package/.github/actions/setup-pnpm/action.yml +9 -16
  31. package/.github/dependabot.yml +16 -0
  32. package/.github/workflows/auto-tag.yml +45 -0
  33. package/.github/workflows/ci.yml +155 -15
  34. package/.github/workflows/dependabot-auto-merge.yml +63 -4
  35. package/.github/workflows/production.yml +8 -0
  36. package/.github/workflows/publish.yml +60 -45
  37. package/.vscode/settings.json +19 -0
  38. package/CLAUDE.md +5 -12
  39. package/SECURITY.md +2 -2
  40. package/dist/CLAUDE.md +5 -12
  41. package/dist/commands/document-read-cli.js +2 -1
  42. package/dist/commands/document-section-main.js +2 -1
  43. package/dist/commands/read-set-cli.js +194 -134
  44. package/dist/commands/run-prep-main.js +9694 -10173
  45. package/dist/commands/run-step-cli.js +22138 -18285
  46. package/dist/hooks/audit-provision.js +23 -5
  47. package/dist/hooks/chunk-3GKE653K.js +6204 -0
  48. package/dist/hooks/chunk-45CJJOO7.js +1243 -0
  49. package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
  50. package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
  51. package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
  52. package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
  53. package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
  54. package/dist/hooks/chunk-VVNYJWEI.js +30 -0
  55. package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
  56. package/dist/hooks/codex-hook-adapter.js +8 -8
  57. package/dist/hooks/format-edited.js +4 -5
  58. package/dist/hooks/inputs.json +386 -360
  59. package/dist/hooks/pretool-guard.js +7 -6
  60. package/dist/hooks/session-lang.js +101 -258
  61. package/dist/hooks/stop-guard.js +1744 -2576
  62. package/dist/josh.js +7133 -6368
  63. package/dist/version/index.js +161 -1
  64. package/eslint/rules/comment-why-only.js +108 -0
  65. package/package.json +16 -9
  66. package/prompts/coding-standards.md +1 -1
  67. package/prompts/collaboration-workflow/file-edits.md +14 -11
  68. package/prompts/collaboration-workflow/issue-citation.md +0 -4
  69. package/prompts/collaboration-workflow/issue-template.md +40 -21
  70. package/prompts/collaboration-workflow/lane-instructions.md +3 -1
  71. package/prompts/collaboration-workflow/measurement.md +8 -1
  72. package/prompts/collaboration-workflow/operating-rules.md +11 -2
  73. package/prompts/collaboration-workflow/output-bounds.md +6 -2
  74. package/prompts/collaboration-workflow/plan-comment.md +1 -1
  75. package/prompts/collaboration-workflow/principles.md +9 -0
  76. package/prompts/collaboration-workflow/residency.md +3 -2
  77. package/prompts/collaboration-workflow/rule-delivery.md +13 -1
  78. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  79. package/prompts/collaboration-workflow/wip-cap.md +3 -1
  80. package/prompts/review-rubric.md +8 -0
  81. package/prompts/review.md +31 -22
  82. package/prompts/testing-guide.md +1 -0
  83. package/scripts/agent/agent-argv.ts +14 -16
  84. package/scripts/agent/agent-provider.ts +138 -0
  85. package/scripts/agent/agent-role-profile.ts +108 -156
  86. package/scripts/agent/agent-safe-text.ts +20 -5
  87. package/scripts/agent/api-usage-limit.ts +65 -0
  88. package/scripts/agent/claude-agent-argv.ts +19 -9
  89. package/scripts/agent/codex-agent-argv.ts +118 -22
  90. package/scripts/agent/usage-limit-reset.ts +179 -0
  91. package/scripts/auth/claude-token-output.ts +2 -2
  92. package/scripts/auto-ok/auto-ok-author.ts +53 -0
  93. package/scripts/auto-ok/auto-ok-cli.ts +20 -9
  94. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  95. package/scripts/backlog/backlog-drive-cli.ts +6 -1
  96. package/scripts/backlog/backlog-drive-hold.ts +3 -3
  97. package/scripts/backlog/backlog-drive-idle.ts +10 -0
  98. package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
  99. package/scripts/backlog/backlog-drive-launch.ts +8 -2
  100. package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
  101. package/scripts/backlog/backlog-drive-named.ts +28 -7
  102. package/scripts/backlog/backlog-drive-paused.ts +5 -4
  103. package/scripts/backlog/backlog-drive-restore.ts +11 -3
  104. package/scripts/backlog/backlog-drive.ts +21 -60
  105. package/scripts/backlog/backlog-gate-scope.ts +61 -0
  106. package/scripts/backlog/backlog-measure-close.ts +112 -0
  107. package/scripts/backlog/backlog-next.ts +39 -49
  108. package/scripts/backlog/backlog-occupied.ts +2 -2
  109. package/scripts/backlog/backlog-overlap.ts +42 -8
  110. package/scripts/backlog/backlog-plan-cli.ts +4 -1
  111. package/scripts/backlog/backlog-plan-comment.ts +81 -0
  112. package/scripts/backlog/backlog-plan-read.ts +19 -6
  113. package/scripts/backlog/backlog-pool.ts +50 -12
  114. package/scripts/backlog/backlog-rank.ts +60 -13
  115. package/scripts/backlog/backlog-stalled-detect.ts +2 -1
  116. package/scripts/backlog/backlog-waves.ts +17 -2
  117. package/scripts/backlog/backlogrun-launch.ts +7 -4
  118. package/scripts/cost-runtime/context-cut-payback.ts +27 -25
  119. package/scripts/cost-runtime/cost-cli.ts +37 -16
  120. package/scripts/cost-runtime/cost-pricing.ts +9 -0
  121. package/scripts/cost-runtime/cost-verdict.ts +4 -1
  122. package/scripts/delegation/delegation-policy.ts +2 -2
  123. package/scripts/delegation/duplicate-read-guard.ts +1 -0
  124. package/scripts/delegation/investigation-guard.ts +14 -2
  125. package/scripts/delegation/investigation-reads.ts +45 -23
  126. package/scripts/dependabot/dependabot-cooldown.ts +3 -2
  127. package/scripts/discovery/repo-party.ts +80 -20
  128. package/scripts/discovery/session-owner-pin.ts +51 -0
  129. package/scripts/document/capped-print.ts +1 -1
  130. package/scripts/document/command-reference-line-budget.ts +42 -0
  131. package/scripts/document/document-byte-budget.ts +17 -7
  132. package/scripts/document/document-checks.ts +20 -0
  133. package/scripts/document/document-ratchet-check.ts +101 -0
  134. package/scripts/document/document-section.ts +1 -0
  135. package/scripts/document/entry-read-budget.ts +7 -3
  136. package/scripts/document/issue-citation-budget.ts +94 -0
  137. package/scripts/epic/epic-cli-argv.ts +2 -2
  138. package/scripts/epic/epic-fetch.ts +4 -2
  139. package/scripts/epic/epic-graph.ts +7 -2
  140. package/scripts/epic/epic-issue.ts +3 -1
  141. package/scripts/epic/epic-rank.ts +5 -0
  142. package/scripts/exports/unused-members-check.ts +62 -0
  143. package/scripts/exports/unused-members-cli.ts +6 -51
  144. package/scripts/followup/followup-ci-path.ts +67 -0
  145. package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
  146. package/scripts/followup/followup-coderabbit-wait.ts +13 -10
  147. package/scripts/followup/followup-failed-tests.ts +160 -0
  148. package/scripts/followup/followup-job-needs.ts +100 -0
  149. package/scripts/followup/followup-job-rerun.ts +303 -0
  150. package/scripts/followup/followup-main-move.ts +113 -0
  151. package/scripts/followup/git-followup-finish.ts +43 -11
  152. package/scripts/followup/git-followup-pending.ts +3 -3
  153. package/scripts/followup/git-followup-stages.ts +41 -11
  154. package/scripts/followup/git-pr-followup-checks.ts +55 -16
  155. package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
  156. package/scripts/followup/git-pr-followup.ts +62 -27
  157. package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
  158. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
  159. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
  160. package/scripts/gate/busy-average.ts +34 -2
  161. package/scripts/gate/check-related-pinned.ts +43 -0
  162. package/scripts/gate/check-related-reuse.ts +64 -0
  163. package/scripts/gate/check-related-section.ts +57 -0
  164. package/scripts/gate/check-related-unplanned.ts +34 -0
  165. package/scripts/gate/check-related.ts +265 -0
  166. package/scripts/gate/command-memory.ts +1 -1
  167. package/scripts/gate/core-admission.ts +24 -6
  168. package/scripts/gate/core-budget-timing.ts +20 -6
  169. package/scripts/gate/core-budget.ts +40 -11
  170. package/scripts/gate/core-wait-record.ts +3 -2
  171. package/scripts/gate/core-wait.ts +25 -2
  172. package/scripts/gate/core-weights.ts +15 -7
  173. package/scripts/gate/gate-labels.ts +46 -0
  174. package/scripts/gate/gate-ledger.ts +14 -6
  175. package/scripts/gate/gate-plan.ts +103 -58
  176. package/scripts/gate/gate-report.ts +9 -7
  177. package/scripts/gate/gate-run-steps.ts +26 -3
  178. package/scripts/gate/gate-skip.ts +45 -6
  179. package/scripts/gate/gate-static-group.ts +60 -0
  180. package/scripts/gate/gate-steps.ts +2 -1
  181. package/scripts/gate/gate-tree.ts +4 -2
  182. package/scripts/gate/hook-gate-reuse.ts +19 -2
  183. package/scripts/gate/metrics-totals.ts +59 -0
  184. package/scripts/gate/scoped-green.ts +68 -15
  185. package/scripts/gate/since-green.ts +14 -6
  186. package/scripts/gate/structural-checks.ts +96 -0
  187. package/scripts/gate/verification-gate.ts +51 -16
  188. package/scripts/gate/verification-inputs.ts +21 -6
  189. package/scripts/gate/verification-tail.ts +52 -0
  190. package/scripts/gh/gh-author-association.ts +23 -2
  191. package/scripts/gh/git-ai-review-scan.ts +1 -1
  192. package/scripts/gh/git-gh-api-path.ts +12 -3
  193. package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
  194. package/scripts/gh/git-gh-exec.ts +32 -2
  195. package/scripts/gh/git-gh-issue-read.ts +4 -2
  196. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  197. package/scripts/gh/git-gh-issue-write.ts +8 -2
  198. package/scripts/gh/git-gh-issue.ts +12 -5
  199. package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
  200. package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
  201. package/scripts/gh/git-gh-pr-read.ts +74 -13
  202. package/scripts/gh/git-gh-pr-rest.ts +3 -1
  203. package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
  204. package/scripts/gh/git-gh-pr-too-large.ts +20 -0
  205. package/scripts/gh/git-gh-pr.ts +21 -7
  206. package/scripts/gh/git-gh-retry.ts +141 -0
  207. package/scripts/gh/git-pr-ai-review.ts +15 -6
  208. package/scripts/gh/git-pr-checks-eval.ts +3 -0
  209. package/scripts/gh/git-pr-checks.ts +5 -5
  210. package/scripts/git/base-tree.ts +30 -0
  211. package/scripts/git/change-base.ts +4 -10
  212. package/scripts/git/changed-paths-sync.ts +63 -0
  213. package/scripts/git/changed-paths.ts +16 -1
  214. package/scripts/git/git-common-directory.ts +11 -1
  215. package/scripts/git/git-diff-reads.ts +66 -20
  216. package/scripts/git/git-error.ts +9 -2
  217. package/scripts/git/git-schemas.ts +13 -0
  218. package/scripts/git/git-worktree.ts +13 -0
  219. package/scripts/git/gone-branch.ts +4 -1
  220. package/scripts/git/main-merge-budget-conflict.ts +89 -0
  221. package/scripts/git/main-merge-guard.ts +55 -18
  222. package/scripts/git/main-merge-regenerate.ts +59 -25
  223. package/scripts/git/main-merge.ts +53 -5
  224. package/scripts/git/no-base-checkout.ts +127 -0
  225. package/scripts/git/stash/git-stash.ts +11 -2
  226. package/scripts/git/stash/stash-push-cli.ts +10 -0
  227. package/scripts/hooks/batch-guard.ts +40 -21
  228. package/scripts/hooks/edited-cspell.ts +37 -11
  229. package/scripts/hooks/format-edited-file.ts +22 -2
  230. package/scripts/hooks/lane-plan-guard.ts +50 -0
  231. package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
  232. package/scripts/hooks/pre-commit-lint.ts +14 -1
  233. package/scripts/hooks/pre-push-related-switch.ts +10 -0
  234. package/scripts/hooks/pre-push-unit.ts +76 -10
  235. package/scripts/hooks/pretool-guard.ts +2 -0
  236. package/scripts/hooks/reserved-run.ts +14 -1
  237. package/scripts/hooks/run-hook.sh +23 -8
  238. package/scripts/hooks/step-zero-notice.ts +6 -1
  239. package/scripts/hooks/stop-actions.ts +3 -15
  240. package/scripts/hooks/stop-guard.ts +7 -6
  241. package/scripts/init/distributed-paths.ts +1 -0
  242. package/scripts/init/hook-command-rewrite.ts +5 -1
  243. package/scripts/init/hook-launch.ts +4 -2
  244. package/scripts/issue/aggregation-split.ts +52 -0
  245. package/scripts/issue/defect-origin-line.ts +19 -1
  246. package/scripts/issue/defect-rate-cli.ts +5 -102
  247. package/scripts/issue/defect-report.ts +4 -4
  248. package/scripts/issue/effect-audit-cli.ts +178 -0
  249. package/scripts/issue/effect-audit.ts +185 -0
  250. package/scripts/issue/expected-effect-lint.ts +64 -9
  251. package/scripts/issue/git-next-issues.ts +5 -0
  252. package/scripts/issue/issue-body-or-comment.ts +38 -4
  253. package/scripts/issue/issue-cite-cli.ts +1 -3
  254. package/scripts/issue/issue-classification.ts +1 -0
  255. package/scripts/issue/issue-closing-pr.ts +8 -1
  256. package/scripts/issue/issue-evidence-exit.ts +4 -2
  257. package/scripts/issue/issue-file-cap.ts +58 -0
  258. package/scripts/issue/issue-file-cli.ts +111 -38
  259. package/scripts/issue/issue-file-deferral.ts +52 -0
  260. package/scripts/issue/issue-file-fold.ts +29 -10
  261. package/scripts/issue/issue-file-interrupt.ts +85 -0
  262. package/scripts/issue/issue-file-lane-hint.ts +30 -0
  263. package/scripts/issue/issue-file.ts +43 -6
  264. package/scripts/issue/issue-labels.ts +19 -2
  265. package/scripts/issue/issue-lint-cli.ts +27 -2
  266. package/scripts/issue/issue-measured-cli.ts +13 -35
  267. package/scripts/issue/issue-measured-close.ts +58 -0
  268. package/scripts/issue/issue-not-before-label.ts +2 -2
  269. package/scripts/issue/issue-not-before.ts +4 -1
  270. package/scripts/issue/issue-priority-cli.ts +60 -0
  271. package/scripts/issue/issue-priority-raise.ts +55 -0
  272. package/scripts/issue/issue-priority.ts +174 -43
  273. package/scripts/issue/issue-rank.ts +21 -4
  274. package/scripts/issue/issue-read-cli.ts +32 -6
  275. package/scripts/issue/issue-read.ts +37 -12
  276. package/scripts/issue/issue-sample-gate.ts +95 -74
  277. package/scripts/issue/issue-sample-prime.ts +127 -0
  278. package/scripts/issue/issue-sample-required.ts +57 -0
  279. package/scripts/issue/issue-sample-source.ts +12 -11
  280. package/scripts/issue/issue-search.ts +124 -0
  281. package/scripts/issue/issue-state.ts +13 -1
  282. package/scripts/issue/issue-template-section.ts +43 -0
  283. package/scripts/issue/issue-watch-diff.ts +31 -1
  284. package/scripts/issue/issue-watch-hook.ts +11 -2
  285. package/scripts/issue/issue-watch.ts +55 -12
  286. package/scripts/issue/markdown-section.ts +1 -0
  287. package/scripts/issue/measure-declaration.ts +121 -0
  288. package/scripts/issue/measure-rerun-cli.ts +7 -70
  289. package/scripts/issue/plan-consumers-section.ts +22 -1
  290. package/scripts/issue/reproduction-measure.ts +3 -1
  291. package/scripts/issue/size-estimate.ts +90 -0
  292. package/scripts/issue/trusted-measure.ts +84 -0
  293. package/scripts/josh/command-usage.ts +3 -2
  294. package/scripts/josh/durable-state-migrate.ts +105 -0
  295. package/scripts/josh/durable-state.ts +112 -0
  296. package/scripts/josh/josh-aliases.ts +95 -0
  297. package/scripts/josh/josh-command-map.ts +1 -82
  298. package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
  299. package/scripts/josh/josh-commands-ai.ts +5 -5
  300. package/scripts/josh/josh-commands-development.ts +59 -58
  301. package/scripts/josh/josh-commands-issue-write.ts +23 -4
  302. package/scripts/josh/josh-commands-issue.ts +32 -0
  303. package/scripts/josh/josh-commands-lane.ts +87 -4
  304. package/scripts/josh/josh-commands-lint.ts +16 -3
  305. package/scripts/josh/josh-commands-maintenance.ts +15 -0
  306. package/scripts/josh/josh-commands-review.ts +41 -6
  307. package/scripts/josh/josh-commands-ship.ts +56 -8
  308. package/scripts/josh/josh-commands-test-check.ts +80 -0
  309. package/scripts/josh/josh-commands-workflow.ts +1 -1
  310. package/scripts/josh/josh-environment-file.ts +43 -3
  311. package/scripts/josh/josh-in-process.ts +2 -2
  312. package/scripts/josh/josh-logic.ts +5 -3
  313. package/scripts/josh/josh-run.ts +19 -1
  314. package/scripts/josh/run-record-path.ts +15 -0
  315. package/scripts/josh/stamp-file.ts +11 -2
  316. package/scripts/lane/lane-adopt-cli.ts +98 -0
  317. package/scripts/lane/lane-adopt.ts +208 -0
  318. package/scripts/lane/lane-await.ts +5 -8
  319. package/scripts/lane/lane-baseline.ts +123 -0
  320. package/scripts/lane/lane-breakdown-cli.ts +64 -0
  321. package/scripts/lane/lane-breakdown-implement.ts +170 -0
  322. package/scripts/lane/lane-breakdown-laps.ts +107 -0
  323. package/scripts/lane/lane-breakdown-pinned.ts +76 -0
  324. package/scripts/lane/lane-breakdown-text.ts +145 -0
  325. package/scripts/lane/lane-breakdown.ts +239 -0
  326. package/scripts/lane/lane-check-stats.ts +29 -8
  327. package/scripts/lane/lane-child-invocation.ts +9 -2
  328. package/scripts/lane/lane-child-mark-keys.ts +12 -0
  329. package/scripts/lane/lane-child-marker.ts +20 -1
  330. package/scripts/lane/lane-ci-path-stats.ts +73 -0
  331. package/scripts/lane/lane-claim-stats.ts +84 -0
  332. package/scripts/lane/lane-close.ts +8 -3
  333. package/scripts/lane/lane-dispatch-log.ts +3 -3
  334. package/scripts/lane/lane-dispatch.ts +2 -2
  335. package/scripts/lane/lane-gap-stats.ts +7 -6
  336. package/scripts/lane/lane-guard-policy.ts +7 -1
  337. package/scripts/lane/lane-implement-stats.ts +207 -0
  338. package/scripts/lane/lane-implement-subagents.ts +90 -0
  339. package/scripts/lane/lane-investigation-switch.ts +16 -0
  340. package/scripts/lane/lane-ledger-kind.ts +31 -0
  341. package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
  342. package/scripts/lane/lane-ledger-schema.ts +119 -36
  343. package/scripts/lane/lane-ledger-write.ts +257 -0
  344. package/scripts/lane/lane-ledger.ts +31 -177
  345. package/scripts/lane/lane-load.ts +1 -1
  346. package/scripts/lane/lane-measure-cli.ts +176 -59
  347. package/scripts/lane/lane-net-read.ts +44 -0
  348. package/scripts/lane/lane-net-time.ts +212 -0
  349. package/scripts/lane/lane-open.ts +16 -7
  350. package/scripts/lane/lane-output.ts +2 -4
  351. package/scripts/lane/lane-overrun-prompt.ts +147 -0
  352. package/scripts/lane/lane-overrun.ts +274 -0
  353. package/scripts/lane/lane-paths.ts +24 -1
  354. package/scripts/lane/lane-pause-cli.ts +4 -1
  355. package/scripts/lane/lane-paused-spans.ts +97 -0
  356. package/scripts/lane/lane-plan-check.ts +207 -0
  357. package/scripts/lane/lane-plan-cli.ts +41 -0
  358. package/scripts/lane/lane-plan.ts +144 -0
  359. package/scripts/lane/lane-registry.ts +14 -1
  360. package/scripts/lane/lane-relaunch.ts +1 -1
  361. package/scripts/lane/lane-round-one-stats.ts +51 -0
  362. package/scripts/lane/lane-sampler.ts +23 -4
  363. package/scripts/lane/lane-session-action.ts +93 -18
  364. package/scripts/lane/lane-session-broker.ts +49 -21
  365. package/scripts/lane/lane-session-reread-main.ts +14 -0
  366. package/scripts/lane/lane-session-reread.ts +51 -0
  367. package/scripts/lane/lane-ship-lap-stats.ts +74 -0
  368. package/scripts/lane/lane-sleep.ts +58 -0
  369. package/scripts/lane/lane-spent.ts +1 -1
  370. package/scripts/lane/lane-split-stats.ts +28 -0
  371. package/scripts/lane/lane-stage-stats.ts +13 -1
  372. package/scripts/lane/lane-state.ts +41 -9
  373. package/scripts/lane/lane-stats-tables.ts +58 -0
  374. package/scripts/lane/lane-stats.ts +21 -5
  375. package/scripts/lane/lane-stop-stats.ts +51 -19
  376. package/scripts/lane/lane-tail-stats.ts +32 -0
  377. package/scripts/lane/lane-transcript-calls.ts +67 -10
  378. package/scripts/lane/lane-transcript-stats.ts +44 -12
  379. package/scripts/lane/lane-value-cell.ts +72 -0
  380. package/scripts/lane/lane-value.ts +118 -0
  381. package/scripts/lane/lane-withdraw-closed.ts +124 -0
  382. package/scripts/lane/lane-withdraw.ts +108 -31
  383. package/scripts/lane/lane-yield.ts +85 -0
  384. package/scripts/lane/openai-lane-ship.ts +12 -11
  385. package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
  386. package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
  387. package/scripts/lane/openai-lane-supervisor.ts +5 -4
  388. package/scripts/lib/poll.ts +38 -9
  389. package/scripts/lib/settled-together.ts +10 -0
  390. package/scripts/lib/shell-quote.ts +21 -0
  391. package/scripts/lib/token-shape.ts +111 -0
  392. package/scripts/lint/eslint-daemon-sources.ts +79 -0
  393. package/scripts/lint/eslint-daemon.ts +45 -6
  394. package/scripts/lint/eslint-warm-cli.ts +7 -0
  395. package/scripts/lint/eslint-warm.ts +66 -0
  396. package/scripts/lint/import-graph.ts +141 -11
  397. package/scripts/lint/lint-gate-scope.ts +2 -2
  398. package/scripts/lint/lint-parallel.ts +54 -6
  399. package/scripts/lint/lint-related.ts +154 -33
  400. package/scripts/lint/rename-cli.ts +179 -0
  401. package/scripts/lint/rename-identifier.ts +192 -0
  402. package/scripts/lint/type-only-imports.ts +58 -0
  403. package/scripts/notify/telegram-notify.ts +12 -3
  404. package/scripts/observations/observation-record.ts +42 -16
  405. package/scripts/observations/observations-flush.ts +32 -9
  406. package/scripts/overrides/overrides-check.ts +9 -1
  407. package/scripts/release/release-approval-prune.ts +7 -1
  408. package/scripts/repo/required-checks-logic.ts +4 -1
  409. package/scripts/repo/ruleset-check.ts +4 -3
  410. package/scripts/report/markdown-shape.ts +28 -0
  411. package/scripts/report/report-format-reference.ts +15 -1
  412. package/scripts/report/report-labels.ts +12 -1
  413. package/scripts/report/report-lint-child-summary.ts +94 -0
  414. package/scripts/report/report-lint-cli.ts +26 -7
  415. package/scripts/report/report-lint.ts +2 -20
  416. package/scripts/review/prose-stale-cli.ts +88 -0
  417. package/scripts/review/prose-stale-scan.ts +152 -0
  418. package/scripts/review/review-attest.ts +73 -23
  419. package/scripts/review/review-brief-cli.ts +68 -19
  420. package/scripts/review/review-brief-paths.ts +22 -0
  421. package/scripts/review/review-brief.ts +14 -7
  422. package/scripts/review/review-comment-carry.ts +47 -0
  423. package/scripts/review/review-comment-round.ts +85 -22
  424. package/scripts/review/review-finding-ledger.ts +109 -7
  425. package/scripts/review/review-finding-text.ts +43 -0
  426. package/scripts/review/review-interaction.ts +11 -0
  427. package/scripts/review/review-lane-handover.ts +68 -0
  428. package/scripts/review/review-lint-cli.ts +57 -0
  429. package/scripts/review/review-lint-findings.ts +92 -0
  430. package/scripts/review/review-lint-sections.ts +80 -0
  431. package/scripts/review/review-lint.ts +221 -0
  432. package/scripts/review/review-prior-findings.ts +102 -0
  433. package/scripts/review/review-readers-scan.ts +4 -2
  434. package/scripts/review/review-record-cli.ts +177 -54
  435. package/scripts/review/review-record-code-round.ts +55 -0
  436. package/scripts/review/review-record-comment.ts +60 -0
  437. package/scripts/review/review-record-interaction.ts +35 -0
  438. package/scripts/review/review-record-pinned.ts +108 -0
  439. package/scripts/review/review-record.ts +25 -1
  440. package/scripts/review/review-reviewed-tree.ts +307 -0
  441. package/scripts/review/review-round-one-closure.ts +62 -0
  442. package/scripts/review/review-round-reuse.ts +106 -0
  443. package/scripts/review/review-round2-cli.ts +91 -29
  444. package/scripts/review/review-round2-skip-comment.ts +55 -0
  445. package/scripts/review/review-round2.ts +24 -8
  446. package/scripts/review/review-stamps.ts +7 -3
  447. package/scripts/review/review-stop-log.ts +20 -6
  448. package/scripts/review/review-stops-cli.ts +2 -1
  449. package/scripts/review/review-submit-cli.ts +101 -0
  450. package/scripts/rules/agent-model.ts +1 -1
  451. package/scripts/rules/bash-triggers.ts +3 -3
  452. package/scripts/rules/concise-reply.ts +88 -0
  453. package/scripts/rules/credential-body.ts +77 -0
  454. package/scripts/rules/decision-oracle.ts +1 -1
  455. package/scripts/rules/delivered-rules.ts +36 -17
  456. package/scripts/rules/destructive-command.ts +3 -11
  457. package/scripts/rules/direct-issue-comment.ts +34 -12
  458. package/scripts/rules/direct-issue-read.ts +191 -0
  459. package/scripts/rules/direct-pr-create.ts +4 -24
  460. package/scripts/rules/early-heartbeat.ts +2 -0
  461. package/scripts/rules/edit-tool.ts +27 -0
  462. package/scripts/rules/file-edit-rows.ts +17 -0
  463. package/scripts/rules/file-rewrite.ts +146 -0
  464. package/scripts/rules/firing-point.ts +3 -2
  465. package/scripts/rules/gh-api.ts +128 -7
  466. package/scripts/rules/gh-repo-selector.ts +84 -0
  467. package/scripts/rules/git-force.ts +2 -2
  468. package/scripts/rules/guard-remedy.ts +14 -5
  469. package/scripts/rules/implementation-cut.ts +61 -28
  470. package/scripts/rules/investigator-foreground.ts +231 -0
  471. package/scripts/rules/issue-citation.ts +15 -60
  472. package/scripts/rules/issue-comments.ts +9 -4
  473. package/scripts/rules/josh-git-bare.ts +1 -1
  474. package/scripts/rules/lane-background.ts +41 -13
  475. package/scripts/rules/lane-direct-tool.ts +107 -9
  476. package/scripts/rules/lane-interactive-ask.ts +4 -1
  477. package/scripts/rules/lane-park.ts +4 -2
  478. package/scripts/rules/lane-spilled-read.ts +90 -0
  479. package/scripts/rules/lane-split-park.ts +1 -1
  480. package/scripts/rules/lane-switch-main.ts +4 -4
  481. package/scripts/rules/last-prompt.ts +20 -47
  482. package/scripts/rules/oracle-consulted.ts +1 -1
  483. package/scripts/rules/permission-guards.ts +5 -1
  484. package/scripts/rules/piped-verification-shapes.ts +110 -0
  485. package/scripts/rules/piped-verification.ts +67 -17
  486. package/scripts/rules/poll-loop.ts +72 -9
  487. package/scripts/rules/pr-merge.ts +5 -5
  488. package/scripts/rules/pre-gate-cut.ts +3 -3
  489. package/scripts/rules/protected-files.ts +88 -24
  490. package/scripts/rules/reply-language.ts +3 -17
  491. package/scripts/rules/reply-prose.ts +34 -0
  492. package/scripts/rules/repo-admin.ts +132 -0
  493. package/scripts/rules/reviewer-foreground.ts +88 -0
  494. package/scripts/rules/round-one-check-related.ts +120 -0
  495. package/scripts/rules/rule-body-guard.ts +5 -15
  496. package/scripts/rules/rule-value-cache.ts +53 -5
  497. package/scripts/rules/rule-value-candidates.ts +84 -0
  498. package/scripts/rules/rule-value-cli.ts +59 -11
  499. package/scripts/rules/rule-value.ts +17 -4
  500. package/scripts/rules/run-tail-rule.ts +1 -1
  501. package/scripts/rules/sed-in-place.ts +196 -0
  502. package/scripts/rules/shell-segments.ts +10 -2
  503. package/scripts/rules/stop-rule-rows.ts +1 -13
  504. package/scripts/rules/stop-rules.ts +33 -58
  505. package/scripts/rules/test-declared-commit.ts +1 -1
  506. package/scripts/rules/third-party-write.ts +110 -23
  507. package/scripts/rules/timed-rerun.ts +1 -1
  508. package/scripts/rules/turn-filings.ts +90 -0
  509. package/scripts/rules/worktree-guard.ts +22 -7
  510. package/scripts/run/board/run-board-ending-reason.ts +6 -2
  511. package/scripts/run/board/run-board-fit.ts +24 -1
  512. package/scripts/run/board/run-board-found.ts +134 -0
  513. package/scripts/run/board/run-board-header.ts +8 -0
  514. package/scripts/run/board/run-board-labels.ts +11 -1
  515. package/scripts/run/board/run-board-lane-time.ts +1 -0
  516. package/scripts/run/board/run-board-link.ts +15 -2
  517. package/scripts/run/board/run-board-notes.ts +7 -1
  518. package/scripts/run/board/run-board-operation.ts +4 -2
  519. package/scripts/run/board/run-board-phase.ts +2 -5
  520. package/scripts/run/board/run-board-plan.ts +1 -1
  521. package/scripts/run/board/run-board-read.ts +46 -1
  522. package/scripts/run/board/run-board-reason.ts +20 -8
  523. package/scripts/run/board/run-board-render-checks.ts +3 -2
  524. package/scripts/run/board/run-board-render-drawn.ts +11 -0
  525. package/scripts/run/board/run-board-render-found.ts +157 -0
  526. package/scripts/run/board/run-board-render-lead.ts +9 -0
  527. package/scripts/run/board/run-board-render-legend.ts +19 -0
  528. package/scripts/run/board/run-board-render-tail.ts +1 -1
  529. package/scripts/run/board/run-board-render.ts +82 -18
  530. package/scripts/run/board/run-board-spin.ts +1 -3
  531. package/scripts/run/board/run-board-status.ts +2 -0
  532. package/scripts/run/board/run-board-tick.ts +39 -10
  533. package/scripts/run/carry/run-carry-ended.ts +3 -2
  534. package/scripts/run/carry/run-carry.ts +3 -2
  535. package/scripts/run/cut/run-cut-cli.ts +33 -133
  536. package/scripts/run/cut/run-cut-digest.ts +152 -0
  537. package/scripts/run/cut/run-cut-handoff.ts +52 -8
  538. package/scripts/run/cut/run-cut-report.ts +24 -3
  539. package/scripts/run/cut/run-cut-resume.ts +129 -0
  540. package/scripts/run/cut/run-cut-review.ts +105 -0
  541. package/scripts/run/cut/run-cut.ts +18 -7
  542. package/scripts/run/detached-launch.ts +1 -1
  543. package/scripts/run/entry/run-entry-cli.ts +88 -19
  544. package/scripts/run/entry/run-entry-spill.ts +83 -0
  545. package/scripts/run/entry/run-entry-stop.ts +5 -1
  546. package/scripts/run/entry/run-entry-trust.ts +91 -0
  547. package/scripts/run/event/run-event-baseline.ts +56 -0
  548. package/scripts/run/event/run-event-filed.ts +9 -3
  549. package/scripts/run/event/run-event-overrun.ts +197 -0
  550. package/scripts/run/event/run-event-render.ts +4 -0
  551. package/scripts/run/event/run-event-stream.ts +25 -3
  552. package/scripts/run/hold/run-stop-notify.ts +3 -8
  553. package/scripts/run/merge/run-merge-cap.ts +57 -0
  554. package/scripts/run/merge/run-merge-cli.ts +66 -23
  555. package/scripts/run/merge/run-merge-outcome.ts +2 -0
  556. package/scripts/run/merge/run-merge-ship.ts +13 -3
  557. package/scripts/run/merge/run-merge-signals.ts +40 -23
  558. package/scripts/run/merge/run-merge-steps.ts +56 -36
  559. package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
  560. package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
  561. package/scripts/run/merge/run-merge.ts +41 -14
  562. package/scripts/run/review/run-review-cli.ts +306 -20
  563. package/scripts/run/review/run-review-early-push.ts +121 -0
  564. package/scripts/run/review/run-review-join-read.ts +94 -0
  565. package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
  566. package/scripts/run/review/run-review-pinned-join.ts +216 -0
  567. package/scripts/run/review/run-review-pinned-steps.ts +383 -0
  568. package/scripts/run/review/run-review-pinned-tree.ts +37 -0
  569. package/scripts/run/review/run-review-pinned.ts +53 -0
  570. package/scripts/run/review/run-review-steps.ts +70 -34
  571. package/scripts/run/review/run-review.ts +26 -6
  572. package/scripts/run/run-parent-cut-hook.ts +3 -4
  573. package/scripts/run/run-prep.ts +1 -0
  574. package/scripts/run/run-stage-read.ts +2 -6
  575. package/scripts/run/run-step-cli.ts +17 -5
  576. package/scripts/run/run-step-lane-plan.ts +27 -0
  577. package/scripts/run/run-step-section.ts +26 -7
  578. package/scripts/run/run-step.ts +5 -2
  579. package/scripts/run/ship/run-ship-args.ts +19 -0
  580. package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
  581. package/scripts/run/ship/run-ship-cli.ts +131 -39
  582. package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
  583. package/scripts/run/ship/run-ship-detach.ts +10 -4
  584. package/scripts/run/ship/run-ship-handover.ts +106 -0
  585. package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
  586. package/scripts/run/ship/run-ship-lap.ts +63 -0
  587. package/scripts/run/ship/run-ship-next.ts +27 -26
  588. package/scripts/run/ship/run-ship-overlap.ts +8 -3
  589. package/scripts/run/ship/run-ship-place.ts +9 -2
  590. package/scripts/run/ship/run-ship-planned.ts +92 -0
  591. package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
  592. package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
  593. package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
  594. package/scripts/run/ship/run-ship-preflight.ts +16 -4
  595. package/scripts/run/ship/run-ship-probe.ts +23 -9
  596. package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
  597. package/scripts/run/ship/run-ship-relaunch.ts +51 -0
  598. package/scripts/run/ship/run-ship-return.ts +103 -29
  599. package/scripts/run/ship/run-ship-review-reason.ts +24 -0
  600. package/scripts/run/ship/run-ship-review-steps.ts +230 -59
  601. package/scripts/run/ship/run-ship-review.ts +171 -51
  602. package/scripts/run/ship/run-ship-reviewer.ts +138 -42
  603. package/scripts/run/ship/run-ship-scoped.ts +39 -10
  604. package/scripts/run/ship/run-ship-stage.ts +49 -13
  605. package/scripts/run/ship/run-ship-steps.ts +24 -4
  606. package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
  607. package/scripts/run/ship/run-ship-stop-context.ts +50 -13
  608. package/scripts/run/ship/run-ship-sync.ts +171 -30
  609. package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
  610. package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
  611. package/scripts/run/ship/run-ship-waiter.ts +114 -0
  612. package/scripts/run/ship/run-ship.ts +16 -2
  613. package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
  614. package/scripts/run/wake/run-wake-cli.ts +6 -113
  615. package/scripts/run/wake/run-wake-code.ts +53 -11
  616. package/scripts/run/wake/run-wake-driver.ts +5 -3
  617. package/scripts/run/wake/run-wake-finish.ts +137 -0
  618. package/scripts/run/wake/run-wake-loop.ts +5 -3
  619. package/scripts/run/wake/run-wake-ports.ts +11 -3
  620. package/scripts/run/wake/run-wake-session.ts +2 -0
  621. package/scripts/run/wake/run-wake.ts +3 -2
  622. package/scripts/safe-chain/ci-installer-pin.ts +49 -8
  623. package/scripts/safe-chain/project-config.ts +3 -2
  624. package/scripts/security/secretlint-scan.ts +42 -9
  625. package/scripts/security/security-audit-provision-main.ts +9 -1
  626. package/scripts/security/security-audit-provision.ts +1 -1
  627. package/scripts/security/security-audit.ts +21 -1
  628. package/scripts/security/sonar-file.ts +10 -5
  629. package/scripts/split/split-assess.ts +12 -3
  630. package/scripts/split/split-early-notice.ts +116 -0
  631. package/scripts/split/split-measure.ts +20 -2
  632. package/scripts/split/split-resolution.ts +107 -0
  633. package/scripts/test/direct-importers.ts +185 -0
  634. package/scripts/test/pure-files-entries.ts +83 -0
  635. package/scripts/test/related-heavy-tests.ts +81 -0
  636. package/scripts/test/test-no-base.ts +88 -0
  637. package/scripts/test/test-pinned-cache-logic.ts +363 -0
  638. package/scripts/test/test-pinned-cache.ts +337 -0
  639. package/scripts/test/test-pinned-emit.ts +49 -0
  640. package/scripts/test/test-pinned-functions.ts +116 -0
  641. package/scripts/test/test-pinned-ledger.ts +80 -0
  642. package/scripts/test/test-pinned-logic.ts +208 -20
  643. package/scripts/test/test-pinned-main.ts +84 -18
  644. package/scripts/test/test-pinned-pool.ts +73 -0
  645. package/scripts/test/test-pinned-probe.ts +254 -0
  646. package/scripts/test/test-pinned-prune-logic.ts +118 -0
  647. package/scripts/test/test-pinned-reach.ts +194 -0
  648. package/scripts/test/test-pinned-record.ts +196 -0
  649. package/scripts/test/test-pinned-rewire.ts +148 -0
  650. package/scripts/test/test-pinned-spawn.ts +81 -0
  651. package/scripts/test/test-pinned-stage.ts +70 -0
  652. package/scripts/test/test-pinned.ts +258 -47
  653. package/scripts/test/test-reach-cli.ts +61 -0
  654. package/scripts/test/test-read-map.ts +35 -11
  655. package/scripts/test/test-read-recording.ts +17 -5
  656. package/scripts/test/test-read-trace.ts +26 -8
  657. package/scripts/test/test-red-logic.ts +46 -7
  658. package/scripts/test/test-red.ts +34 -14
  659. package/scripts/test/test-related-claim.ts +37 -0
  660. package/scripts/test/test-related-main.ts +4 -1
  661. package/scripts/test/test-related-moved.ts +91 -0
  662. package/scripts/test/test-related-record.ts +43 -23
  663. package/scripts/test/test-related-scope.ts +99 -34
  664. package/scripts/test/test-related-synced.ts +135 -0
  665. package/scripts/test/test-related-touched.ts +84 -0
  666. package/scripts/test/test-related.ts +167 -83
  667. package/scripts/test/test-unit-guard.ts +28 -5
  668. package/scripts/test/unit-durations.ts +41 -0
  669. package/scripts/test/unit-worker-share.ts +5 -4
  670. package/scripts/test/vitest-cache-directory.ts +19 -0
  671. package/scripts/test/vitest-cli-excludes.ts +14 -0
  672. package/scripts/test/vitest-failure-summary.ts +111 -0
  673. package/scripts/test/whole-suite-files.ts +12 -1
  674. package/scripts/time-runtime/time-batch-guard.ts +42 -4
  675. package/scripts/time-runtime/time-bundle-call.ts +1 -1
  676. package/scripts/time-runtime/time-bundles.ts +36 -5
  677. package/scripts/time-runtime/time-markers.ts +2 -2
  678. package/scripts/time-runtime/time-search-call.ts +38 -0
  679. package/scripts/time-runtime/time-shell.ts +1 -1
  680. package/scripts/time-runtime/time-transcript-line.ts +17 -5
  681. package/scripts/version/development-engines-verdict.ts +124 -0
  682. package/scripts/version/latest-corepack.ts +2 -0
  683. package/templates/gitignore +2 -0
  684. package/templates/workflows/ci.yml +41 -0
  685. package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
  686. package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
  687. package/dist/hooks/chunk-TUIFS5E7.js +0 -72
  688. package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
  689. package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
  690. package/scripts/rules/filing-cap.ts +0 -139
@@ -1,21 +1,27 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { lane_child_marker } from '#scripts/lane/lane-child-marker'
4
- import { lane_ledger } from '#scripts/lane/lane-ledger'
4
+ import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
5
5
  import { review_stop_log } from '#scripts/review/review-stop-log'
6
6
  import { run_event_stream } from '#scripts/run/event/run-event-stream'
7
7
  import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
8
8
  import { run_ship, type ShipSection } from './run-ship'
9
9
  import { run_ship_args } from './run-ship-args'
10
10
  import { run_ship_body_draft } from './run-ship-body-draft'
11
+ import { run_ship_conflict_probe } from './run-ship-conflict-probe'
11
12
  import { run_ship_detach } from './run-ship-detach'
13
+ import { run_ship_handover, type CodeCheck } from './run-ship-handover'
12
14
  import { run_ship_infra_retry } from './run-ship-infra-retry'
15
+ import { run_ship_lap } from './run-ship-lap'
16
+ import type { PreDetachResult } from './run-ship-pre-detach'
17
+ import { run_ship_pre_detach_pinned } from './run-ship-pre-detach-pinned'
13
18
  import { run_ship_pre_detach_timed } from './run-ship-pre-detach-timed'
14
19
  import { run_ship_probe } from './run-ship-probe'
15
20
  import { run_ship_return } from './run-ship-return'
16
21
  import { run_ship_stage, type Phase, type ShipState, type Stage } from './run-ship-stage'
17
22
  import { run_ship_steps, type ShipArguments, type Step } from './run-ship-steps'
18
23
  import { run_ship_stop_cause } from './run-ship-stop-cause'
24
+ import type { StepResult } from './run-ship-sync'
19
25
 
20
26
  // `josh ship "<title> #<N>"` — one call for the fixed commit-to-report region a run ships a change on.
21
27
  // It runs `gate`, `git -y`, `followup` and `run:tail` internally, so no round trip re-bills a lane's
@@ -47,7 +53,7 @@ const ARGV_OFFSET = 2
47
53
  const SUCCESS_EXIT_CODE = 0
48
54
  const FAILURE_EXIT_CODE = 1
49
55
  const USAGE =
50
- 'Usage: josh ship "<title> #<N>" [<follow-up-N> ...] [--cite <N> ...] [--review] [--detach] [--body-file <path>] [--notify-message <text> | --notify-message-file <path>] | josh ship --log <N>'
56
+ 'Usage: josh ship "<title> #<N>" [<follow-up-N> ...] [--cite <N> ...] [--review [--unpinned-ok "<reason>"]] [--detach] [--body-file <path>] [--notify-message <text> | --notify-message-file <path>] | josh ship --log <N>'
51
57
  const NO_LOG_NOTE = 'no detached ship supervisor log for this issue'
52
58
  const LANE_CHILD_DETACH_NOTE =
53
59
  'lane child: shipping through the detached supervisor (--detach implied), so the end of this turn cannot kill it'
@@ -61,9 +67,17 @@ interface ShipContext {
61
67
  done: ReadonlySet<string>
62
68
  state: ShipState
63
69
  started: Array<string>
70
+ // Whether the code this process loaded has changed since it started (`run-ship-handover.ts`).
71
+ is_stale: CodeCheck
72
+ }
73
+
74
+ interface ShipOutcome {
75
+ sections: ReadonlyArray<ShipSection>
76
+ is_handed_over: boolean
64
77
  }
65
78
 
66
79
  const { PHASE, STAGE } = run_ship_stage
80
+ const REREAD_AFTER: ReadonlySet<Stage> = new Set([STAGE.SYNC, STAGE.REVIEW])
67
81
 
68
82
  // Every stage that ran is timed into the lane ledger, failed ones included — a stage that stopped the
69
83
  // ship cost the lane its time as well. The append is local and best-effort, so it adds no wait.
@@ -72,12 +86,16 @@ const { PHASE, STAGE } = run_ship_stage
72
86
  // recorded, or a gate that reused its tree's green record would otherwise read as a run that took a
73
87
  // second and pull the stage's median to zero.
74
88
  async function record_timed(step: Step, issue: string, elapsed_ms: number): Promise<void> {
75
- await lane_ledger.record_stage({ stage: step.stage, elapsed_ms, issue: Number(issue) })
89
+ await lane_ledger_write.record_stage({ stage: step.stage, elapsed_ms, issue: Number(issue) })
76
90
  }
77
91
 
78
92
  async function run_once(step: Step, args: ShipArguments, state: ShipState): Promise<ShipSection> {
79
93
  const started_ms = performance.now()
80
- const result = await step.run(args, state)
94
+ const result = await run_ship_lap.within(
95
+ args.number,
96
+ step.stage,
97
+ async () => await step.run(args, state),
98
+ )
81
99
  const elapsed_ms = performance.now() - started_ms
82
100
 
83
101
  if (result.is_skipped !== true) await record_timed(step, args.number, elapsed_ms)
@@ -97,15 +115,31 @@ async function run_step(step: Step, args: ShipArguments, state: ShipState): Prom
97
115
  })
98
116
  }
99
117
 
118
+ // One stage's line on the event stream, with why it ran or was skipped where the stage said.
119
+ interface StageMark {
120
+ stage: Stage
121
+ phase: Phase
122
+ reason?: string | undefined
123
+ }
124
+
100
125
  async function emit_phase(
101
126
  args: ShipArguments,
102
127
  context: ShipContext,
103
- stage: Stage,
104
- phase: Phase,
128
+ mark: StageMark,
105
129
  ): Promise<void> {
106
- const text = run_ship_stage.event_text(args.number, stage, phase, context.started)
130
+ const text = run_ship_stage.event_text(args.number, mark.stage, mark.phase, context.started)
107
131
 
108
- await run_event_stream_emit.emit(run_event_stream.EVENT_KIND.SHIP_STAGE, text)
132
+ await run_event_stream_emit.emit(
133
+ run_event_stream.EVENT_KIND.SHIP_STAGE,
134
+ run_ship_stage.with_reason(text, mark.reason),
135
+ )
136
+ }
137
+
138
+ // A green stage that found nothing to do ends `skipped` rather than `done` (joshuafolkken/kit#5296).
139
+ function settled_phase(section: ShipSection): Phase {
140
+ if (section.code !== SUCCESS_EXIT_CODE) return PHASE.FAILED
141
+
142
+ return section.is_skipped === true ? PHASE.SKIPPED : PHASE.DONE
109
143
  }
110
144
 
111
145
  // The record is written best-effort, as the event stream is: a record that could not be written costs
@@ -124,13 +158,13 @@ async function settle(
124
158
  step: Step,
125
159
  args: ShipArguments,
126
160
  context: ShipContext,
127
- code: number,
161
+ section: ShipSection,
128
162
  ): Promise<void> {
129
- const is_green = code === SUCCESS_EXIT_CODE
163
+ const phase = settled_phase(section)
130
164
 
131
- await emit_phase(args, context, step.stage, is_green ? PHASE.DONE : PHASE.FAILED)
165
+ await emit_phase(args, context, { stage: step.stage, phase, reason: section.reason })
132
166
 
133
- if (!is_green) return
167
+ if (phase === PHASE.FAILED) return
134
168
 
135
169
  record(context.target, (path) => {
136
170
  run_ship_stage.mark_done(path, step.stage)
@@ -150,7 +184,7 @@ async function run_stage(
150
184
  context: ShipContext,
151
185
  ): Promise<ShipSection> {
152
186
  if (run_ship_stage.is_done(step.stage, context.done, context.state, args.body.length > 0)) {
153
- await emit_phase(args, context, step.stage, PHASE.SKIPPED)
187
+ await emit_phase(args, context, { stage: step.stage, phase: PHASE.SKIPPED })
154
188
 
155
189
  return { header: step.header, body: run_ship.SKIPPED_BODY, code: SUCCESS_EXIT_CODE }
156
190
  }
@@ -159,20 +193,22 @@ async function run_stage(
159
193
  record(context.target, (path) => {
160
194
  run_ship_stage.mark_started(path, step.stage)
161
195
  })
162
- await emit_phase(args, context, step.stage, PHASE.START)
196
+ await emit_phase(args, context, { stage: step.stage, phase: PHASE.START })
163
197
  const section = await run_step(step, args, context.state)
164
198
 
165
- await settle(step, args, context, section.code)
166
- // A review may fix the tree and refreshes the brief, so the stages after it read the state anew.
167
- if (step.stage === STAGE.REVIEW) set_state(context, await run_ship_probe.read_state())
199
+ await settle(step, args, context, section)
200
+ // The sync commits locally and a review may fix the tree, so the stages after either read the state
201
+ // anew — the commit stage then pushes the sync's commit rather than making it again.
202
+ if (REREAD_AFTER.has(step.stage)) set_state(context, await run_ship_probe.read_state())
168
203
 
169
204
  return section
170
205
  }
171
206
 
172
207
  async function open_context(args: ShipArguments): Promise<ShipContext> {
173
- const [target, state] = await Promise.all([
208
+ const [target, state, is_stale] = await Promise.all([
174
209
  run_ship_probe.record_target(args.number),
175
210
  run_ship_probe.read_state(),
211
+ run_ship_handover.watch(),
176
212
  ])
177
213
 
178
214
  const done = target === undefined ? new Set<string>() : run_ship_stage.read_done(target)
@@ -183,7 +219,7 @@ async function open_context(args: ShipArguments): Promise<ShipContext> {
183
219
  run_ship_stage.clear_waits(path)
184
220
  })
185
221
 
186
- return { target, done, state, started }
222
+ return { target, done, state, started, is_stale }
187
223
  }
188
224
 
189
225
  // Every stop goes to the lane ledger — supervised, in an agent's own turn, or a lane child's preflight
@@ -192,7 +228,7 @@ async function open_context(args: ShipArguments): Promise<ShipContext> {
192
228
  async function record_stop(args: ShipArguments, stage: Stage, failed?: ShipSection): Promise<void> {
193
229
  const issue = Number(args.number)
194
230
 
195
- await lane_ledger.record_stop(run_ship_stop_cause.record_of(issue, stage, failed))
231
+ await lane_ledger_write.record_stop(run_ship_stop_cause.record_of(issue, stage, failed))
196
232
  review_stop_log.record({ issue, stage, findings: run_ship_stop_cause.details_of(stage, failed) })
197
233
  }
198
234
 
@@ -217,28 +253,50 @@ async function stopped(
217
253
  return sections
218
254
  }
219
255
 
256
+ // One stage, and the outcome where the ship ends at it: a failure, or a sync after which a successor on
257
+ // the code it merged took the rest over (`run-ship-handover.ts`).
258
+ async function ship_stage(
259
+ step: Step,
260
+ args: ShipArguments,
261
+ context: ShipContext,
262
+ sections: Array<ShipSection>,
263
+ ): Promise<ShipOutcome | undefined> {
264
+ const section = await run_stage(step, args, context)
265
+
266
+ sections.push(section)
267
+
268
+ if (section.code !== SUCCESS_EXIT_CODE) {
269
+ return { sections: await stopped(sections, args, step, context), is_handed_over: false }
270
+ }
271
+
272
+ if (step.stage !== STAGE.SYNC) return undefined
273
+ const handover = await run_ship_handover.hand_over(args, context.is_stale)
274
+
275
+ return handover === undefined
276
+ ? undefined
277
+ : { sections: [...sections, handover], is_handed_over: true }
278
+ }
279
+
220
280
  // Run the stages in order, stopping at the first that failed: a red gate never reaches the commit, so
221
281
  // the returned sections end at the failure the report names. A ship that reached the end clears its
222
- // record, so the next ship of the same issue starts from the preflight.
223
- async function ship(args: ShipArguments): Promise<ReadonlyArray<ShipSection>> {
282
+ // record, so the next ship of the same issue starts from the preflight; one handed over leaves it to
283
+ // the successor, which passes over what this one finished.
284
+ async function ship(args: ShipArguments): Promise<ShipOutcome> {
224
285
  const context = await open_context(args)
225
286
  const sections: Array<ShipSection> = []
226
287
 
227
288
  for (const step of run_ship_steps.steps(args)) {
228
289
  // eslint-disable-next-line no-await-in-loop -- stages run in order and the first failure stops the ship
229
- const section = await run_stage(step, args, context)
230
-
231
- sections.push(section)
290
+ const outcome = await ship_stage(step, args, context, sections)
232
291
 
233
- // eslint-disable-next-line no-await-in-loop -- stages run in order and the first failure stops the ship
234
- if (section.code !== SUCCESS_EXIT_CODE) return await stopped(sections, args, step, context)
292
+ if (outcome !== undefined) return outcome
235
293
  }
236
294
 
237
295
  record(context.target, (path) => {
238
296
  run_ship_stage.clear(path)
239
297
  })
240
298
 
241
- return sections
299
+ return { sections, is_handed_over: false }
242
300
  }
243
301
 
244
302
  async function print_log(number: string): Promise<number> {
@@ -283,11 +341,29 @@ function should_detach(args: ShipArguments): boolean {
283
341
  return !run_ship_detach.is_supervised() && lane_child_marker.is_child_of(process.cwd())
284
342
  }
285
343
 
286
- // The preflight stop's unmet kinds, with `pre-detach` added when those checks went red beside it.
287
- function with_checks(stop: ShipSection, checks: ShipSection): ShipSection {
344
+ // The preflight stop's unmet kinds, with the red pre-detach checks' own kinds added beside them
345
+ // (`run-ship-pre-detach.ts`).
346
+ function with_checks(stop: ShipSection, checks: PreDetachResult): ShipSection {
288
347
  if (checks.code === SUCCESS_EXIT_CODE) return stop
289
348
 
290
- return { ...stop, preconditions: [...(stop.preconditions ?? []), lane_ledger.PRE_DETACH_STAGE] }
349
+ return {
350
+ ...stop,
351
+ preconditions: [...(stop.preconditions ?? []), ...(checks.preconditions ?? [])],
352
+ }
353
+ }
354
+
355
+ // The stop is the sync stage's, so it is recorded and resumed as that stage's `conflict` is; the
356
+ // preflight's refusal, if any, still prints last.
357
+ async function conflict_sections(
358
+ args: ShipArguments,
359
+ conflict: StepResult,
360
+ preflight: ShipSection,
361
+ ): Promise<ReadonlyArray<ShipSection>> {
362
+ const section = run_ship.section_of(run_ship.SYNC_HEADER, conflict)
363
+
364
+ await record_stop(args, STAGE.SYNC, section)
365
+
366
+ return [section, preflight]
291
367
  }
292
368
 
293
369
  // The preflight is deterministic and fast, so it runs here, in the agent's own turn, before the
@@ -296,19 +372,32 @@ function with_checks(stop: ShipSection, checks: ShipSection): ShipSection {
296
372
  // checks (`run-ship-pre-detach.ts`) follow it here for the same reason; they are not a stage, so the
297
373
  // supervisor's gate runs them again rather than passing over them. They run even when the preflight
298
374
  // refused, so one ship reports every failure there is to fix (joshuafolkken/kit#4305); the preflight's
299
- // refusal still comes before its own scoped pair, the one heavy check here.
375
+ // refusal still comes before its own scoped pair, the one heavy check here beside a `--review` ship's
376
+ // `test:pinned`.
300
377
  //
301
378
  // **The refusal is printed last**: a lane child reading the report through `tail` saw only the red
302
379
  // metrics or type check after it, fixed those, and shipped into the same refusal again — every repeated
303
380
  // preflight stop of joshuafolkken/kit#4478. The stop records the pre-detach checks' red beside the
304
- // unmet kinds, so the ledger shows which refusals came with one.
381
+ // unmet kinds, so the ledger shows which refusals came with one. **Red checks behind a passed preflight
382
+ // are a stop too** (joshuafolkken/kit#5228): recorded under the preflight with their kinds, an
383
+ // `unpinned` place among them.
384
+ //
385
+ // **A conflict with origin/main stops before the checks** (joshuafolkken/kit#5018): the supervisor's
386
+ // sync would stop on it after them, so they would only be run again once it is resolved.
305
387
  async function pre_detach_sections(args: ShipArguments): Promise<ReadonlyArray<ShipSection>> {
306
388
  const preflight = await run_stage(run_ship_steps.PREFLIGHT_STEP, args, await open_context(args))
307
- const result = await run_ship_pre_detach_timed.checks(args.number)
389
+ const conflict = await run_ship_conflict_probe.probe()
390
+
391
+ if (conflict !== undefined) return await conflict_sections(args, conflict, preflight)
392
+
393
+ const result = await run_ship_pre_detach_timed.checks(
394
+ args.number,
395
+ run_ship_pre_detach_pinned.checks_for(args),
396
+ )
308
397
  const checks = { header: run_ship.PRE_DETACH_HEADER, body: result.out, code: result.code }
309
398
 
310
- if (preflight.code !== SUCCESS_EXIT_CODE) {
311
- await record_stop(args, run_ship_steps.PREFLIGHT_STEP.stage, with_checks(preflight, checks))
399
+ if (run_ship.exit_code([checks, preflight]) !== SUCCESS_EXIT_CODE) {
400
+ await record_stop(args, run_ship_steps.PREFLIGHT_STEP.stage, with_checks(preflight, result))
312
401
  }
313
402
 
314
403
  return [checks, preflight]
@@ -343,7 +432,9 @@ async function record_supervised_result(
343
432
  await run_ship_detach.mark_result(repository, args.number, run_ship.exit_code(sections))
344
433
  }
345
434
 
346
- async function run_ship_command(args: ShipArguments): Promise<number> {
435
+ async function run_ship_command(given: ShipArguments): Promise<number> {
436
+ const args = await run_ship_pre_detach_pinned.with_recorded_reason(given)
437
+
347
438
  if (should_detach(args)) {
348
439
  if (!args.is_detach) console.error(LANE_CHILD_DETACH_NOTE)
349
440
 
@@ -351,9 +442,10 @@ async function run_ship_command(args: ShipArguments): Promise<number> {
351
442
  }
352
443
 
353
444
  await claim_supervised_identity(args)
354
- const sections = await ship(args)
445
+ const { sections, is_handed_over } = await ship(args)
355
446
 
356
- await record_supervised_result(args, sections)
447
+ // The successor carries the same launch record, so its result is the one written there.
448
+ if (!is_handed_over) await record_supervised_result(args, sections)
357
449
  console.info(run_ship.format_report(sections))
358
450
 
359
451
  return run_ship.exit_code(sections)
@@ -0,0 +1,59 @@
1
+ import { changed_paths } from '#scripts/git/changed-paths'
2
+ import { git_command } from '#scripts/git/git-command'
3
+ import { git_merge_conflicts } from '#scripts/git/git-merge-conflicts'
4
+ import { main_merge_guard } from '#scripts/git/main-merge-guard'
5
+ import { main_merge_regenerate } from '#scripts/git/main-merge-regenerate'
6
+ import type { StepResult } from './run-ship-sync'
7
+
8
+ // Before `josh ship --detach` runs its pre-detach checks, whether merging origin/main would stop on a
9
+ // conflict (joshuafolkken/kit#5018). The supervisor's sync stage is the first to merge, so a branch
10
+ // main had moved under ran the whole pre-detach first and then stopped there, its checks thrown away.
11
+ // The reading touches neither the working tree nor the index (`git-merge-conflicts.ts`); only the
12
+ // remote-tracking ref of the default branch moves with the fetch. A probe that cannot answer — a fetch
13
+ // that failed, a merge base it cannot read — leaves the question to the sync stage, as the sync itself
14
+ // leaves a failed fetch to the followup; the ship then runs exactly as it did before the probe.
15
+
16
+ const FAILURE_EXIT_CODE = 1
17
+ const CONFLICT_NOTE = 'merging origin/main would stop on a conflict in:'
18
+
19
+ // The sync's own order: `main:merge` refuses uncommitted work origin/main also changed, so the commit
20
+ // comes first (`main-merge-guard.ts`).
21
+ function fix_note(): string {
22
+ return `The pre-detach checks did not run. Commit the work with ${main_merge_guard.commit_command()}, run \`pnpm josh main:merge\`, resolve the conflict in those files, then run the ship again.`
23
+ }
24
+
25
+ async function conflicting_paths(): Promise<ReadonlyArray<string>> {
26
+ const default_branch = await git_command.get_default_branch()
27
+
28
+ await git_command.fetch_branch(default_branch)
29
+
30
+ const paths = await changed_paths.read_changed_paths(false)
31
+ const root = await git_command.repository_root()
32
+
33
+ return git_merge_conflicts.working_tree_conflicts(`origin/${default_branch}`, paths, root) ?? []
34
+ }
35
+
36
+ async function answered_paths(): Promise<ReadonlyArray<string>> {
37
+ try {
38
+ return await conflicting_paths()
39
+ } catch {
40
+ return []
41
+ }
42
+ }
43
+
44
+ // The stop, carrying the paths as the sync stage's conflict does, so the stop is recorded as
45
+ // `conflict`; `undefined` when the merge would go through, the sync's merge would resolve every
46
+ // conflict itself (`main-merge-regenerate.ts`), or the probe could not tell.
47
+ async function probe(): Promise<StepResult | undefined> {
48
+ const conflicts = await answered_paths()
49
+
50
+ if (conflicts.length === 0 || main_merge_regenerate.is_resolvable(conflicts)) return undefined
51
+
52
+ const out = [CONFLICT_NOTE, ...conflicts, fix_note()].join('\n')
53
+
54
+ return { code: FAILURE_EXIT_CODE, out, conflicts }
55
+ }
56
+
57
+ const run_ship_conflict_probe = { CONFLICT_NOTE, probe }
58
+
59
+ export { run_ship_conflict_probe }
@@ -3,6 +3,7 @@ import { setTimeout as sleep } from 'node:timers/promises'
3
3
  import { agent_role_profile } from '#scripts/agent/agent-role-profile'
4
4
  import { issue_cite } from '#scripts/issue/issue-cite'
5
5
  import { session_cite } from '#scripts/issue/session-cite'
6
+ import { durable_state } from '#scripts/josh/durable-state'
6
7
  import { process_identity } from '#scripts/josh/process-identity'
7
8
  import { stamp_file } from '#scripts/josh/stamp-file'
8
9
  import { json_value } from '#scripts/lib/json-value'
@@ -33,7 +34,7 @@ import { z } from 'zod'
33
34
  // probe cannot momentarily read is the opposite case: the pid is alive and was verified once, so it is
34
35
  // kept running rather than read as a crash that would license a second supervisor.
35
36
 
36
- const LOG_PREFIX = 'josh-ship-log-'
37
+ const LOG_PREFIX = durable_state.PREFIX.SHIP_LOG
37
38
  const PID_PREFIX = 'josh-ship-pid-'
38
39
  const BODY_PREFIX = 'josh-ship-notify-'
39
40
  const KEY_SEPARATOR = '-'
@@ -51,7 +52,7 @@ const SUPERVISED_VALUE = '1'
51
52
  const LAUNCHED = 'launched'
52
53
  const BUSY = 'busy'
53
54
  const FAILED = 'failed'
54
- const RESULT_PREFIX = 'josh-ship-result-'
55
+ const RESULT_PREFIX = durable_state.PREFIX.SHIP_RESULT
55
56
  const RECORD_POLL_MS = 25
56
57
  const RECORD_WAIT_ATTEMPTS = 200
57
58
  // How long a bare launched pid is believed before the supervisor must have claimed the record: far above
@@ -92,6 +93,7 @@ interface DetachRequest {
92
93
  body: ReadonlyArray<string>
93
94
  cites: ReadonlyArray<string>
94
95
  is_review: boolean
96
+ unpinned_ok: ReadonlyArray<string>
95
97
  repository: string
96
98
  cwd: string
97
99
  }
@@ -106,11 +108,11 @@ function key(prefix: string, request: Pick<DetachRequest, 'number' | 'repository
106
108
  }
107
109
 
108
110
  function log_path(repository: string, number: string): string {
109
- return stamp_file.stamp_path(`${LOG_PREFIX}${number}${KEY_SEPARATOR}`, repository, LOG_SUFFIX)
111
+ return durable_state.path(`${LOG_PREFIX}${number}${KEY_SEPARATOR}`, repository, LOG_SUFFIX)
110
112
  }
111
113
 
112
114
  function result_path(repository: string, number: string): string {
113
- return stamp_file.stamp_path(`${RESULT_PREFIX}${number}${KEY_SEPARATOR}`, repository)
115
+ return durable_state.path(`${RESULT_PREFIX}${number}${KEY_SEPARATOR}`, repository)
114
116
  }
115
117
 
116
118
  function read_record(repository: string, number: string): ShipRecord | undefined {
@@ -277,6 +279,7 @@ function supervisor_argv(request: DetachRequest): LaunchArgv {
277
279
  'josh',
278
280
  'ship',
279
281
  ...review,
282
+ ...request.unpinned_ok,
280
283
  ...notify_arguments(request),
281
284
  ...request.body,
282
285
  ...cites,
@@ -381,8 +384,11 @@ const run_ship_detach = {
381
384
  last_launch,
382
385
  log_path,
383
386
  read_log,
387
+ read_record,
388
+ result_path,
384
389
  running_since,
385
390
  supervisor_argv,
391
+ supervisor_environment,
386
392
  }
387
393
 
388
394
  export type { DetachRequest, DetachResult }
@@ -0,0 +1,106 @@
1
+ import { fileURLToPath } from 'node:url'
2
+ import { issue_cite } from '#scripts/issue/issue-cite'
3
+ import { process_identity } from '#scripts/josh/process-identity'
4
+ import { run_event_stream } from '#scripts/run/event/run-event-stream'
5
+ import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
6
+ import { run_wake_code } from '#scripts/run/wake/run-wake-code'
7
+ import { run_wake_stop } from '#scripts/run/wake/run-wake-stop'
8
+ import { run_wake_takeover, type TakeoverPorts } from '#scripts/run/wake/run-wake-takeover'
9
+ import type { ShipSection } from './run-ship'
10
+ import { run_ship_detach } from './run-ship-detach'
11
+ import { run_ship_probe } from './run-ship-probe'
12
+ import { run_ship_relaunch } from './run-ship-relaunch'
13
+ import type { ShipArguments } from './run-ship-steps'
14
+
15
+ // A supervisor whose sync merged a change to its own code hands the ship to a successor on that code
16
+ // (joshuafolkken/kit#4849). The stages run in one process, so a review stage after a sync that pulled
17
+ // a fix to `scripts/review/` still judged on the code the supervisor started with.
18
+ //
19
+ // **The wake supervisor's mechanism, shared**: the loaded code is the import closure of the ship entry
20
+ // (`run-wake-code.ts`), and the successor's own claim on the ship record is the take-over's proof
21
+ // (`run-wake-takeover.ts`). A successor that dies or never claims leaves the record naming this
22
+ // supervisor, which runs the rest on the code it started with.
23
+
24
+ const ENTRY = fileURLToPath(new URL('run-ship-cli.ts', import.meta.url))
25
+ const ROOT = fileURLToPath(new URL('../../..', import.meta.url))
26
+ const HANDOVER_HEADER = '=== handover ==='
27
+ const SUCCESS_EXIT_CODE = 0
28
+
29
+ type CodeCheck = () => Promise<boolean>
30
+
31
+ async function never_stale(): Promise<boolean> {
32
+ return false
33
+ }
34
+
35
+ // Only a supervisor hands over: a ship in an agent's own turn ends with that turn, which reads the
36
+ // next command on the merged code anyway.
37
+ async function watch(): Promise<CodeCheck> {
38
+ if (!run_ship_detach.is_supervised()) return never_stale
39
+
40
+ return await run_wake_code.watch(run_wake_code.source_of(ROOT, ENTRY))
41
+ }
42
+
43
+ function ports_of(repository: string, number: string): TakeoverPorts {
44
+ return {
45
+ holder_pid: () => run_ship_relaunch.holder_pid(repository, number),
46
+ is_alive: process_identity.is_live_pid,
47
+ stop: run_wake_stop.signal,
48
+ }
49
+ }
50
+
51
+ // Several lanes' supervisors hand over at once, so each note leads with its issue for `run:board`.
52
+ async function note(number: string, text: string): Promise<void> {
53
+ await run_event_stream_emit.emit(
54
+ run_event_stream.EVENT_KIND.NOTE,
55
+ `${issue_cite.plain(number)} ${text}`,
56
+ )
57
+ }
58
+
59
+ async function take_over(args: ShipArguments, repository: string): Promise<number | undefined> {
60
+ const pid = run_ship_relaunch.relaunch({ repository, number: args.number, cwd: process.cwd() })
61
+ if (pid === undefined) return undefined
62
+ const result = await run_wake_takeover.await_takeover(pid, ports_of(repository, args.number))
63
+
64
+ return result === 'taken' ? pid : undefined
65
+ }
66
+
67
+ function handed_over(pid: number): ShipSection {
68
+ return {
69
+ header: HANDOVER_HEADER,
70
+ body: `code changed on main — process ${String(pid)} ships the rest on the new code`,
71
+ code: SUCCESS_EXIT_CODE,
72
+ }
73
+ }
74
+
75
+ // The handover section where a successor took the ship over; `undefined` where the code is unchanged
76
+ // or no successor took it, and this supervisor runs the rest of the stages itself.
77
+ async function hand_over(
78
+ args: ShipArguments,
79
+ is_stale: CodeCheck,
80
+ ): Promise<ShipSection | undefined> {
81
+ if (!(await is_stale())) return undefined
82
+ const repository = await run_ship_probe.repository_directory()
83
+ if (repository === undefined) return undefined
84
+ const pid = await take_over(args, repository)
85
+
86
+ if (pid === undefined) {
87
+ await note(
88
+ args.number,
89
+ `The ship supervisor's code changed on main, but no successor took the ship over, so process ${String(process.pid)} runs the rest on the code it started with.`,
90
+ )
91
+
92
+ return undefined
93
+ }
94
+
95
+ await note(
96
+ args.number,
97
+ `The ship supervisor's code changed on main, so process ${String(process.pid)} handed the ship to process ${String(pid)} on the new code.`,
98
+ )
99
+
100
+ return handed_over(pid)
101
+ }
102
+
103
+ const run_ship_handover = { hand_over, watch }
104
+
105
+ export type { CodeCheck }
106
+ export { run_ship_handover }
@@ -1,4 +1,5 @@
1
1
  import { setTimeout as delay } from 'node:timers/promises'
2
+ import { RETRY_PENDING_KEY, RETRY_PENDING_VALUE } from '#scripts/gh/git-gh-comments-unreadable'
2
3
  import type { ShipSection } from './run-ship'
3
4
  import type { Stage } from './run-ship-stage'
4
5
  import { run_ship_stop_cause } from './run-ship-stop-cause'
@@ -35,13 +36,27 @@ function is_retryable(stage: Stage, section: ShipSection): boolean {
35
36
  return run_ship_stop_cause.cause_of(stage, section) === run_ship_stop_cause.CAUSE.INFRA
36
37
  }
37
38
 
39
+ // An attempt with a retry still left runs with `RETRY_PENDING_KEY` set, so its stop sends no
40
+ // confirmation; the final attempt runs without it and sends the one confirmation.
41
+ async function run_attempt(attempt: Attempt, retries_left: number): Promise<ShipSection> {
42
+ if (retries_left === 0) return await attempt()
43
+
44
+ process.env[RETRY_PENDING_KEY] = RETRY_PENDING_VALUE
45
+
46
+ try {
47
+ return await attempt()
48
+ } finally {
49
+ Reflect.deleteProperty(process.env, RETRY_PENDING_KEY)
50
+ }
51
+ }
52
+
38
53
  async function retried(
39
54
  stage: Stage,
40
55
  attempt: Attempt,
41
56
  hooks: RetryHooks = DEFAULT_HOOKS,
42
57
  retries_left = MAX_INFRA_RETRIES,
43
58
  ): Promise<ShipSection> {
44
- const section = await attempt()
59
+ const section = await run_attempt(attempt, retries_left)
45
60
 
46
61
  if (retries_left === 0 || !is_retryable(stage, section)) return section
47
62
 
@@ -0,0 +1,63 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+ import type { ShipLapRecord } from '#scripts/lane/lane-ledger-schema'
3
+ import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
4
+
5
+ // The inner steps of a `josh ship` stage, each timed into the lane ledger as a lap, so `lane:breakdown`
6
+ // names what a stage spent its minutes on (joshuafolkken/kit#5294). The stage and the issue are held
7
+ // for the stage's whole run (`within`), so a step deep inside it — the scoped pair the preflight, the
8
+ // review rounds and the gate share — is timed without either being passed down to it. A step run
9
+ // outside a ship stage (the followup's own gate) has no stage to belong to and is not timed.
10
+
11
+ interface LapScope {
12
+ issue: number
13
+ stage: string
14
+ }
15
+
16
+ type LapSink = (lap: ShipLapRecord) => Promise<void>
17
+
18
+ // The names a lap is written under, read back by `lane:breakdown` and `lane:stats` as they are.
19
+ const STEP = {
20
+ PRECONDITIONS: 'preconditions',
21
+ SCOPED_PAIR: 'scoped pair',
22
+ GATE: 'josh gate',
23
+ OVERLAP_WAIT: 'overlap wait',
24
+ LOCAL_COMMIT: 'local commit',
25
+ MERGE: 'merge',
26
+ PUSH: 'push',
27
+ COMMIT: 'josh git',
28
+ } as const
29
+
30
+ const scope = new AsyncLocalStorage<LapScope>()
31
+
32
+ async function to_ledger(lap: ShipLapRecord): Promise<void> {
33
+ await lane_ledger_write.record_ship_lap(lap)
34
+ }
35
+
36
+ async function within<T>(issue: string, stage: string, run: () => Promise<T>): Promise<T> {
37
+ return await scope.run({ issue: Number(issue), stage }, run)
38
+ }
39
+
40
+ // A step that threw still cost the stage its time, so it is timed either way; the append is
41
+ // best-effort (`lane-ledger-write.ts`), so it never replaces the step's own error.
42
+ async function timed<T>(
43
+ step: string,
44
+ run: () => Promise<T>,
45
+ sink: LapSink = to_ledger,
46
+ ): Promise<T> {
47
+ const current = scope.getStore()
48
+
49
+ if (current === undefined) return await run()
50
+
51
+ const started_ms = performance.now()
52
+
53
+ try {
54
+ return await run()
55
+ } finally {
56
+ await sink({ ...current, step, elapsed_ms: performance.now() - started_ms })
57
+ }
58
+ }
59
+
60
+ const run_ship_lap = { STEP, timed, within }
61
+
62
+ export type { LapSink }
63
+ export { run_ship_lap }